【微信】.NET,利用微信JS-SDK调用微信接口的例子
本帖最后由 李维强-15级 于 2016-12-12 00:50 编辑微信作为这个时代的产物,要知道怎么去用它,把它加入到我们自己的页面里面,是现在的趋势,下面我就演示下如何调用微信的接口,开启一个微信自定义分享的页面
首先需要一个服务器和一个域名,没得的同学可以用花生壳+域名转向到自己的电脑也可以实现哈
下面是官网说明文档https://mp.weixin.qq.com/wiki/home/index.html
然后是我自己的代码实现微信服务器配置接入http://www.cqutlab.cn/thread-206-1-1.html
首先是微信说的,在服务器里面需要全局缓存accessToken和ticket具体看如下
https://mp.weixin.qq.com/wiki/11/74ad127cc054f6b80759c40f77ec03db.html#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95
我这里是直接写了个线程完成,90分钟去更新一次,重新获取票据,然后保存,给后面的接口调用签名做准备
下面直接贴代码
首先需要在global里面建立全局变量并且开启线程
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
Application.Add("gtest", 0);
Application.Add("mainURL", "http://n.cqutbbs.cn/");
//
//wxd9ab4c2b8379f20d
Application.Add("appId", "XXXXXXXXXXXX");
Application.Add("appsecret", "XXXXXXXXXXXX");
Application.Add("jsapi_ticket", "");
Application.Add("noncestr", "");
Application.Add("UNIXTime", "");
acc_TokenClass mythread = new acc_TokenClass();
mythread.context = HttpContext.Current;
mythread.start();
}
然后贴出线程函数
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.Mvc;
using System.Data.SqlClient;
using HTMLTest.SQLConn;
using System.Security.Cryptography;
using System.Web.Security;
using System.Net;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Text;
using System.Data;
using HTMLTest.Tools;
namespace HTMLTest.myThread
{
public class acc_TokenClass
{
BaseDao basedao = new BaseDao();
Thread thread = null;
public bool isOpen = false;
//public HttpContext context;
//public HttpContext myHttpContext_1;
//public acc_TokenClass(HttpContext myHttpContext)
//{
// myHttpContext_1 = myHttpContext;
//}
public HttpContext context
{
get;
set;
}
public void run()
{
RandomStr RString = new RandomStr();
//if (null != context.Application)
//{
// string aaa = context.Application["gtest"].ToString();
// context.Application["gtest"] = 50;
//}
while (true)
{
if (thread != null)
{
//微信公众号的东西
//string appid = "wxd9ab4c2b8379f20d";
//string secret = "7dd034c6d70f99038ae465d5f6b7fbfc";
string appid = context.Application["appId"].ToString();
string secret = context.Application["appsecret"].ToString();
string url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret + "";
HttpWebRequest req;
HttpWebResponse resp;
StreamReader sr;
string fullhtml = null;
try
{
req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "GET";
req.KeepAlive = true;
req.Timeout = 30000;
req.ContentType = "application/json;encoding=utf-8";
resp = (HttpWebResponse)req.GetResponse();
if (resp.StatusCode != HttpStatusCode.OK) //如果服务器未响应,那么继续等待相应
{
Dictionary<string, object> datalog = new Dictionary<string, object>();
datalog.Add("type", int.Parse("2"));
datalog.Add("OpTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
datalog.Add("OpID", "0");
datalog.Add("OpLog", "异常,获取服务器令牌盾时候,服务器没有返回200,返回值" + resp.StatusCode.ToString());
basedao.save("T_LOG", datalog);
//req.Abort();
//resp.Close();
Thread.Sleep(5000);
continue;
}
sr = new StreamReader(resp.GetResponseStream(), Encoding.UTF8);
fullhtml = sr.ReadToEnd().Trim();
}
catch (WebException ex)
{
ex.StackTrace.ToString();
Dictionary<string, object> datalog = new Dictionary<string, object>();
datalog.Add("type", int.Parse("2"));
datalog.Add("OpTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
datalog.Add("OpID", "0");
datalog.Add("OpLog", "异常,获取令牌时候抛出的catch :" + ex.Message.ToString().Replace("'", "''").Replace(" ", ""));
basedao.save("T_LOG", datalog);
Thread.Sleep(5000);
continue;
}
req.Abort();
resp.Close();
sr.Close();
//access_token
if (fullhtml.Contains("access_token"))
{
}
else
{
Dictionary<string, object> datalog = new Dictionary<string, object>();
datalog.Add("type", int.Parse("2"));
datalog.Add("OpTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
datalog.Add("OpID", "0");
datalog.Add("OpLog", "异常:获取令牌盾错误:" + fullhtml.Replace("'", "''").Replace(" ", ""));
basedao.save("T_LOG", datalog);
Thread.Sleep(5000);
continue;
}
//JToken jo = (JToken)JsonConvert.DeserializeObject(fullhtml);
string acc_Token=null;
JToken json_AccToken = JToken.Parse(fullhtml);
acc_Token = json_AccToken.Value<string>("access_token");
url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + acc_Token + "&type=jsapi";
fullhtml = null;
try
{
req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "GET";
req.KeepAlive = true;
req.Timeout = 30000;
req.ContentType = "application/json;encoding=utf-8";
resp = (HttpWebResponse)req.GetResponse();
if (resp.StatusCode != HttpStatusCode.OK) //如果服务器未响应,那么继续等待相应
{
Dictionary<string, object> datalog = new Dictionary<string, object>();
datalog.Add("type", int.Parse("2"));
datalog.Add("OpTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
datalog.Add("OpID", "0");
datalog.Add("OpLog", "异常,jsapi_ticket时候,服务器没有返回200,返回值" + resp.StatusCode.ToString());
basedao.save("T_LOG", datalog);
//req.Abort();
//resp.Close();
Thread.Sleep(5000);
continue;
}
sr = new StreamReader(resp.GetResponseStream(), Encoding.UTF8);
fullhtml = sr.ReadToEnd().Trim();
}
catch (WebException ex)
{
ex.StackTrace.ToString();
Dictionary<string, object> datalog = new Dictionary<string, object>();
datalog.Add("type", int.Parse("2"));
datalog.Add("OpTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
datalog.Add("OpID", "0");
datalog.Add("OpLog", "异常,获取jsapi_ticket时候抛出的catch :" + ex.Message.ToString().Replace("'", "''").Replace(" ", ""));
basedao.save("T_LOG", datalog);
Thread.Sleep(5000);
continue;
}
req.Abort();
resp.Close();
sr.Close();
//errcode
if (fullhtml.Contains("errcode"))
{
}
else
{
Dictionary<string, object> datalog = new Dictionary<string, object>();
datalog.Add("type", int.Parse("2"));
datalog.Add("OpTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
datalog.Add("OpID", "0");
datalog.Add("OpLog", "异常:获取jsapi_ticket时候,服务器返回值不包含errcode,数据可能出错:" + fullhtml.Replace("'", "''").Replace(" ", ""));
basedao.save("T_LOG", datalog);
Thread.Sleep(5000);
continue;
}
string jsapi_ticket = null;
int errcode;
JToken json_JSTicket = JToken.Parse(fullhtml);
errcode = json_JSTicket.Value<int>("errcode");
if (errcode != 0)
{
Dictionary<string, object> datalog = new Dictionary<string, object>();
datalog.Add("type", int.Parse("2"));
datalog.Add("OpTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
datalog.Add("OpID", "0");
datalog.Add("OpLog", "异常:获取jsapi_ticket时候,errcode!=0," + fullhtml.Replace("'", "''").Replace(" ", ""));
basedao.save("T_LOG", datalog);
Thread.Sleep(5000);
continue;
}
jsapi_ticket = json_JSTicket.Value<string>("ticket");
//这里生成随机16位字符串
string noncestr = RString.GenerateCheckCode(16);
//这里生成时间戳
string UNIXTime = ((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000).ToString();
//最后更新这3个基本的数据
//把这个存到全局变量里面去
context.Application["jsapi_ticket"] = jsapi_ticket;
context.Application["noncestr"] = noncestr;
context.Application["UNIXTime"] = UNIXTime;
//HttpContext.Current.Application["jsapi_ticket"] = jsapi_ticket;
//HttpContext.Current.Application["noncestr"] = noncestr;
//HttpContext.Current.Application["UNIXTime"] = UNIXTime;
//EncryptTool Encrypt = new EncryptTool();
//string b = "jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://mp.weixin.qq.com?params=value";
//string aaa = Encrypt.SHA1_Hash(b);
//string aaa1 = Encrypt.SHA1_Hash("jsapi_ticket=" + jsapi_ticket + "&noncestr=" + noncestr + "×tamp=" + UNIXTime + "&url=http://mp.weixin.qq.com?params=value");
}
Thread.Sleep(60000 * 90);
}
}
public void start()
{
if (thread == null)
{
thread = new Thread(run);
isOpen = true;
thread.Start();
}
}
}
}
里面涉及到的一个产生随机字符串的函数
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Cryptography;
using System.Text;
namespace HTMLTest.Tools
{
public class RandomStr
{
private int rep = 0;
public string GenerateCheckCodeNum(int codeCount)
{
string str = string.Empty;
long num2 = DateTime.Now.Ticks + this.rep;
this.rep++;
Random random = new Random(((int)(((ulong)num2) & 0xffffffffL)) | ((int)(num2 >> this.rep)));
for (int i = 0; i < codeCount; i++)
{
int num = random.Next();
str = str + ((char)(0x30 + ((ushort)(num % 10)))).ToString();
}
return str;
}
//方法二:随机生成字符串(数字和字母混和)
public string GenerateCheckCode(int codeCount)
{
string str = string.Empty;
long num2 = DateTime.Now.Ticks + this.rep;
this.rep++;
Random random = new Random(((int)(((ulong)num2) & 0xffffffffL)) | ((int)(num2 >> this.rep)));
for (int i = 0; i < codeCount; i++)
{
char ch;
int num = random.Next();
if ((num % 2) == 0)
{
ch = (char)(0x30 + ((ushort)(num % 10)));
}
else
{
ch = (char)(0x41 + ((ushort)(num % 0x1a)));
}
str = str + ch.ToString();
}
return str;
}
#region
/// <summary>
/// 从字符串里随机得到,规定个数的字符串.
/// </summary>
/// <param name="allChar"></param>
/// <param name="CodeCount"></param>
/// <returns></returns>
private string GetRandomCode(string allChar, int CodeCount)
{
//string allChar = "1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,i,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z";
string[] allCharArray = allChar.Split(',');
string RandomCode = "";
int temp = -1;
Random rand = new Random();
for (int i = 0; i < CodeCount; i++)
{
if (temp != -1)
{
rand = new Random(temp * i * ((int)DateTime.Now.Ticks));
}
int t = rand.Next(allCharArray.Length - 1);
while (temp == t)
{
t = rand.Next(allCharArray.Length - 1);
}
temp = t;
RandomCode += allCharArray;
}
return RandomCode;
}
#endregion
#region 获取由SHA1加密的字符串
public string EncryptToSHA1(string str)
{
SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
byte[] str1 = Encoding.UTF8.GetBytes(str);
byte[] str2 = sha1.ComputeHash(str1);
sha1.Clear();
(sha1 as IDisposable).Dispose();
return Convert.ToBase64String(str2);
}
#endregion
}
}
通过以上步骤,我们需要的几个参数都存到全局变量里面去了
context.Application["jsapi_ticket"]
context.Application["noncestr"]
context.Application["UNIXTime"]
好了 这个是前期准备了,然后接着以下工作
第一步,需要微信接入 看帖子http://www.cqutlab.cn/thread-206-1-1.html ,官网说明文档https://mp.weixin.qq.com/wiki/8/f9a0b8382e0b77d87b3bcc1ce6fbc104.html
第二步,在页面里面写JS配置相关东西
在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.0.0.js
请注意,如果你的页面启用了https,务必引入 https://res.wx.qq.com/open/js/jweixin-1.0.0.js ,否则将无法在iOS9.0以上系统中成功使用JSSDK
步骤三:通过config接口注入权限验证配置
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名,见附录1
jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
好了 这一步里面有很多东西前面几个参数都是我们之前开线程得到的几个全局变量,然后我直接贴一个页面出来
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<!DOCTYPE html>
<html style="font-size: 64px;">
<head runat="server">
<%--<meta name="viewport" content="width=device-width" />--%>
<script type ="text/javascript" src="/Scripts/jquery-1.8.3.min.js" ></script>
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<link type="text/css" rel="stylesheet" href="../../Content/StyleSheet1.css" />
<title>微信示例页面</title>
</head>
<body>
<% var otherController=DependencyResolver.Current.GetService<HTMLTest.Controllers.TestViewController>();
Dictionary<string, object> map = otherController.wxPageConfig(Request.Url.ToString(),"3");
%>
<div class="viewer">
<p class="title2">页面内容</p>
</div>
<script type="text/javascript">
wx.config({
// 配置信息正确与否 可以开debug:true来看
debug: false,
appId: '<%=map["appId"] %>',
timestamp: <%=map["UNIXTime"].ToString()%>,
nonceStr: '<%=map["noncestr"].ToString()%>',
signature: '<%=map["signature"].ToString()%>',
jsApiList: ['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQZone']
});
wx.ready(function () {
//document.getElementById('media').play();
//alert(window.location.href.split("#"));
//window.location.href.split("#");
var srcList = [];
$.each($('body img'),function(i,item){//$('.info_detail .container img') 容器中的图片
if(item.src) {
srcList.push(item.src);
$(item).click(function(e){
wx.previewImage({
current: this.src,
urls: srcList
});
});
}
});
wx.onMenuShareTimeline({
title: '布依-饿了么打折讨论', // 分享标题
link: '<%=Application["mainURL"].ToString()+"TestView/View4" %>', // 分享链接
imgUrl: '<%=Application["mainURL"].ToString()+"Content/16-12-11/logo.jpg" %>', // 分享图标
success: function () {
// 用户确认分享后执行的回调函数
},
cancel: function () {
// 用户取消分享后执行的回调函数
},
fail: function(){
}
});
wx.onMenuShareAppMessage({
title: '布依-饿了么打折讨论', // 分享标题
desc: '布依-饿了么打折讨论', // 分享描述
link: '<%=Application["mainURL"].ToString()+"TestView/View4" %>', // 分享链接
imgUrl: '<%=Application["mainURL"].ToString()+"Content/16-12-11/logo.jpg" %>', // 分享图标
type: '', // 分享类型,music、video或link,不填默认为link
dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
success: function () {
// 用户确认分享后执行的回调函数
},
cancel: function () {
// 用户取消分享后执行的回调函数
}
});
wx.onMenuShareQZone({
title: '《悟空》歌词解析', // 分享标题
desc: '100个人里面有100种理解,Squall001带来自己的理解', // 分享描述
link: '<%=Application["mainURL"].ToString()+"TestView/View4" %>', // 分享链接
imgUrl: '<%=Application["mainURL"].ToString()+"Content/16-12-11/logo.jpg" %>', // 分享图标
success: function () {
// 用户确认分享后执行的回调函数
},
cancel: function () {
// 用户取消分享后执行的回调函数
}
});
});
</script>
</body>
</html>
可以看到在页面的上面一开始就向后台请求了wxPageConfig这个函数,这里特别强调下,微信访问这个页面可能在后面加了些参数什么的,而SDK特别强调了需要传入当前访问页面的全部地址,所以我在上面页面里面特地使用了Request.Url.ToString()来获取当前的页面,当然后面的那个参数是我为了记录当前访问的第几个新闻的ID,这里可以根据自己的开发选择使用哈,下面贴出wxPageConfig这个代码
public Dictionary<string,object> wxPageConfig(string pageUrl,string NewsID)
{
string absoluteURL = pageUrl;
string appid = System.Web.HttpContext.Current.Application["appId"].ToString();
string jsapi_ticket = System.Web.HttpContext.Current.Application["jsapi_ticket"].ToString();
string noncestr = System.Web.HttpContext.Current.Application["noncestr"].ToString();
string UNIXTime = System.Web.HttpContext.Current.Application["UNIXTime"].ToString();
EncryptTool Encrypt = new EncryptTool();
string signature = Encrypt.SHA1_Hash("jsapi_ticket=" + jsapi_ticket + "&noncestr=" + noncestr + "×tamp=" + UNIXTime + "&url=" + absoluteURL);
Dictionary<string, object> map = new Dictionary<string, object>();
map.Add("appId", appid);
map.Add("jsapi_ticket", jsapi_ticket);
map.Add("noncestr", noncestr);
map.Add("UNIXTime", UNIXTime);
map.Add("signature", signature);
HTMLTest.SQLConn.BaseDao dao = new BaseDao();
string num;
try
{
string[] prototype = new string[] { "clickNum", "title", };
List<Dictionary<string, object>> list = dao.findMapByProperties("t_m_clicklog", prototype, " newsID=" + NewsID);
num = list["clickNum"].ToString();
SQLConn.BaseDao.execute("update t_m_clicklog set clickNum=clickNum+1 where newsID=" + NewsID);
map.Add("ClickNum", num);
}
catch (Exception e)
{
Dictionary<string, object> datalog = new Dictionary<string, object>();
datalog.Add("type", int.Parse("1"));
datalog.Add("OpTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
datalog.Add("OpID", "0");
datalog.Add("OpLog", "异常:TestView/addClick里面 sql=\"update t_m_clicklog set clickNum=clickNum+1 where newsID=1\"");
dao.save("T_LOG", datalog);
map.Add("ClickNum", "阅读量拉取错误");
return map;
}
return map;
}
最后需要特别注意,任何公众号,都需要至少关注一个用户,也就是自己需要关注一下,才能应用哈,我当时就被这个坑了很久。另外 wx.config配置完成后,会触发wx.ready这个函数,在wx.config里面设置debug: false,是正常使用,如果设置为true的话,就是开启调试信息,但是这个调试信息只能在微信浏览器里面可以看到,其他浏览器看不见的。
好了 不懂得可以问我。
页:
[1]