当微软牛津计划遇到微信App ——微信实现部分

2023-12-30 20:40

本文主要是介绍当微软牛津计划遇到微信App ——微信实现部分,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者:王豫翔,微软连续多年多个方向的MVP,目前主要关注大数据、云技术和人工智能。在编程道路上遵循自己的“三少”“三多”原则:少讨论概念,少争论特征、少议论模型;多写代码、多做测试、多做应用。
本文为《程序员》原创文章,未经允许不得转载,更多精彩文章请订阅2016年《程序员》

微软牛津计划(Project Oxford)提供了一系列机器学习API,包含计算机视觉、语音识别和语言理解等认知服务,它能为微信开发带来怎样有趣的功能?请看本文分解。

微软牛津计划提供了一组基于Rest架构的API和SDK工具包,帮助开发者轻轻松松使用微软的自然数据理解能力为自己的解决方案增加智能服务。利用微软牛津计划构建你自己的解决方案,支持任意语言及任意开发平台。主要提供了四个自然语言处理方面的核心问题解决方案:人脸识别、语音识别、计算机视觉,以及语言理解智能服务。

图片描述

图1 应用界面

微软提供了这么强大的API,我第一时间就想,是不是可以迁移到微信平台上去做一些好玩的应用,不过在这之前,我没有做过任何微信开发的工作,所以本篇文章将分享整个实现的经验。

ASP.NET WebAPI实现微信接入验证

首先你需要一个微信公众号,很重要的是你需要完成认证,这点非常重要。当你完成公众号的基本设定后,我们需要为开发做第一件事情:让微信验证通过开发者中心页配置的服务器地址。微信服务器将发送GET请求到我们注册的服务器地址URL上,GET请求携带四个参数:signature、timestamp、nonce、echostr。我们编写了一个WebAPI对微信的请求进行反馈。

public HttpResponseMessage Get(string signature, string timestamp, string nonce, string echostr)
{string[] ArrTmp = { TOKEN, timestamp, nonce };Array.Sort(ArrTmp);string tmpStr = string.Join("", ArrTmp);var result = FormsAuthentication.HashPasswordForStoringInConfigFile(tmpStr, "SHA1").ToLower();return new HttpResponseMessage (){ Content = new StringContent(result, Encoding.GetEncoding("UTF-8"), "application/x-www-form-urlencoded") };
}

上面这段代码的要点是返回值,很多工程师在使用WebAPI返回给微信验证时一直失败,是因为忽略了返回值的编码要求是application/x-www-form-urlencoded。

ASP.NET WebAPI实现微信JS-SDK接口注入权限验证配置

我们的客户端采用微信的JS-SDK,但是所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用,当使用JS-SDK的时候,微信会将appId、timestamp、nonceStr和signature的参数进行加密和验算是否正确,所以我们需要提供一个正确的签名值。需要获得这个签名必须要完成两步,图2所示的UML描述了这个过程。

图片描述

图2 获取签名过程示意图

第一步:获取Access Token

if (HttpRuntime.Cache["access_token"] == null)
{var queryString = HttpUtility.ParseQueryString(string.Empty);queryString["grant_type"] = "client_credential";queryString["appid"] = APPID;queryString["secret"] = APPSECRET;var uri = "https://api.weixin.qq.com/cgi-bin/token?" + queryString;HttpResponseMessage response;response = await client.GetAsync(uri);var msg = await response.Content.ReadAsStringAsync();var jsonobj = Newtonsoft.Json.Linq.JObject.Parse(msg);HttpRuntime.Cache.Add("access_token",(string)jsonobj["access_token"],null,DateTime.Now.AddMinutes((int)jsonobj["expires_in"]),new TimeSpan(0, 0, 0),System.Web.Caching.CacheItemPriority.AboveNormal,null);
}

第二步:获取jsapi_ticket。

if (HttpRuntime.Cache["jsapi_ticket"] == null)
{var queryString = HttpUtility.ParseQueryString(string.Empty);queryString["access_token"] = (string)HttpRuntime.Cache["access_token"];queryString["type"] = "jsapi";var uri = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?" + queryString;HttpResponseMessage response;response = await client.GetAsync(uri);var msg = await response.Content.ReadAsStringAsync();var jsonobj = Newtonsoft.Json.Linq.JObject.Parse(msg);HttpRuntime.Cache.Add("jsapi_ticket",(string)jsonobj["ticket"],null,DateTime.Now.AddMinutes((int)jsonobj["expires_in"]), new TimeSpan(0, 0, 0), System.Web.Caching.CacheItemPriority.AboveNormal, null);
}

