利用web-view组件将已有h5网站移植到微信小程序,且可以用微信身份直接登录

目标

解决三件事。

  1. 小程序嵌入h5.
  2. 获取用户信息。
  3. 将用户信息以及一个唯一ID传入web-view环境里。

一、小程序里嵌入H5网页

这个很简单。

  1. 申请注册微信小程序(必须是国内企业版)。
  2. 登录管理后台,设置–开发设置–配置业务域名。
  3. 给你的网站配置ssl证书,开启https,开发阶段可以用natapp之类的内网穿透工具做。
  4. 下载安装微信开发者工具,使用小程序模式,新建小程序,在index页面或者其他页面(跳转一下)写:<web-view src="你的URL"></web-view>,保存编译运行即可。

二、获取用户信息

在微信调试工具里打开app.js,可以看到有个方法叫wx.login,已经有写好的基本逻辑。用户信息可以console.log查看一下res这个变量。

 wx.login({
      success: res => {
        if (res.code) {
          console.log(res)
          }// 发送 res.code 到后台换取 openId, sessionKey, unionId
      }
    })

可以看到,res里包含的只是一些基本用户信息,并不包含一个唯一的ID让我们确定这个用户。所以需要利用获取openid的机制,利用ajax与自建服务器交互。其逻辑图在小程序开发文档–登录机制里写的很清楚。
在这里我直接贴出来我的代码(C#,ashx一般处理程序),里面包含了服务器端获取openid的方式(将信息传到后台的时候,我用的是明文,实际上为了安全,可以自己加密一下再传到后台。)
小程序端:

 wx.login({
      success: res => {
        if (res.code) {
          //console.log(res)
          //发起网络请求
           wx.request({
             url: 'https://我的URL路径/处理文件.ashx',
             header: {
                      "Content-Type": "applciation/json"
                      },
             method: 'POST',
              data: {
               code: res.code
                    },
             success: function (res) {
               wx.setStorageSync('openid', res.data);
              // console.log(res.data)这里是成功后回调函数,已经拿到了服务器端获取到的openid
              //利用小程序的缓存能力,将获取到的openid存进缓存里,在小程序的生命周期里,都可以随意全局调用。
               
             }
            })
         
          }// 发送 res.code 到后台换取 openId, sessionKey, unionId
      }
    })

服务器端:

<%@ WebHandler Language="C#" Class="wx" %>
using System.Web;
using System.Net;
using System.Text;
using System.IO;
using System.Collections.Generic;
public class wx : IHttpHandler {

    public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "text/plain";
        HttpRequest request = context.Request; 
        Stream stream = request.InputStream;
        string json = string.Empty;
        string responseJson = string.Empty;
        if (stream.Length != 0)
        {
            StreamReader streamReader = new StreamReader(stream);
            json = streamReader.ReadToEnd();
        }
        Dictionary<string, object> ob =JSONSerializer.Deserialize<Dictionary<string, object>>(json);
        string js_code=ob["code"].ToString();
        //这里就是与微信服务器交互,获取openid 
        string data = HttpGet("https://api.weixin.qq.com/sns/jscode2session?appid=你的appid&secret=你的secret&js_code="+js_code+"&grant_type=authorization_code");
        Dictionary<string, object> suggestions =JSONSerializer.Deserialize<Dictionary<string, object>>(data);

        string openid = suggestions["openid"].ToString();
	    //将openid返回
        context.Response.Write(openid);
    }
  public static string HttpGet(string Url)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
        request.Method = "GET";
        request.ContentType = "text/html;charset=UTF-8";

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        Stream myResponseStream = response.GetResponseStream();
        StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
        string retString = myStreamReader.ReadToEnd();
        myStreamReader.Close();
        myResponseStream.Close();

        return retString;
    }
    public bool IsReusable {
        get {
            return false;
        }
    }

}

到此为止,就获取到了有效用户信息。

三、将用户信息传进web-view

首先我们发现,小程序里不可以像html那样随意append组件,web-view目前也仅有一个src可以和小程序环境有交互,所以,就想到可以用get方式传值,比如url配置url?openid=1234就可以把信息传进入。
以免出现没有用户信息的情况,我没有在index页面直接写web-view,我用它当前置页面,页面加载时,会判定有没有用户信息,如果有的话,会自动跳到web页面,web页面就是我方web-view的地方。如果没有用户信息的话,会强制获取用户信息,然后再跳转,其代码大概是这样的:

onLoad: function () {
    
    if (app.globalData.userInfo) {
      this.setData({
        userInfo: app.globalData.userInfo,
        hasUserInfo: true
      })
      wx.setStorageSync('url', encodeURI("你的url?nickname=" + app.globalData.userInfo.nickName + "&city=" + app.globalData.userInfo.city + "&country=" + app.globalData.userInfo.country + "&gender=" + app.globalData.userInfo.gender + "&province=" + app.globalData.userInfo.province + "&avatarUrl=" + app.globalData.userInfo.avatarUrl + "&openid=" + wx.getStorageSync('openid')))
      //这里的globalData变量是存的用户基本信息,我为了方便把它们都传进去了,其实就传一个openid就够了
      wx.navigateTo({
        url: '../web/web'
      })
    } else if (this.data.canIUse){
      // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
      // 所以此处加入 callback 以防止这种情况
      app.userInfoReadyCallback = res => {
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        })
        wx.setStorageSync('url', encodeURI("你的URL?nickname=" + app.globalData.userInfo.nickName + "&city=" + app.globalData.userInfo.city + "&country=" + app.globalData.userInfo.country + "&gender=" + app.globalData.userInfo.gender + "&province=" + app.globalData.userInfo.province + "&avatarUrl=" + app.globalData.userInfo.avatarUrl + "&openid=" + wx.getStorageSync('openid')))
        wx.navigateTo({
          url: '../web/web'
        })
      }
    } else {
      // 在没有 open-type=getUserInfo 版本的兼容处理
      wx.getUserInfo({
        success: res => {
          //console.log(res)
          app.globalData.userInfo = res.userInfo
          this.setData({
            userInfo: res.userInfo,
            hasUserInfo: true
          })
          wx.setStorageSync('url', encodeURI("你的URL?nickname=" + app.globalData.userInfo.nickName + "&city=" + app.globalData.userInfo.city + "&country=" + app.globalData.userInfo.country + "&gender=" + app.globalData.userInfo.gender + "&province=" + app.globalData.userInfo.province + "&avatarUrl=" + app.globalData.userInfo.avatarUrl + "&openid=" + wx.getStorageSync('openid')))
          wx.navigateTo({
            url: '../web/web'
          })
        }
      })
    }
  },

然后是web页面。
web.wxml:

<web-view src="{{url}}"></web-view>

web.js:

  onLoad: function (options) {
    var dataurl = wx.getStorageSync('url');//获取缓存里的url信息
    console.log(dataurl.length)
    this.setData({
      url: dataurl
    });
  },

上面有个重要的坑需要特别说明一下,这种传值方式在微信调试工具和安卓手机上完全没问题,但是在IOS上,你会发现传值传不进去,其原因就是在ios下,web-view的src不能出现中文或者一些特殊字符,当用户名比较特殊时,就传不进去值,所以,我在index.js里写了urlencode。当然,在这里也可以把内容加密一下再传至后台。
比如最简单的无需秘钥的base64,我写一下用法:

  onLoad: function (options) {
   
  	function base64_encode(str) { // 编码,配合encodeURIComponent使用
      var c1, c2, c3;
      var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
      var i = 0, len = str.length, strin = '';
      while (i < len) {
        c1 = str.charCodeAt(i++) & 0xff;
        if (i == len) {
          strin += base64EncodeChars.charAt(c1 >> 2);
          strin += base64EncodeChars.charAt((c1 & 0x3) << 4);
          strin += "==";
          break;
        }
        c2 = str.charCodeAt(i++);
        if (i == len) {
          strin += base64EncodeChars.charAt(c1 >> 2);
          strin += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
          strin += base64EncodeChars.charAt((c2 & 0xF) << 2);
          strin += "=";
          break;
        }
        c3 = str.charCodeAt(i++);
        strin += base64EncodeChars.charAt(c1 >> 2);
        strin += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
        strin += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
        strin += base64EncodeChars.charAt(c3 & 0x3F)
      }
      return strin
    }
     var dataurl = wx.getStorageSync('测试base64');
	var base64Url = base64_encode(dataurl)
  },

到此为止,用户信息就传到后台了,那么后台就可以做用户对应了,然后将openid在页面的生命周期里保持,就可以记录用户行为了。

附加:还可以做的事情

1.分享页面

首先,我们要知道,所有的内容都是在web-view里的,所以当用户分享小程序时,无论用户正在看哪个页面,分享出去之后,其他用户打开,web-view都会重新加载,直接跳到主页,那么,用户分享页面的时候,怎么样才能让其他人打开的时候直接跳到分享者正在看的页面呢?
在这里我们注意到:

  1. 微信小程序在分享的时候,可以获取到web-view里的当前url。
  2. 分享方法的回调里可以写一个打开小程序页面的方法。

