Linux日志
PHP记事本
JS记事本
数据库
团队资讯

微信公众号自定义分享内容接口的实现及踩坑记录(laravel开发实战)

分类:PHP记事本 发布时间:2019-01-23 22:28:00 阅读:作者:郑祥景

一、env配置

WEIXIN.APPID=
WEIXIN.APPSECRET=

二、控制器(配置redis缓存,redis的trait附后)

<?php

namespace App\Http\Controllers\Home;

use App\Http\Controllers\Controller;
use App\Services\Home\ArticleService;
use App\Services\Home\CategoryService;
use App\Services\RedisService;

class ArticleController extends Controller
{
    use RedisService;

    protected $category, $article;

    public function __construct(CategoryService $category, ArticleService $article)
    {
        $this->category = $category;
        $this->article = $article;
    }

    //curl获取请求文本内容
    public function get_curl_contents($url, $method ='GET', $data = array()) {
        if ($method == 'POST') {
            //使用crul模拟
            $ch = curl_init();
            //禁用htt<a href="/fw/photo.html" target="_blank">ps</a>

            //允许请求以文件流的形式返回
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
            curl_setopt($ch, CURLOPT_DNS_CACHE_TIMEOUT, 30);
            curl_setopt($ch, CURLOPT_URL, $url);
            $result = curl_exec($ch); //执行发送
            curl_close($ch);
        }else {
            if (ini_get('allow_<a href="/tags.php/fopen/" target="_blank">fopen</a>_url') == '1') {
                $result = file_get_contents($url);
            }else {
                //使用crul模拟
                $ch = curl_init();
                //允许请求以文件流的形式返回
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
                //禁用https
                curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
                curl_setopt($ch, CURLOPT_URL, $url);
                $result = curl_exec($ch); //执行发送
                curl_close($ch);
            }
        }
        return $result;
    }

    //获取微信公从号access_token
    function wx_get_token() {

        $access_token = $this->redisSingleGet('weixin.access_token');

        if (!$access_token) {
            $AppID = env('WEIXIN.APPID');//AppID(应用ID)
            $AppSecret = env('WEIXIN.APPSECRET');//AppSecret(应用密钥)
            $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$AppID.'&secret='.$AppSecret;
            $res = $this->get_curl_contents($url);
            $res = json_decode($res, true);
            //这里应该把access_token缓存起来,至于要怎么缓存就看各位了,有效期是7200s

            $this->redisSingleAdd('weixin.access_token', $res['access_token'], 7200);

            return $res['access_token'];
        }

        return $access_token;
    }

    //获取微信公从号ticket
    public function wx_get_jsapi_ticket()
    {
        $ticket = $this->redisSingleGet('weixin.ticket');

        if (!$ticket) {
            $url = sprintf("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi", $this->wx_get_token());
            $res = $this->get_curl_contents($url);
            $res = json_decode($res, true);
            //这里应该把access_token缓存起来,至于要怎么缓存就看各位了,有效期是7200s

            $this->redisSingleAdd('weixin.ticket', $res['ticket'], 7200);

            return $res['ticket'];
        }

        return $ticket;
    }

    public function view($article_id)
    {
        //当前文章内容
        $article = $this->article->first($article_id);

        $wx['appid'] = env('WEIXIN.APPID');

        //生成签名的时间戳
        $wx['timestamp'] = time();

        //生成签名的随机串
        $wx['noncestr'] = 'Wm3WZYTPz0wzccnW';

        //jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。

        $wx['jsapi_ticket'] = $this->wx_get_jsapi_ticket();
        //分享的地址,注意:这里是指当前网页的URL,不包含#及其后面部分,曾经的我就在这里被坑了,所以小伙伴们要小心了

        $wx['url'] = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];

        $string = sprintf("jsapi_ticket=%s&noncestr=%s&timestamp=%s&url=%s", $wx['jsapi_ticket'], $wx['noncestr'], $wx['timestamp'], $wx['url']);

        //生成签名
        $wx['signature'] = sha1($string);

        return view('home.article.view', [
            'article' => $article,
            'pre_article' => $pre_article ?? ['id' => $article_id, 'title' => '没有了'],
            'next_article' => $next_article ?? ['id' => $article_id, 'title' => '没有了'],
            'wx' => $wx,
        ]);
    }
}

三、业务逻辑层

<?php

namespace App\Services\Home;

use App\Repositories\ArticleRepository;

class ArticleService
{
    protected $article;

    public function __construct(ArticleRepository $article)
    {
        $this->article = $article;
    }

    public function first($article_id)
    {
        return $this->service->validata($article_id);
    }
}

四、数据库操作层

<?php

namespace App\Repositories;

use App\Article;

class ArticleRepository
{
    protected $article;

    public function __construct(Article $article)
    {
        $this->article = $article;
    }

    /**
     * 获取单条记录
     *
     * @param $id
     * @return mixed
     */
    public function first($id)
    {
        return $this->article->find($id);
    }
}