我们用于签名的素材都到齐了,我们要实现签名算法了。实现的代码如下。

var pwd = string.Format("jsapi_ticket={0}&noncestr={1}×tamp={2}&url={3}",(string)HttpRuntime.Cache["jsapi_ticket"],noncestr,timestamp,url);var tmpStr = FormsAuthentication.HashPasswordForStoringInConfigFile(pwd, "SHA1");return Request.CreateResponse(HttpStatusCode.OK, tmpStr);

这时候我们前端的HTML5就可以正确的采用JS-SDK了。

ASP.NET获取微信客户端上传的图片

本来我以为这是个很简单的事情,后来才发现,使用微信JS-SDK的时候,微信的HTML5客户端不会将图片直接POST给我的服务端,而是先提交给微信服务器,然后我的服务端需要通过serverId 来获得图片,大致的流程我绘制了UML,见图3,大家可以理解下。

图片描述

图3 获取微信客户端上传图片的过程

目前我们只关心服务器这段,我们将得到客户端传来的serverID,从微信的服务器上下载图片到本地。我们实现的代码如下。

public async Task<string> Get(string mediaid)
{var queryString = HttpUtility.ParseQueryString(string.Empty);queryString["access_token"] = await Get();queryString["media_id"] = mediaid;var uri = "http://file.api.weixin.qq.com/cgi-bin/media/get?" + queryString;HttpResponseMessage response;response = await client.GetAsync(uri);var msg = await response.Content.ReadAsStreamAsync();var file = response.Content.Headers.ContentDisposition.FileName.Replace("\"", "");var helper = new ProjecToxfordClientHelper();var content = await FileHelper.ReadAsync (msg);FileHelper.SaveFile(content, file);return file;
}</string>

好了,到了现在,我们对微信服务器需要实现的接口都差不多了,接下来就可以设计微信的客户端了。

WeUI设计微信客户端首页样式

WeUI是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信网页开发量身设计,可以令用户的使用感知更加统一。在微信网页开发中使用 WeUI,有如下优势:

  • 同微信客户端一致的视觉效果,令所有微信用户都能更容易地使用你的网站;
  • 便捷获取快速使用,降低开发和设计成本;
  • 微信设计团队精心打造,清晰明确,简洁大方。

该样式库目前包含button、cell、dialog、progress、toast、article、icon等各式元素,我们可以在https://github.com/weui/weui获得源代码和DEMO。

我们先做首页,以了解WeUI样式库的使用方式。

建立Index.html引入样式库和配置head节点。

<meta charset="utf-8">
<title>脸探</title>
<meta name="viewport" content="initial-scale=1.0,user- scalable=no,maximum- scale=1,width=device-width"> <link href="css/weui.css" rel="stylesheet"><link href="css/example.css" rel="stylesheet">

Body节点下的直接子元素是< div class=”page”>,其他所有元素都在这个节点下,我们的Index.html页面设计了两个div元素分别是:

<div class="hd">
<div class="bd"></div></div>

hd节点下的代码非常简单,就是title的描述。

<h1 class="page_title">脸探</h1>
<p class="page_desc">测测脸的相似度</p>

bd包含的是一个< div class=”weui_panel weui_panel_access”>,WeUI提供的Panel非常容易设计图文组合列表,WeUI提供了一系列很有用的类:weui_panel、weui_panel_access、weui_panel_hd、weui_panel_bd。

我们的Panel的标题就可以用weui_panel_hd进行修饰。

<div class="weui_panel_hd"></div>

具体的内容可以被weui_panel_bd修饰。

<div class="weui_panel_bd"></div>

weui_panel_bd 的子元素如下:

<a href="" class=" weui_media_box weui_media_appmsg "><div class=" weui_media_hd"><imgsrc="img 4432144_111855038929_2.jpg"="" alt=""></imgsrc="img></div><div class=" weui_media_bd"><h4 class=" weui_media_title ">标题</h4><p class="weui_grid_label">内容 </p></div>  
</a>

了解了如何布局一个列表项,那首页就容易完成了,代码如下。

