前后端分环境开发微信网页项目

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

主要问题

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流向后端,后端将流过来的数据处理后又将新的数据推向前端… …如此反复,直到用户跳转到新的页面开始新的一轮数据流动或者直到用户关闭浏览器为止。

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

这里帅华君提两种前后端分环境开发的方式:

  • 后端主导:后端设置响应头允许跨域,某些场景下前端也要设置参数确保每次Ajax请求携带凭证的唯一性。
  • 前端主导:无需后端费心设置请求头,前端搭建Nodejs本地服务器,借助http代理模块实现类似爬虫的特定路径的接口转发功能。

方式一

前端在自己的电脑上搭建本地测试服务器(如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请求。

方式二

方式一中,前端搭建的Nodejs的作用仅仅是为了保证能发起Ajax请求,至于前端能不能收到后端的响应帅华君在方式一中已经介绍过了,主要是通过后端设置响应头来告诉浏览器放宽因为同源安全策略的限制。方式一中前端搭建Nodejs服务另一个目的是提供静态文件服务。

方式二帅华君要介绍的和方式一相似但又不同,前端同学依然需要搭建Nodejs环境,以提供静态文件服务和确保能发起Ajax请求。除此之外,Nodejs本地服务还肩负着另一个任务,那就是转发Ajax请求,简单来讲,就是前端同学可以通过为基于Nodejs的Express框架添加中间件,以实现将网页中js脚本发起的Ajax请求原本发送到本地Nodejs服务器,实际上转发到了某一台远程服务器,而远程服务器接收到请求后的响应信息默认情况下会原封不动的返回。

也就是说,方式二中搭建的Nodejs服务环境中嵌入了一个类似爬虫的中间件,通过指定要爬取的远程服务器获取想要从远程服务器获取的内容。

当然,Nodejs做爬虫也不是那么简单,但也不复杂,前端与后端需要协商出一套规则来,既保证其它恶意爬虫无法顺利了解爬取规则,又能使“自己人”能爬取到远程服务器的资源。

这里帅华君所说的规则,指的就是Nodejs爬虫发起的请求信息是否能逃过远程服务器防止恶意爬取而检查请求头某些属性值的规则。

比如说 Referer 请求头,虽然Ajax请求可以设置某些安全的请求头,但是像Referer 这样的请求头浏览器为了安全考虑是不允许前端手动设置的,但是Nodejs作为爬虫完全不care这套安全规则,前端通过Nodejs可以手动设置任何请求头,伪装成让远程服务器误以为合法的请求,从而让前端成功拿到数据。

帅华君推荐一款第三方模块Nodejs模块,使用npm安装:

npm i http-proxy-middleware

该模块配置简单,设置匹配路径,远程主机域名,设置请求头等,详细配置信息阅读http-proxy-middleware 官方文档

例如帅华君在本地发起Ajax请求根据微信用户与公众号产生的唯一openid获取用户资料。

内网穿透

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

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

没有什么太高深的技术,不想造轮子可以 natapp.cn 注册申请一个域名即可。

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

CSDN上看到一篇介绍内网穿透原理及几款工具的文章,写的挺全面,推荐给想自己造轮子的高手:

《一分钟实现内网穿透(ngrok服务器搭建)》

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

下一篇《服务器端采用Nodejs实现微信网页授权及JS-SDK接口配置》

上一篇《基于Nodejs实现服务器端发送Email》

快速跳转 心头好文 - language - 《前后端分环境开发微信网页项目》

发布日期 2018年12月15日 星期六

版权声明 自由转载-非商用-非衍生-保持署名(创意共享3.0许可证