五、前端显示

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>{{ $article['title'] }}</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta name="Keywords" content="{{ $article['title'] }}">
        <meta name="Description" content="{{ $article['title'] }}">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">

        <script type="text/javascript" src="{{ asset('https://res.wx.qq.com/open/js/jweixin-1.4.0.js') }}" ></script>
        <script type="text/javascript">
            wx.config({
                debug: true,
                appId: "{{ $wx['appid'] }}",
                timestamp: {{ $wx['timestamp'] }},
                nonceStr: "{{ $wx['noncestr'] }}",
                signature: "{{ $wx['signature'] }}",
                jsApiList: [
                    'onMenuShareTimeline',
                    'onMenuShareAppMessage'
                ],
            });

            wx.ready(function(){
                var
                    s_title = '{{ $article['title'] }}',  // 分享标题
                    s_link = '',  // 分享链接
                    s_desc = ' ',  //分享描述
                    s_imgUrl = '{{ asset('/style/images/white.jpg') }}'; // 分享图标

                //朋友圈
                wx.onMenuShareTimeline({
                    title: s_title, // 分享标题
                    link: s_link, // 分享链接
                    imgUrl: s_imgUrl, // 分享图标
                    success: function () {},
                    cancel: function () { }
                });

                //发送给好友
                wx.onMenuShareAppMessage({
                    title: s_title, // 分享标题
                    desc: s_desc, // 分享描述
                    link: s_link, // 分享链接
                    imgUrl: s_imgUrl, // 分享图标
                    type: '', // 分享类型,music、video或link,不填默认为link
                    dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
                    success: function () {},
                    cancel: function () {}
                });

                //QQ好友
                wx.onMenuShareQQ({
                    title: s_title, // 分享标题
                    desc: s_desc, // 分享描述
                    link: s_link, // 分享链接
                    imgUrl: s_imgUrl, // 分享图标
                    success: function () { },
                    cancel: function () { }
                });

                //腾讯微博
                wx.onMenuShareWeibo({
                    title: s_title, // 分享标题
                    desc: s_desc, // 分享描述
                    link: s_link, // 分享链接
                    imgUrl: s_imgUrl, // 分享图标
                    success: function () { },
                    cancel: function () { }
                });
            });
        </script>
    </head>

    <body>
        {!! $article['content'] !!}
    </body>
</html>

六、路由

$this->get('/article/view/{article_id}.html', 'ArticleController@view')->name('home.article_view');

七、附件:Redis Trait

<?php
namespace App\Services;

use Illuminate\Support\Facades\Redis;

trait RedisService
{
    /**
     * 批量添加.
     *
     * @param $prefix
     * @param $value
     * @param int  $expir
     * @param null $suffix
     *
     * @return bool
     */
    public function redisMultiAdd($prefix, $value, $expir = 0, $suffix = null)
    {
        if (!is_array($value) && !is_object($value)) {
            return false;
        }

        foreach ($value as $key => $item) {
            //默认keyword
            $keyword = $prefix.':'.$key;

            //如果有传入规则
            if (isset($item[$suffix])) {
                if (!is_array($item[$suffix]) && !is_object($item[$suffix]) && (!empty($item[$suffix]) || $item[$suffix] == 0)) {
                    $keyword = $prefix.':'.$item[$suffix];
                }
            }

            //调用单个插入方法
            $this->redisSingleAdd($keyword, $item, $expir);
        }

        return true;
    }

    /**
     * 单个添加.
     *
     * @param $keyword
     * @param $value
     * @param int $expir
     *
     * @return mixed
     */
    public function redisSingleAdd($keyword, $value, $expir = 0)
    {
        if (is_array($value) || is_object($value)) {
            $value = serialize($value);
        }

        if ($expir > 0) {
            return Redis::setex($keyword, $expir, $value);
        }

        return Redis::set($keyword, $value);
    }

    /**
     * 批量删除.
     *
     * @param $prefix
     *
     * @return bool
     */
    public function redisMultiDelete($prefix)
    {
        $keys = Redis::keys($prefix.'*');

        foreach ($keys as $key) {
            //删除键值
            Redis::del($key);
        }

        return true;
    }

    /**
     * 单个删除.
     *
     * @param $keyword
     *
     * @return mixed
     */
    public function redisSingleDelete($keyword)
    {
        return Redis::del($keyword);
    }

    /**
     * 获取单个.
     *
     * @param $keyword
     *
     * @return mixed
     */
    public function redisSingleGet($keyword)
    {
        return Redis::get($keyword);
    }

    /**
     * 批量获取.
     *
     * @param $prefix
     *
     * @return array
     */
    public function redisMultiGet($prefix)
    {
        $array = [];

        $keys = Redis::keys($prefix.'*');

        foreach ($keys as $key) {
            $array[] = $this->redisSingleGet($key);
        }

        return $array;
    }
}

八、踩坑记录

        按照上面代码可以正常使用,但是如果你不是按照上面代码实现,这边提供笔者踩坑记录,给各位提供拍错思路。

        在开发过程中,注意设置前端显示页面中wx.config中的“debug: true”,这样在测试时候有错误提示或者成功提示,方便排错。

        笔者在开发过程中出现“invalid signature”(签名错误)的错误提示,在使用“https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign”校验签名后都是正确的,在搜索资料后发现解决办法:缓存:access_token、缓存:jsapi_ticket;顺便提醒,timestamp是数字,在传值时请确认是否传的是数字格式,否则也会报签名错误,wx.config中大小写必须按照上面代码的格式来,否则也会出现错误。

        另外,此项功能必须认证的公众号Appid才有权限使用,或者使用测试账号的Appid,测试账号申请地址:“https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index”


  

   编辑:郑祥景

[随享社区版权所有 未经许可不得转载 ]

返回首页


推荐
Linux日志
PHP记事本
JS记事本
数据库
团队资讯
在线客服随享宝宝
有任何问题,都可以戳我反馈哦!
微信公众号 方便 快速
扫描二维码 关注公众号

版权所有:天妖云/随享社区V5.2 beta Copyright 2015-2019 TIAYO.COM Inc. All rights reserved.