那么我们可以这样设计:

  1. 对各个页面做改动,对于一些非敏感值,不要用cookie或者缓存池的方式页面传值,而是用get方式在路径上留下id:URL?id=1,这样分享出去的页面,本身就带有基本信息,直接就可以还原出来。
  2. 小程序里新建一个页面,比如叫content,也是只放一个web-view。
  3. 在分享事件里,这样写:
 onShareAppMessage: function (options) {
    function base64_encode(str) { // 编码,配合encodeURIComponent使用
      var c1, c2, c3;
      var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
      var i = 0, len = str.length, strin = '';
      while (i < len) {
        c1 = str.charCodeAt(i++) & 0xff;
        if (i == len) {
          strin += base64EncodeChars.charAt(c1 >> 2);
          strin += base64EncodeChars.charAt((c1 & 0x3) << 4);
          strin += "==";
          break;
        }
        c2 = str.charCodeAt(i++);
        if (i == len) {
          strin += base64EncodeChars.charAt(c1 >> 2);
          strin += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
          strin += base64EncodeChars.charAt((c2 & 0xF) << 2);
          strin += "=";
          break;
        }
        c3 = str.charCodeAt(i++);
        strin += base64EncodeChars.charAt(c1 >> 2);
        strin += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
        strin += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
        strin += base64EncodeChars.charAt(c3 & 0x3F)
      }
      return strin
    }
    console.log(options.webViewUrl)
    var webViewUrl = base64_encode(options.webViewUrl)
    console.log(webViewUrl)
    return {

      
      path: 'pages/content/content?url=' + encodeURI(webViewUrl)  // 路径,传递参数到指定页面。

    }

  }

当然,content里怎么写也显而易见了。
content.wxml:

<web-view src="{{url}}"></web-view>

content.js

onLoad: function (options) {
    var openid = wx.getStorageSync('openid');
    if (openid.length<=10)
    {
      wx.navigateTo({
        url: '../index/index'
      })
    }
    function base64_decode(input) { // 解码,配合decodeURIComponent使用
      var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
      var output = "";
      var chr1, chr2, chr3;
      var enc1, enc2, enc3, enc4;
      var i = 0;
      input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
      while (i < input.length) {
        enc1 = base64EncodeChars.indexOf(input.charAt(i++));
        enc2 = base64EncodeChars.indexOf(input.charAt(i++));
        enc3 = base64EncodeChars.indexOf(input.charAt(i++));
        enc4 = base64EncodeChars.indexOf(input.charAt(i++));
        chr1 = (enc1 << 2) | (enc2 >> 4);
        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
        chr3 = ((enc3 & 3) << 6) | enc4;
        output = output + String.fromCharCode(chr1);
        if (enc3 != 64) {
          output = output + String.fromCharCode(chr2);
        }
        if (enc4 != 64) {
          output = output + String.fromCharCode(chr3);
        }
      }
      return utf8_decode(output);
    }


    function utf8_decode(utftext) { // utf-8解码
      var string = '';
      let i = 0;
      let c = 0;
      let c1 = 0;
      let c2 = 0;
      while (i < utftext.length) {
        c = utftext.charCodeAt(i);
        if (c < 128) {
          string += String.fromCharCode(c);
          i++;
        } else if ((c > 191) && (c < 224)) {
          c1 = utftext.charCodeAt(i + 1);
          string += String.fromCharCode(((c & 31) << 6) | (c1 & 63));
          i += 2;
        } else {
          c1 = utftext.charCodeAt(i + 1);
          c2 = utftext.charCodeAt(i + 2);
          string += String.fromCharCode(((c & 15) << 12) | ((c1 & 63) << 6) | (c2 & 63));
          i += 3;
        }
      }
      return string;
    }
    var weburl = base64_decode(options.url)//这里就是将分享时的web-view的页面路径值还原出来
    var data=""
    if (weburl.indexOf("?")!=-1)
    {
      data ="&openid="+openid
    }
    else
    {
      data = "?openid=" + openid
    }//这里是将openid加在每个页面上,可以用来开发记录用户行为的模块
    this.setData({
      url: weburl + data
    })
   
  },

2.页面里的其他外链

如果小程序里的页面有其他链接的话,会提示“不支持打开非业务域名xxxxx,请重新配置”。因为web-view不是浏览器,不是所有的网址都可以跳的,只有你自己网址,在后台配置过业务域名的,才可以跳。所以,这里要自己在自己的网站里把跳转屏蔽一下,当用户点击一些非业务链接地址跳转的时候,弹出“此链接是外部链接,已复制到粘贴板”,然后用clipboardjs把链接内容复制到粘贴板即可,这样用户体验更好点。这里就不讲怎么用了,点击链接可以去它的GitHub主页看一下,有各种demo,很简单。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章