<div class="page"><div class="hd"><h1 class="page_title">脸探</h1><p class="page_desc">测测脸的相似度</p></div><div class="bd"><div class="weui_panel weui_panel_access"><div class="weui_panel_hd">娃像谁     </div><div class="weui_panel_bd"><a href="family.html" class="weui_media_box weui_media_appmsg"><div class="weui_media_hd"><img class="weui_media_appmsg_thumb" src="fonts/family.jpg" alt=""></div><div class="weui_media_bd"><h4 class="weui_media_title">三人照</h4><p class="weui_media_desc">上传一家三口三人照,立即知道孩子与父母相像指数</p></div></a><a href="family3.html" class="weui_media_box weui_media_appmsg"><div class="weui_media_hd"><img class="weui_media_appmsg_thumb" src="fonts/one.jpg" alt=""></div><div class="weui_media_bd"><h4 class="weui_media_title">单人照</h4><p class="weui_media_desc">上传一家三口各自照片,立即知道孩子与父母相像指数</p></div></a></div></div></div><div class="bd"><div class="weui_panel weui_panel_access"><div class="weui_panel_hd">夫妻相    </div><div class="weui_panel_bd"><a href="couple2.html" class="weui_media_box weui_media_appmsg">  <div class="weui_media_hd"><img class="weui_media_appmsg_thumb" src="fonts/couple.jpg" alt=""></div><div class="weui_media_bd"><h4 class="weui_media_title">双人照</h4><p class="weui_media_desc">上传你和TA的双人照,你立即知道你们的天生缘分指数</p></div></a><a href="couple.html" class="weui_media_box weui_media_appmsg"><div class="weui_media_hd"><img class="weui_media_appmsg_thumb" src="fonts/one.jpg" alt=""></div><div class="weui_media_bd"><h4 class="weui_media_title">单人照</h4><p class="weui_media_desc">上传你们两人各自照片,你立即知道你们的天生缘分指数</p></div></a></div></div></div>
</div>

我们得到的首页效果大致如图4所示。

图片描述

图4 微信客户端首页效果图

设计微信客户端功能页样式

以娃像谁-单人照的页面为例,页面代码如下。

<div class="pic_panel"><div class="parent" id="parent1"><i class="icon iconfont icon-210 human"></i></div><div class="parent" id="parent2"><i class="icon iconfont icon-nv human"></i></div><div class="clear"></div><div class="parent1like like"></div><div class="parent2like like"></div><div class="clear"></div><div class="picture" id="child"><i class="icon iconfont icon-child human"></i></div><form><input type="button" class="next" id="uploadImage" value="GO !!!"></form>
</div>

id=”parent1” 和 id=”parent2” 为存放父母照片的容器,id=”child”为存放孩子照片的容器,点击容器触发选择照片,选择完成点击按钮作比较。class=”parent1like”和class=”parent2like” 分别显示 id=”child”分别与id=”parent1”和id=”parent2” 对比的结果。

我们得到的页面效果类似图5所示的样子。

图片描述

图5 娃像谁—单人照的页面效果图

实现微信客户端交互

在之前我们写了一个WebAPI接口来实现微信JS-SDK接口注入权限验证配置,现在我们的客户端需要调用这个接口来做验证了。客户端你需要引用jweixin-1.0.0.js。

只要我们的业务需要使用微信JS-SDK,则都需要完成接口注入的权限验证,验证的方式我们来一步步分析见图6。

图片描述

图6 JS-SDK接口注入的权限验证过程

页面将noncestr(这个可以是页面定义一个常数)、timestamp(其实也可以是常数)、url当前页面地址提交给我们最早写的/api/weixin接口,然后将返回的签名提交给wx.config即可。下面的代码可以作为你的模板使用。

$(function () {var timestamp = Date.parse(new Date())/1000;var localurl = encodeURIComponent(window.location.href.split('#')[0]);$.ajax({url: 'http://www.********.cn/wxapi/api/weixin',dataType: "json",data: {noncestr: 'FFUmZdbWVT9mVP7a',timestamp: timestamp,url: window.location.href.split('#')[0]
},success: function (data) {
wxFace(data.toLowerCase());}})
function wxFace(signature) {wx.config({debug: false,appId: 'wxec54ec7f720993da',timestamp: timestamp,nonceStr: 'FFUmZdbWVT9mVP7a',signature: signature,jsApiList: ['checkJsApi','onMenuShareTimeline','onMenuShareAppMessage','chooseImage','previewImage','uploadImage','downloadImage']            });}});

