H5+.Net Webapi集成微信分享前后端代码 微信JS-SDK wx.onMenuShareTimeline wx.onMenuShareAppMessage
说明:
1/因为赚麻烦这里没有使用数据库或服务器缓存来存储access_token和jsapi_ticket,为了方便这里使用了本地的xml进行持久化这两个值以及这两个值的创建时间和有限期限。
2/每次请求先检查有没有存在并且在有效期内的access_token和jsapi_ticket,存在的话直接进行加密操作,不存在或过期重新请求wechat接口获得再进行加密。
3/每个分享的页面都需要将当页的url发送到服务器进行签名,且一定要encodeURIComponent,因为在微信中打开会自动给当前链接加个各种参数,从而导致url不一致,导致invalid signature签名错误。
4/分享的图标url( imgUrl )必须是绝对路径。
一 封装的微信授权工具类
WechatJsSdk.cs
using SouthRuiHeH5.Models;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web.Script.Serialization;
using System.Xml.Linq; namespace SouthRuiHeH5.Provider
{
public class WechatJsSdk
{
/// <summary>
/// 模拟get请求获取AccessToken
/// </summary>
/// <param name="appID"></param>
/// <param name="appSecret"></param>
/// <returns></returns>
public static string GetAccessToken(string appID, string appSecret)
{
string url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", appID, appSecret);
JavaScriptSerializer js = new JavaScriptSerializer();
AccessTokenOutput output = js.Deserialize<AccessTokenOutput>(HttpGet(url));
SaveAccessTokenInXml(output);
return output.access_token;
} /// <summary>
/// 将AccessToken保存进xml
/// </summary>
/// <param name="input"></param>
private static void SaveAccessTokenInXml(AccessTokenOutput input)
{
if (string.IsNullOrWhiteSpace(input?.access_token)) return;
var mappedPath = System.Web.Hosting.HostingEnvironment.MapPath("~/");
string filePath = mappedPath + "Xml/AccessToken.xml";
XDocument inputDoc = XDocument.Load(filePath);
inputDoc.Elements().First().Element("access_token").Value = input.access_token;
inputDoc.Elements().First().Element("expires_in").Value = input.expires_in;
inputDoc.Elements().First().Element("create_time").Value = DateTime.Now.ToString();
inputDoc.Save(filePath);
} /// <summary>
/// 模拟get请求获取JsapiTicket
/// </summary>
/// <param name="accessToken"></param>
/// <returns></returns>
public static string GetJsapiTicket(string accessToken)
{
string url = string.Format("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi", accessToken);
JavaScriptSerializer js = new JavaScriptSerializer();
JsapiTicketOutput output = js.Deserialize<JsapiTicketOutput>(HttpGet(url));
SaveJsapiTicketInXml(output);
return output.ticket;
} /// <summary>
/// 将JsapiTicket保存进xml
/// </summary>
/// <param name="input"></param>
private static void SaveJsapiTicketInXml(JsapiTicketOutput input)
{
if (string.IsNullOrWhiteSpace(input?.ticket)) return;
var mappedPath = System.Web.Hosting.HostingEnvironment.MapPath("~/");
string filePath = mappedPath + "Xml/JsapiTicket.xml";
XDocument inputDoc = XDocument.Load(filePath);
inputDoc.Elements().First().Element("ticket").Value = input.ticket;
inputDoc.Elements().First().Element("expires_in").Value = input.expires_in;
inputDoc.Elements().First().Element("create_time").Value = DateTime.Now.ToString();
inputDoc.Save(filePath);
} /// <summary>
/// get模拟请求
/// </summary>
/// <param name="Url"></param>
/// <param name="postDataStr"></param>
/// <returns></returns>
private static string HttpGet(string Url, string postDataStr = "")
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url + (postDataStr == "" ? "" : "?") + postDataStr);
request.Method = "GET";
request.ContentType = "text/html;charset=UTF-8"; HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream myResponseStream = response.GetResponseStream();
StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
string retString = myStreamReader.ReadToEnd();
myStreamReader.Close();
myResponseStream.Close();
return retString;
} private static string[] strs = new string[]
{
"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",
"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"
}; /// <summary>
/// 获取随机字符串
/// </summary>
/// <returns></returns>
public static string CreatenNonce_str()
{
Random r = new Random();
var sb = new StringBuilder();
var length = strs.Length;
for (int i = ; i < ; i++)
{
sb.Append(strs[r.Next(length - )]);
}
return sb.ToString();
} /// <summary>
/// 获取时间戳
/// </summary>
/// <returns></returns>
public static long CreatenTimestamp()
{
return (DateTime.Now.ToUniversalTime().Ticks - ) / ;
} /// <summary>
/// sha1加密string1 获得Signature
/// </summary>
/// <param name="jsapi_ticket"></param>
/// <param name="noncestr"></param>
/// <param name="timestamp"></param>
/// <param name="url"></param>
/// <returns></returns>
public static string GetSignature(string jsapi_ticket, string noncestr, long timestamp, string url)
{
string string1 = string.Format("jsapi_ticket={0}&noncestr={1}×tamp={2}&url={3}", jsapi_ticket, noncestr, timestamp, url);
return Sha1(string1);
} /// <summary>
/// sha1
/// </summary>
/// <param name="orgStr"></param>
/// <param name="encode"></param>
/// <returns></returns>
private static string Sha1(string orgStr, string encode = "UTF-8")
{
var sha1 = new SHA1Managed();
var sha1bytes = System.Text.Encoding.GetEncoding(encode).GetBytes(orgStr);
byte[] resultHash = sha1.ComputeHash(sha1bytes);
string sha1String = BitConverter.ToString(resultHash).ToLower();
sha1String = sha1String.Replace("-", "");
return sha1String;
} } }
二 webapi部分
ConfigController.cs
using SouthRuiHeH5.Models;
using SouthRuiHeH5.Provider;
using System;
using System.Configuration;
using System.Linq;
using System.Web.Http;
using System.Xml.Linq; namespace SouthRuiHeH5.Controllers
{
public class ConfigController : ApiController
{
public ConfigOutput Get([FromUri]string url)
{
string appId = ConfigurationManager.AppSettings.Get("AppId");
string appSecret = ConfigurationManager.AppSettings.Get("AppSecret"); if (string.IsNullOrWhiteSpace(appId)) throw new Exception("AppSeeting:AppId Missed");
if (string.IsNullOrWhiteSpace(appSecret)) throw new Exception("AppSeeting:AppSecret Missed"); string jsapiTicket = getJsapiTicketFromXml();
if (string.IsNullOrEmpty(jsapiTicket))
{
string accessToken = GetAccessTokenFromXmlFirst();
if (string.IsNullOrEmpty(accessToken)) accessToken = WechatJsSdk.GetAccessToken(appId, appSecret);
if (string.IsNullOrEmpty(accessToken)) throw new Exception("Get AccessToken Error");
jsapiTicket = WechatJsSdk.GetJsapiTicket(accessToken);
if (string.IsNullOrEmpty(jsapiTicket)) throw new Exception("Get JsapiTicket Error");
} ConfigOutput output = new ConfigOutput
{
appId = appId,
nonceStr = WechatJsSdk.CreatenNonce_str(),
timestamp = WechatJsSdk.CreatenTimestamp()
};
output.signature = WechatJsSdk.GetSignature(jsapiTicket, output.nonceStr, output.timestamp, url);
return output;
} /// <summary>
/// 检查xml是否有JsapiTicket,并且JsapiTicket在有效期内
/// </summary>
/// <returns></returns>
private string getJsapiTicketFromXml()
{
var mappedPath = System.Web.Hosting.HostingEnvironment.MapPath("~/");
string filePath = mappedPath + "Xml/JsapiTicket.xml";
XDocument inputDoc = XDocument.Load(filePath); string ticket = inputDoc.Elements().First().Element("ticket").Value;
bool expiresInTry = int.TryParse(inputDoc.Elements().First().Element("expires_in").Value, out int expiresIn);
bool createTimeTry = DateTime.TryParse(inputDoc.Elements().First().Element("create_time").Value, out DateTime createTime); if (!string.IsNullOrWhiteSpace(ticket) || !expiresInTry || !createTimeTry)
{
TimeSpan timeSpan = DateTime.Now.Subtract(createTime);
if (timeSpan.TotalSeconds < expiresIn)
return ticket;
}
return string.Empty;
} /// <summary>
/// 检查xml是否有AccessToken,并且AccessToken在有效期内
/// </summary>
/// <returns></returns>
private string GetAccessTokenFromXmlFirst()
{
var mappedPath = System.Web.Hosting.HostingEnvironment.MapPath("~/");
string filePath = mappedPath + "Xml/AccessToken.xml";
XDocument inputDoc = XDocument.Load(filePath); string accessToken = inputDoc.Elements().First().Element("access_token").Value;
bool expiresInTry = int.TryParse(inputDoc.Elements().First().Element("expires_in").Value, out int expiresIn);
bool createTimeTry = DateTime.TryParse(inputDoc.Elements().First().Element("create_time").Value, out DateTime createTime); if (!string.IsNullOrWhiteSpace(accessToken) || !expiresInTry || !createTimeTry)
{
TimeSpan timeSpan = DateTime.Now.Subtract(createTime);
if (timeSpan.TotalSeconds < expiresIn)
return accessToken;
}
return string.Empty; }
}
}
三 JS部分
wechat.share.js
var url = window.location.href.split('#')[]; $.get("/api/Config?url=" + encodeURIComponent(url), function (res) {
if (!res) return;
var input = res;
//input.debug = true;
input.jsApiList = ["onMenuShareTimeline", "onMenuShareAppMessage"];
wx.config(input);
}); wx.ready(function () {
onMenuShareTimeline();
onMenuShareAppMessage();
}); function onMenuShareTimeline() {
wx.onMenuShareTimeline({
title: '为态度喝彩!',
desc: '唯有创造价值,才能共享价值。南方瑞合三年定开基金(LOF)盛大发行中。',
link: url,
imgUrl: 'http://southruihe.huiz.cn/image/sharelogo.jpg',
success: function () { }
});
} function onMenuShareAppMessage() {
wx.onMenuShareAppMessage({
title: '为态度喝彩!',
desc: '唯有创造价值,才能共享价值。南方瑞合三年定开基金(LOF)盛大发行中。',
link: url,
imgUrl: 'http://southruihe.huiz.cn/image/sharelogo.jpg',
success: function () { }
});
}
四 其中使用的3个数据传输类
AccessTokenOutput.cs
namespace SouthRuiHeH5.Models
{
public class AccessTokenOutput
{
public string access_token { get; set; }
public string expires_in { get; set; }
public string errcode { get; set; }
public string errmsg { get; set; }
}
}
ConfigOutput.cs
namespace SouthRuiHeH5.Models
{
public class ConfigOutput
{
/// <summary>
/// 必填,公众号的唯一标识
/// </summary>
public string appId { get; set; }
/// <summary>
/// 必填,生成签名的时间戳
/// </summary>
public long timestamp { get; set; }
/// <summary>
/// 必填,生成签名的随机串
/// </summary>
public string nonceStr { get; set; }
/// <summary>
/// 必填,签名
/// </summary>
public string signature { get; set; }
}
}
JsapiTicketOutput.cs
namespace SouthRuiHeH5.Models
{
public class JsapiTicketOutput
{
public string errcode { get; set; }
public string errmsg { get; set; }
public string ticket { get; set; }
public string expires_in { get; set; }
}
}
H5+.Net Webapi集成微信分享前后端代码 微信JS-SDK wx.onMenuShareTimeline wx.onMenuShareAppMessage的更多相关文章
- 基于数据库的代码自动生成工具,生成JavaBean、生成数据库文档、生成前后端代码等(v6.0.0版)
TableGo v6.0.0 版震撼发布,此次版本更新如下: 1.UI界面大改版,组件大调整,提升界面功能的可扩展性. 2.新增BeautyEye主题,界面更加清新美观,也可以通过配置切换到原生Jav ...
- [Java 开源项目]一款无需写任何代码,即可一键生成前后端代码的工具
作者:HelloGitHub-小鱼干 JeecgBoot 是一款基于代码生成器的低代码开发平台,零代码开发.JeecgBoot 采用开发模式:Online Coding 模式-> 代码生成器模式 ...
- 记node前后端代码共用的一次坑
项目背景 nodejs项目,webpack打包,用axios请求,Promise封装,nunjucks模板引擎: 之前已将nunjucks模板通过webpack打包策略,做成前后端共用: 目前需要将网 ...
- 实战:一键生成前后端代码,Mybatis-Plus代码生成器让我舒服了
实战:一键生成前后端代码,Mybatis-Plus代码生成器让我舒服了 前言 在日常的软件开发中,程序员往往需要花费大量的时间写CRUD,不仅枯燥效率低,而且每个人的代码风格不统一.MyBatis-P ...
- 【SpringSecurity系列3】基于Spring Webflux集成SpringSecurity实现前后端分离无状态Rest API的权限控制
源码传送门: https://github.com/ningzuoxin/zxning-springsecurity-demos/tree/master/02-springsecurity-state ...
- 微信分享自定义图片标题摘要-微信官方API
我们平时在使用微信内置浏览器打开网页想要分享给好友或者发到朋友圈的时候经常会遇到这样的问题, 别人的网页分享的时候是这样的: 而我们自己的网页分享后这这样的: 看到有人说不做任何设置,微信分享时会自动 ...
- 封装微信分享到朋友/朋友圈js
在页面引入: <script src="/static/lib/jquery-2.2.2.min.js"></script><script src=& ...
- layui上传文件组件(前后端代码实现)
我个人博客系统上传特色图片功能就是用layui上传文件组件做的.另外采用某个生态框架,尽量都统一用该生态框架对应的解决方案,因为这样一来,有这么几个好处?1.统一而不杂糅,有利于制定相应的编码规范,方 ...
- SpringCloud微服务实战——搭建企业级开发框架(三十一):自定义MybatisPlus代码生成器实现前后端代码自动生成
理想的情况下,代码生成可以节省很多重复且没有技术含量的工作量,并且代码生成可以按照统一的代码规范和格式来生成代码,给日常的代码开发提供很大的帮助.但是,代码生成也有其局限性,当牵涉到复杂的业务逻辑 ...
随机推荐
- leetcode617
这道题想了很久,并没有掌握思想,写了很多,也没有解决.先贴出思考的过程. class Solution { public: vector<TreeNode> v1; vector<T ...
- 在centos上面编译安装python
前言 因为在学习storm的过程中需要安装python,storm是部署在linux上面的,所以需要将python安装在linux上面. 安装准备 python下载 官网链接:https://www. ...
- mysql const与eq_ref的区别
简单地说是const是直接按主键或唯一键读取,eq_ref用于联表查询的情况,按联表的主键或唯一键联合查询. 下面的内容翻译自官方方档: const该表最多有一个匹配行, 在查询开始时读取.由于只有一 ...
- mysql授权远程访问
mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'IDENTIFIED BY '123456' WITH GRANT OPTION; Query O ...
- weld
weld - 必应词典 美[weld]英[weld] v.焊接:熔接:锻接:使紧密结合 n.焊接点:焊接处 网络焊缝
- protobuf shutdownprotobuflibrary的时候crash,释放的指针出错
往往是多个子项目中有多次链接使用. 解决方法: 1. 使用静态库. 2. issure中有说2.6.1还未允许多次释放,建议使用3.4.x版本. 参考: https://github.com/prot ...
- [leetcode]39. Combination Sum组合之和
Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), fin ...
- eclipse定制化配置调优、初始化配置指南、可以解决启动慢等问题
配置eclipse的jvm参数 打开eclipse根目录下的eclipse.ini在最后面加上如下的jvm参数 -Xms400m -Xmx1400m -XX:NewSize=128m -XX:MaxN ...
- permissions required by Vibrator.vibrate: android.permission.VIBRATE
<!-- 静止休眠 --><uses-permission android:name="android.permission.WAKE_LOCK" />&l ...
- Step by Step Guide on Yanhua ACDP Clear BMW EGS ISN
Yanhua Mini ACDP authorize new function on BMW EGS ISN clearing.So here UOBDII want to share this st ...