百度和必应,微信开发平台,翻烂了,今天终于开发出来了。
1、与老板沟通,申请企业级微信公众号,耗时十个工作日。先申请公众号,再进行微信认证。一个人跑腿,多个部门协调。
https://mp.weixin.qq.com/
2、查看微信公众号接口说明
3、了解接入的机制,第一个请求是Get,第二个请求是Post。
4、一直报错:“该公众号提供的服务出现故障,请稍后再试”
ToUserName、FromUserName参数值取反了,二个参数取值调换位置。
5、代码如下
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Xml.Linq;
using Dapper;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using ST.Catering.Domain.Syses;
using ST.Catering.Infrastructure;
using ST.Catering.Infrastructure.Dtos;
using ST.Catering.Service.SysUserServices;
using ST.Water.Base;
using ST.Water.Base.Dtos;
using ST.Water.Controllers.Catering.Dtos;
using ST.Water.Controllers.WeiXinReply.Dtos;
using ST.Water.Helpers;
namespace ST.Water.Controllers.WeiXinReply
{
[Route("api/[controller]/[action]")]
[ApiController]
public class WeiXinReplyController : BaseController
{
private SqlConnectionPool _connectionPool;
private IHttpContextAccessor _accessor;
public WeiXinReplyController(SqlConnectionPool connectionPool, IHttpContextAccessor accessor)
{
_connectionPool = connectionPool;
_accessor = accessor;
}
/// <summary>
/// 微信看板配置
/// </summary>
[HttpPost]
public void ProcessRequest()
{
var context = _accessor.HttpContext;
context.Response.ContentType = "text/plain";
string responseMsg = Response(context.Request);
context.Response.Clear();
context.Response.ContentType = "UTF-8";
context.Response.WriteAsync(responseMsg);
}
private void WriteResponse(string responseMsg) {
var context = _accessor.HttpContext;
context.Response.ContentType = "text/plain";
context.Response.Clear();
context.Response.ContentType = "UTF-8";
context.Response.WriteAsync(responseMsg);
}
private string Response(HttpRequest request) {
LoggerHelper.Warn(request.Body.ToString());
return ReArticle("liaocj66", "liaocj66");
}
private string ReArticle(string fromUserName, string toUserName)
{
var title = "飞马点餐";
var description = "";
var picUrl = "https://sps.pheyma.com:5009/food/ff64a4b9ee2f423696e5b1d6949ce9af.jpg";
var url = "https://sps.pheyma.com/sps-mobile/index.html#/mine";
return Template(fromUserName, toUserName, title, description, picUrl, url);
}
private string Template(string fromUserName, string toUserName, string title, string description, string picUrl, string url)
{
string xml = "<xml><ToUserName><![CDATA[" + fromUserName + "]]></ToUserName><FromUserName><![CDATA[" + toUserName + "]]></FromUserName>";
xml += "<CreateTime>" + DateTimeToUnixTimestamp(DateTime.Now) + "</CreateTime>";
xml += "<MsgType><![CDATA[news]]></MsgType><Content><![CDATA[]]></Content><ArticleCount>1</ArticleCount><Articles>";
xml += "<item><Title><![CDATA[" + title + "]]></Title><Description><![CDATA[" + description + "]]></Description><PicUrl><![CDATA[" + picUrl + "]]></PicUrl><Url><![CDATA[" + url + "]]></Url></item>";
xml += "</Articles><FuncFlag>0</FuncFlag></xml>";
return xml;
}
private long DateTimeToUnixTimestamp(DateTime dateTime)
{
var start = new DateTime(1970, 1, 1, 0, 0, 0, dateTime.Kind);
return Convert.ToInt64((dateTime - start).TotalSeconds);
}
[HttpGet]
public ActionResult WechatValidation(string echoStr, string signature, string timestamp, string nonce)
{
string msg = $"\r\nTime:{DateTime.Now},string echoStr:{echoStr},string signature:{signature},string timestamp:{timestamp},string nonce:{nonce}";
LoggerHelper.Warn(msg);
var token = "wechat";
var checkResult = CheckSignature(token, signature, timestamp, nonce);
return Content(echoStr);
}
private bool CheckSignature(string token, string signature, string timestamp, string nonce)
{
//创建数组,将 Token, timestamp, nonce 三个参数加入数组
string[] array = { token, timestamp, nonce };
//进行排序
Array.Sort(array);
//拼接为一个字符串
var tempStr = String.Join("", array);
//对字符串进行 SHA1加密
tempStr = Get_SHA1_Method2(tempStr);
//判断signature 是否正确
if (tempStr.Equals(signature))
{
return true;
}
else
{
return false;
}
}
private string Get_SHA1_Method2(string strSource)
{
string strResult = "";
//Create
System.Security.Cryptography.SHA1 md5 = System.Security.Cryptography.SHA1.Create();
//注意编码UTF8、UTF7、Unicode等的选择
byte[] bytResult = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(strSource));
//字节类型的数组转换为字符串
for (int i = 0; i < bytResult.Length; i++)
{
//16进制转换
strResult = strResult + bytResult[i].ToString("X");
}
return strResult.ToLower();
}
/// <summary>
/// 生成签名
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
[NonAction]
private string MakeSign(params string[] args)
{
//字典排序
Array.Sort(args);
string tmpStr = string.Join("", args);
//字符加密
var sha1 = EncryptHelper.Sha1Encrypt(tmpStr);
return sha1;
}
/// <summary>
/// 生成消息签名
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
[NonAction]
private string MakeMsgSign(params string[] args)
{
//字典排序
Array.Sort(args, new CharSort());
string tmpStr = string.Join("", args);
//字符加密
var sha1 = EncryptHelper.Sha1Encrypt(tmpStr);
return sha1;
}
/// <summary>
/// 微信回调统一接口
/// </summary>
/// <returns></returns>
[HttpGet, HttpPost]
public string ReplyService()
{
//获取配置文件中的数据
var token = "************************";
var encodingAESKey = "************************";
var appId = "************************";
bool isGet = string.Equals(HttpContext.Request.Method, HttpMethod.Get.Method, StringComparison.OrdinalIgnoreCase);
bool isPost = string.Equals(HttpContext.Request.Method, HttpMethod.Post.Method, StringComparison.OrdinalIgnoreCase);
if (!isGet && !isPost)
{
return "";
}
bool isEncrypt = false;
try
{
var query = HttpContext.Request.QueryString.ToString();
string msg_signature = "", nonce = "", timestamp = "", encrypt_type = "", signature = "", echostr = "";
var context = _accessor.HttpContext;
if (!string.IsNullOrEmpty(query))//需要验证签名
{
var collection = HttpUtility.ParseQueryString(query);
StringBuilder sb = new StringBuilder();
for (int i=0;i<collection.Count;i++) {
sb.Append(collection.GetKey(i));
sb.Append("=");
string[] ss = collection.GetValues(i);
sb.AppendLine(String.Join(",", ss));
}
LoggerHelper.Warn("1、微信消息回复");
LoggerHelper.Warn(sb.ToString());
msg_signature = collection["msg_signature"]?.Trim();
nonce = collection["nonce"]?.Trim();
timestamp = collection["timestamp"]?.Trim();
encrypt_type = collection["encrypt_type"]?.Trim();
signature = collection["signature"]?.Trim();
echostr = collection["echostr"]?.Trim();
LoggerHelper.Warn($@"0、微信消息回复,msg_signature={msg_signature},nonce={nonce},timestamp={timestamp},encrypt_type={encrypt_type},signature={signature},echostr={echostr},");
if (!string.IsNullOrEmpty(encrypt_type))//有使用加密
{
if (!string.Equals(encrypt_type, "aes", StringComparison.OrdinalIgnoreCase))//只支持AES加密方式
{
return "";
}
isEncrypt = true;
}
}
//先验证签名
if (!string.IsNullOrEmpty(signature))
{
//字符加密
var sha1 = MakeSign(nonce, timestamp, token);
if (!sha1.Equals(signature, StringComparison.OrdinalIgnoreCase))//验证不通过
{
LoggerHelper.Warn("2、微信消息回复 验证不通过");
return "";
}
if (isGet)//是否Get请求,如果true,那么就认为是修改服务器回调配置信息
{
LoggerHelper.Warn("2、微信消息回复 是否Get请求,如果true,那么就认为是修改服务器回调配置信息");
return echostr;
}
}
else
{
LoggerHelper.Warn("2、微信消息回复 没有签名,请求直接返回");
return "";//没有签名,请求直接返回
}
var body = new StreamReader(HttpContext.Request.Body).ReadToEndAsync().Result;
LoggerHelper.Warn("3、微信消息回复");
LoggerHelper.Warn(body);
RstWeixinReplyBodyDto etybody = new RstWeixinReplyBodyDto();
if (isEncrypt)
{
XDocument doc = XDocument.Parse(body);
var encrypt = doc.Element("xml").Element("Encrypt");
//验证消息签名
if (!string.IsNullOrEmpty(msg_signature))
{
//消息加密
var sha1 = MakeMsgSign(nonce, timestamp, encrypt.Value, token);
if (!sha1.Equals(msg_signature, StringComparison.OrdinalIgnoreCase))//验证不通过
{
LoggerHelper.Warn("3、微信消息回复 验证不通过");
return "";
}
}
body = EncryptHelper.AESDecrypt(encrypt.Value, encodingAESKey);//解密
body = body.Replace(" ", "");
LoggerHelper.Warn("3、微信消息回复 解密:" + body);
doc = XDocument.Parse(body);
etybody.ToUserName = doc.Element("xml").Element("ToUserName").Value;
etybody.MsgType = doc.Element("xml").Element("MsgType").Value;
etybody.FromUserName = doc.Element("xml").Element("FromUserName").Value;
etybody.Content = doc.Element("xml").Element("Content").Value;
etybody.CreateTime = doc.Element("xml").Element("CreateTime").Value;
etybody.MsgId = doc.Element("xml").Element("MsgId").Value;
//return body;
}
else {
XDocument doc = XDocument.Parse(body);
etybody.ToUserName = doc.Element("xml").Element("ToUserName").Value;
etybody.MsgType = doc.Element("xml").Element("MsgType").Value;
etybody.FromUserName = doc.Element("xml").Element("FromUserName").Value;
etybody.Content = doc.Element("xml").Element("Content").Value;
etybody.CreateTime = doc.Element("xml").Element("CreateTime").Value;
etybody.MsgId = doc.Element("xml").Element("MsgId").Value;
}
if (!string.IsNullOrEmpty(body))
{
//
//在这里根据body中的MsgType和Even来区分消息,然后来处理不同的业务逻辑
//
//
//result是上面逻辑处理完成之后的待返回结果,如返回文本消息:
string result = $@"<xml><ToUserName><![CDATA[{etybody.FromUserName}]]></ToUserName><FromUserName><![CDATA[{etybody.ToUserName}]]></FromUserName><CreateTime>{etybody.CreateTime}</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[浩汉智能自动回复测试]]></Content><MsgId><![CDATA[{etybody.MsgId}]]></MsgId></xml>";
result = result.Replace(" ","");
if (!string.IsNullOrEmpty(result))
{
//if (isEncrypt)
//{
// result = EncryptHelper.AESEncrypt(result, encodingAESKey, appId);
// var _msg_signature = MakeMsgSign(nonce, timestamp, result, token);
// result = $@"<xml>
// <Encrypt><![CDATA[{result}]]></Encrypt>
// <MsgSignature>{_msg_signature}</MsgSignature>
// <TimeStamp>{timestamp}</TimeStamp>
// <Nonce>{nonce}</Nonce>
// </xml>";
//}
//WriteResponse(result);
LoggerHelper.Warn("5、微信消息回复 " + result);
//WriteResponse(result);
HttpContext.Response.WriteAsync(result);
return "success";
}
//如果这里我们的处理逻辑需要花费较长时间,可以这里先返回空(""),然后使用异步去处理业务逻辑,
//异步处理完后,调用微信的客服消息接口通知微信服务器
}
}
catch (Exception ex)
{
//记录异常日志
LoggerHelper.Error(ex,"6、微信消息回复 异常退出" );
}
LoggerHelper.Warn("10、终极返回,没有任何数据 " );
return "";
}
}
}