难点:多个前后端交互 (#体会#:为什么要全栈学习?)
重点:前后端调用,略过CSS样式、JS SDK等内容
实现功能:
目标:沟通前后端,把原理吃透,将(ASP.NET 6.0)代码开源!
理解:消息在多方传递:
微信开放了一些“接口”:url,可以“按约定”传递参数,获取相应的信息(例如:二维码/微信用户OpenId)。
#脑子始终保持清醒:#
所有的url都使用HTTP协议,所以需要额外的安全/身份/权限验证。
首先需要向wechat证明,这个服务器(17bang.ren)“是我的”。
问题:开发时的localhost不能被公网访问,也就无法被wechat访问。
为方便开发调试,需要利用网络地址转换(Network Address Translation)的内网穿透技术:让内网地址(的某个端口)能被外网访问。
下载 - 安装 - WebUI 修改 http隧道 (改端口号:和服务器配)
cpolar http 9200
通过浏览器进入配置界面:
注意本地地址里的端口号,要和VS运行时相对应。
最后,
[ApiController] [Route("[controller]")] public class WechatController : BaseController<WechatUser> { // GET: 向微信证明服务器权属 [HttpGet] [Route("Handler")] public string? Get([FromQuery] string? echostr) { return echostr;以上省略安全验……
另外一个需要验证的是向Wechat发起请求的“用户”身份:你谁呀,我凭什么信你?所以
首先,需要向Wechat申请获得appId和appsecret(演示)。
但(基于安全原因)WeChat不想开发人员每次都把appId和appsecret传来传去,所以引入access_token:
演示接口调试工具:根据appId和appSecret向Wechat接口发起请求,得到包含access_token及其过期时间的JSON数据
#小提示#:站在腾讯开发人员的角度思考问题,我access_token发给谁,怎么发,如何确保万无一失……?
微信公众号
HOST白名单
让外部程序就可以访问你的IISExpress(还)
将之前的localhost,改成任何域名都行 (理解/复习)
<bindings> <!--<binding protocol="http" bindingInformation="*:51137:localhost" />--> <binding protocol="http" bindingInformation="*:51137:*" /> </bindings>
以管理员身份,运行cmd(命令提示符)
netsh http show urlacl
添加(add)/删除(delete)网址
netsh http add urlacl url=http://*:51137/ user=everyone
netsh http delete urlacl url=http://*:51137/
有时候还需要添加/删除防火墙规则:
netsh advfirewall firewall add rule name="IISExpressWeb" dir=in protocol=tcp localport=51137 action=allow netsh advfirewall firewall delete rule name="IISExpressWeb"
检查是否微信(浏览器)环境:即网页是否在微信中打开。方法是检测userAgent:
function inWechat() { var userAgent = window.navigator.userAgent.toLowerCase(); return userAgent.indexOf('micromessenger') !== -1;
internal static bool inWechat(HttpRequest request) { return request.Headers.UserAgent.Any( u => u.ToLower().Contains("micromessenger"));
我们的策略是:
只要在微信环境且没有授权过(根据cookie判断:授权之后会将openId存进cookie)
if (!$.cookie("WechatOpenId")
private static bool hasOpenId(HttpRequest request) { return request.Cookies.TryGetValue( Const.CookieName.WechatOpenId, out string value);
就自动跳转到授权页面,链接如下:
public string WebAuth() { string callbackUrl = $"{Config.Host.Current}/Api/Wechat/GetOpenIdByAuth"; return $"https://open.weixin.qq.com/connect/oauth2/authorize?" + $"appid={Config.Wechat.AppId}&" + $"redirect_uri={UrlEncoder.Default.Encode(callbackUrl)}&" + $"response_type=code&" + $"scope=snsapi_base&" + $"state=123#wechat_redirect" ; }注意:Host需要在微信中设置
链接没有敏感信息(如果appId不算的话),也可以直接前端生成。但考虑到重用,且指定callbackUrl是后端职责,所以放在后端……
跳转方式:
function goWechatAuth() { $.get("/Api/Wechat/WebAuth", function (data) { location = data;
context.Result = new RedirectResult(url);//filter中
授权后Wechat会发送Http请求到指定回调接口,根据code获取openId
public async Task<IActionResult> GetOpenIdByAuth(string code, string state) { string url = $"https://api.weixin.qq.com/sns/oauth2/access_token?" + $"appid={Config.Wechat.AppId}&" + $"secret={Config.Wechat.AppSecret}&" + $"code={code}&" + $"grant_type=authorization_code"; var response = sendHttpGet(url); string openId = JsonConvert.DeserializeObject<WebAuthToken>(await response)?.openId; Response.Cookies.Append(Const.CookieName.WechatOpenId, openId, new CookieOptions {
只演示scope=snsapi_base,还需要继续通过accessToken获取userinfo的过程略……
不是用户授权,因为需要在PC网页上显示,
功能:关注公众号后自动注册/登录。首先需要一个
用户用微信扫描此二维码,就会被引导到公众号。
同时,WeChat服务器会将该用户操作反馈给Api服务器:[HttpPost] [Route("Handler")] public async Task<string> Post()
获取POST内容并解析:
Request.EnableBuffering(); body = await new StreamReader(Request.Body).ReadToEndAsync(); XElement elements = XElement.Parse(body);首先可以得到关键信息msgType,即用户对该公众号进行了何种操作:
string? msgType = elements.Element("MsgType")?.Value;
msgType可以是:subscribe(订阅),或者SCAN(扫描),或者其他……
if (evt == "subscribe" || evt == "SCAN") //目前仅处理订阅/扫描
然后我们获取当前用户的openId:
string? openId = elements.Element("FromUserName")?.Value;
关键在于怎么知道OpenId对应的是哪一个用户?
利用二维码的参数(通过参数可以告诉api):
注意:参数只能是int类型,不能是string类型。因为要同时处理subscribe和scan(用户已关注时)事件,scan后返回的只能是int
/// <summary> /// 仅用于生成自增Id做为场景二维码参数 /// </summary> public class WechatUser : BaseEntity
EventKey | 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id |
openId如何通知给网页服务器
api服务器只需要保留OpenId
用户已经登录
底层逻辑:先根据openId查找用户,如果用户
已经绑定,直接登录
没有绑定,先绑定再登录
不要用Model绑定
public async void Post([FromBody] string? message) public async void Post([FromBody]WeChatMessage? message)
不要因为传送的是xml文本就:
[Consumes("application/xml")]
多快好省!前端后端,线上线下,名师精讲
更多了解 加: