前后端分离模式下搭建微信公众号网页项目

9,448 阅读10分钟

本文涉及对前后端分离及微信网页项目中的前端如何在本地环境中开发与调试的思考。

主要问题

1、如何配置微信公众平台开发环境

2、如何配置微信网页项目开发环境

3、如何解决前后端分离开发接口调用时的跨域问题

4、如何解决微信服务器无法访问本地测试环境问题

对于上面第一个关于如何配置微信公众号平台开发环境的介绍在帅华君之前的几篇文章中有所提及,大家可以在看完这篇文章后自行跳转阅读:

所以本篇文章主要介绍帅华君对上方第2、3、4项问题的思考。

微信网页项目

微信网页开发依托于微信内置浏览器为开发者提供了丰富的调用微信客户端接口或访问移动设备系统信息的能力(JS-SDK),认证后的订阅号和服务号可以获取用户信息,前提是开发者需要告诉微信服务器,用户当前访问的页面是安全的,那么微信如何判断用户当前访问的页面是安全的呢?这就需要前后端配合做一些简单的验证,以拿到微信服务器的授权。

如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。

关于获取用户信息的授权可以阅读 微信网页授权,由于只有微信认证订阅号和微信认证服务号才有获取用户信息的权限,因此可以在微信后台申请测试账号,来实验微信网页授权获取用户信息的过程。

帅华君在对网页授权机制的实验过程中一切还算顺利,只遇到了一个小问题,通过仔细阅读文档立即解决了这个小问题,那就是微信授权文档中提到的关于设置修改安全域名的配置项,不然或提示授权失败,点击右侧修改按钮:

在弹出的对话框中修改域名即可:

根据微信网页项目开发JS-SDK使用说明文档,如果需要使用微信内置浏览器丰富的API接口需要通过config接口注入权限验证配置以确保用户当前访问的页面是安全的。

wx.config({
  debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
  appId: '', // 必填,公众号的唯一标识
  timestamp: , // 必填,生成签名的时间戳
  nonceStr: '', // 必填,生成签名的随机串
  signature: '',// 必填,签名
  jsApiList: [] // 必填,需要使用的JS接口列表
});

经过帅华君的实验,可以通过向后端发送请求获取到配置信息,动态配置,类似如下方式:

getwechatjsconfig(client => {
  wx.config({
    debug: false,
    appId: 'wxa0bb1e90533c6981',
    timestamp: client.response.timestamp,
    nonceStr: client.response.noncestr,
    signature: client.response.signature,
    jsApiList: ['chooseImage','previewImage','uploadImage','downloadImage']
  })
})

帅华君封装了一个名为getwechatjsconfig的函数,这个函数会向服务器发送异步请求获取配置信息,服务端成功响应Ajax请求后前端会执行回调函数,回调函数中传入了XHR实例对象,对象的response属性包含后端返回的配置信息,将这些信息分别赋值到微信要求的对应的属性上,最终完成JS-SDK权限的校验,这时,用户在该页面上就可以使用微信JS-SDK提供的丰富实用的接口了。

前后端分离开发

为了让前端掌握更多主动权,更重要的是优化整个前端工作流程,提升前端写布局样式、写交互逻辑、写接口规范的效率,降低前后端工作内容的耦合度,让职责分工更加的明确,所以前后端分离开发模式势在必行。

前后端分离的一个好处是,前端无需care后端,前后端各写各的,只需通过一份类似传输协议一样的API接口规范说明文档让数据像源源不断的水流一样在用户能看得见的页面与服务器端之间流动。

首先存储在服务器的html文档及其中的js\css\image等资源从后端流向前端,用户代理(浏览器)将这些数据渲染成用户喜闻乐见的界面,随后用户在页面上的每一次操作都变成了像水一样的数据通过Ajax流向后端,后端将流过来的数据处理后又将新的数据推向前端… …如此反复,直到用户跳转到新的页面开始新的一轮数据流动或者直到用户关闭浏览器为止。

所以前后端分离开发首要解决的问题是如何保证数据在前后端传输的过程中保证畅通无阻!这就不得不提到关于跨域屏障的问题。

前端在自己的电脑上搭建本地测试服务器(如http://localhost:8080/),完成写布局样式、写交互逻辑、写测试接口这一系列繁杂的工作,唯有最后的写接口时(即从服务端http://www.example.com:80/获取数据)成为唯一的阻碍,遇到了跨域的问题,在前后端不做任何配置的情况下,Ajax是无法向远程服务器发送请求的,浏览器会警告前端开发者!

似乎浏览器帮我指了一条明路,只要服务器端在前端请求的资源的相应头上加上一项Access-Control-Allow-Origin配置即可:

服务端脚本中加上此Access-Control-Allow-Origin控制头后,此时再通过Ajax访问相同的API地址,便能成功,可以在控制台查看,响应同种的确有了Access-Control-Allow-Origin,这是服务端用来告诉浏览器我允许其它服务器跨域访问这个地址。

此时登录成功,这是帅华君拿自己的文章管理接口做的测试,为了获取全部文章列表,必须先登录,可是我发现,即便登录成功,再次向服务端发送获取全部文章列表的请求时,被告知的是请先登录!嗯???刚才不是登录成功了么?为什么???还要登录???

这是因为,按照常理,当我登录成功后,服务端会将当前用户的session信息保存到cookie中,cookie的唯一性能确保用户无需重复登录,cookie会在用户每次向服务器端发送请求时携带在请求头中,服务器端通过cookie验证用户是否登录。很明显问题出在了每次用户向服务器端发送请求时所携带的cookie都是新的cookie,所以即便用户这次请求成功登录,服务器端记住了只要下次凡是携带这个cookie的用户都是登录成功的,可问题是下一次用户的请求向服务器端发送的cookie不是那个被记录为登录成功的cookie,所以对于需要登录才能访问的接口,永远会提示用户需要登录。

所以现在要解决的问题是,如何保持同一个用户每次向服务端发送请求时,能确保请求头携带的cookie的唯一性,而不是总变来变去的!

其实很简单,前端只需在发起Ajax请求时,将设置如下属性,便可在其后的请求中使用相同的cookie信息:

xhr.withCredentials = true

后端代码同样需要做调整,只需在服务端相应头中添加一个允许前端携带凭证的请求头即可,以Express为例:

res.set('Access-Control-Allow-Credentials', true)

那么问题又来了,根据警告信息的提示,如果携带凭证信息,那么Access-Control-Allow-Origin相应头的值就不能是通配符*

ok,那咱就试试将通配符改一改,以Express为例:

res.set('Access-Control-Allow-Origin', 'http://localhost:8080')

经过试验,登录成功,并且再次向服务器端发送请求时,所携带的cookie值与登录成功的那一次一模一样,只要用户不手动清除cookie或服务器端不清除cookie,那么随后的所有请求都携带相同的cookie值。

但是,帅华君认为对于线上的服务器,将Access-Control-Allow-Origin设置为类似http://localhost:8080这种本地测试服务器地址是非常危险的操作,因为如果不怀好意的程序员随便在本地搭一个服务器就能向线上的远程服务器肆意发送携带凭证的请求的话,那岂不是对于线上的远程服务器来说很危险。

所以,建议在前后端分离开发时,能专门有一台只有局域网内的可信赖的ip地址才能访问的测试服务器,这台测试服务器首先保证了所有的Ajax请求都是咱们自己的前端发送的,不怕外人来捣乱,如此,这台模拟线上服务器的测试服务器可以设置相应头为允许本地测试服务器发送携带凭证的Ajax请求。

微信验证前端本地服务

凡是有微信公众平台开发经验的都知道,微信要求开发者必须配置一个安全域名,而这个安全域名下的URL地址必须是微信服务器可以访问到的,才能通过微信的验证,才能使用微信公众平台或者微信网页授权还有JS-SDK的校验。

帅华君的做法是,申请了一个支持内网穿透的域名地址,它的神奇之处在于,本地测试中的代码无需反复的部署到线上服务器上进行调试,微信服务器就可以间接的访问到本地服务器。

没有什么太高深的技术,大家自行前往 natapp.cn 注册申请购买一个域名即可。

经过对内网穿透的实验,微信公众平台的验证、微信网页授权的验证以及对JS-SDK配置项中URL地址的校验完全没问题。

当然这只是解决微信开发特有的需要验证的解决方案之一,帅华君也希望能了解其它在前后端分离的开发模式下微信公众平台以及微信网页项目的授权解决方案。


本文完。