然后我们定义选择图片函数,当选择id=”parent1”、 id=”parent2”、id=”child”时调用。

function chooseUpload(selector) {wx.chooseImage({success: function (res) {$("#loading").show();$(function () {$.each(res.localIds, function (i, n) {wx.uploadImage({localId: res.localIds.toString(), // 需要上传的图片的本地ID,由chooseImage接口获得                        isShowProgressTips: 0, // 默认为1,显示进度提示success: function (res1) {$.ajax({url: 'http://www.******.cn/wxapi/face/detect/' + res1.serverId,                                   dataType: "json",success: function (data) {$("#loading").hide(); if (JSON.parse(data).length == 1) { $(selector).html('<img src="' + n + '"> <br>')                                          .data('faceId', JSON.parse(data)[0].faceId);} else if (JSON.parse(data).length > 1) { alert('请选择单人照哦')} else { alert('啊,我看不到你的脸~')}}})},fail: function (res) {alert(JSON.stringify(res));}});});});}});
}

触发点击事件调用上传图片函数。

document.querySelector('#parent1').onclick = function () {chooseUpload('#parent1')
};
定义函数,将拿到的两张照片的id做对比。function verify(selector, parent, child) {$("#loading").show();$.ajax({url: 'http://www.******.cn/wxapi/face/verify/' + parent + '/' + child,    dataType: "json",success: function (data) {$("#loading").hide();$(selector).html('相似度:' + (JSON.parse(data).confidence * 100).toFixed(2) + '%')}})      }

最后是我们分享朋友圈的功能实现。

var shareData = {title: '测测孩子跟谁像',//分享的标题desc: '来看看孩子跟爸爸比较像还是跟妈妈比较像',//分享的描述link: window.location.href,//分享的快照imgUrl: 'http://www. .******.cn/WeFace/fonts/family.jpg'//分享的链接};wx.onMenuShareAppMessage(shareData);wx.onMenuShareTimeline(shareData);

分享结果如图7所示。

图片描述

图7 朋友圈分享功能效果图

总结

本文主要针对如何使用APS.NET WebAPI实现微信注入进行了深入讲解。下期会承接本文,重点分享服务的实现过程,内容主要有:调用封装微软牛津计划API、使用MongoDB存储数据和客户端如何使用。全文阅读完毕后,你将可以自己去编写更有价值的应用了。


订阅2016年程序员(含iOS、Android及印刷版)请访问 http://dingyue.programmer.com.cn
图片描述

订阅咨询:

• 在线咨询(QQ):2251809102
• 电话咨询:010-64351436
• 更多消息,欢迎关注“程序员编辑部”

这篇关于当微软牛津计划遇到微信App ——微信实现部分的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/553944

相关文章

微信公众号脚本-获取热搜自动新建草稿并发布文章

《微信公众号脚本-获取热搜自动新建草稿并发布文章》本来想写一个自动化发布微信公众号的小绿书的脚本,但是微信公众号官网没有小绿书的接口,那就写一个获取热搜微信普通文章的脚本吧,:本文主要介绍微信公众... 目录介绍思路前期准备环境要求获取接口token获取热搜获取热搜数据下载热搜图片给图片加上标题文字上传图片

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

MySQL双主搭建+keepalived高可用的实现

《MySQL双主搭建+keepalived高可用的实现》本文主要介绍了MySQL双主搭建+keepalived高可用的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、测试环境准备二、主从搭建1.创建复制用户2.创建复制关系3.开启复制,确认复制是否成功4.同

Java实现文件图片的预览和下载功能

《Java实现文件图片的预览和下载功能》这篇文章主要为大家详细介绍了如何使用Java实现文件图片的预览和下载功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... Java实现文件(图片)的预览和下载 @ApiOperation("访问文件") @GetMapping("

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

Java实现时间与字符串互相转换详解

《Java实现时间与字符串互相转换详解》这篇文章主要为大家详细介绍了Java中实现时间与字符串互相转换的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、日期格式化为字符串(一)使用预定义格式(二)自定义格式二、字符串解析为日期(一)解析ISO格式字符串(二)解析自定义