百度和必應,微信開發平台,翻爛了,今天終於開發出來了。
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 "";
}
}
}