Asp .NetCore 微信小程序获取AccessToken、订阅消息_.net core调取微信 api 需要使用 access_token-程序员宅基地

技术标签: C#  c#  微信小程序  asp.net  

微信开放文档 https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.addTemplate.html

环境
  • .Net Core 6.0
工具
  • Visual Studio 2022
  • 微信开发者工具
效果

至于样式效果取决于模板选择

image

流程

可能大概也许流程是这样子

获取OpenId
获取asset_token
获取小程序账号的类目
获取当前帐号下的个人模板列表
获取帐号所属类目下的公共模板标题
获取模板标题下的关键词列表
组合模板并添加至帐号下的个人模板库
调起客户端小程序订阅消息界面
发送订阅消息
删除帐号下的个人模板
新建项目

我这里是这样新建的,当然也可以使用Visual Studio中的可视化新建。

dotnet new webapi --name AspNetCoreWeChatSubscribeMessage
添加依赖

编辑.csproj文件。

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.4" />
  </ItemGroup>

</Project>
新增WeChat类进行封装

新建WeChat.cs文件,根据上一篇Asp .NetCore 微信小程序授权登录、获取用户信息进行改进。

using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;

namespace AspNetCoreWeChatSubscribeMessage
{
    

    public class WeChat
    {
    
        /// <summary>
        /// 小程序 appId
        /// </summary>
        public string appid {
     get; set; }
        /// <summary>
        /// 小程序 appSecret
        /// </summary>
        public string secret {
     get; set; }
        public class Code2SessionResult
        {
    
            /// <summary>
            /// 用户唯一标识
            /// </summary>
            public string? openid {
     get; set; }
            /// <summary>
            /// 会话密钥
            /// </summary>
            public string? session_key {
     get; set; }

            /// <summary>
            /// 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回,详见 UnionID 机制说明。
            /// </summary>
            public string? unionid {
     get; set; }
            /// <summary>
            /// 错误码
            /// </summary>
            public int errcode {
     get; set; }
            /// <summary>
            ///  错误信息
            /// </summary>
            public string? errmsg {
     get; set; }
        }
        public enum Gender
        {
    
            unkown = 0,
            man = 1,
            woman = 2
        }
        public class UserInfo
        {
    
            public string? openId {
     get; set; }
            /// <summary>
            /// 用户昵称
            /// </summary>
            public string? nickName {
     get; set; }
            /// <summary>
            /// 用户性别
            /// </summary>
            public Gender? gender {
     get; set; }
            /// <summary>
            /// 用户所在国家
            /// </summary>
            public string? country {
     get; set; }
            /// <summary>
            /// 用户所在省份。
            /// </summary>
            public string? province {
     get; set; }
            /// <summary>
            /// 用户所在城市。
            /// </summary>
            public string? city {
     get; set; }
            /// <summary>
            /// 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回,详见 UnionID 机制说明。
            /// </summary>
            public string? unionId {
     get; set; }
            /// <summary>
            /// 用户头像图片的 URL。URL 最后一个数值代表正方形头像大小(有 0、46、64、96、132 数值可选,0 代表 640x640 的正方形头像,46 表示 46x46 的正方形头像,剩余数值以此类推。默认132),用户没有头像时该项为空。若用户更换头像,原有头像 URL 将失效。
            /// </summary>
            public string? avatarUrl {
     get; set; }
            /// <summary>
            /// 
            /// </summary>
            public Watermark? watermark {
     get; set; }
        }
        public class Watermark
        {
    
            /// <summary>
            /// 敏感数据归属 appId,开发者可校验此参数与自身 appId 是否一致
            /// </summary>
            public string? appid {
     get; set; }
            /// <summary>
            /// 敏感数据获取的时间戳, 开发者可以用于数据时效性校验
            /// </summary>
            public string? timestamp {
     get; set; }
        }
        /// <summary>
        /// 小程序类别
        /// </summary>


        public class GetAccessTokenResult
        {
    
            /// <summary>
            /// 获取到的凭证
            /// </summary>
            public string? access_token {
     get; set; }
            /// <summary>
            /// 凭证有效时间,单位:秒。目前是7200秒之内的值。
            /// </summary> 
            public long expires_in {
     get; set; }
            /// <summary>
            /// 错误码
            /// </summary>
            public long errcode {
     get; set; }
            /// <summary>
            /// 错误信息
            /// </summary>
            public string? errmsg {
     get; set; }
        }


        public class GetCategoryDataResult
        {
    

            /// <summary>
            /// 类目id,查询公共库模版时需要
            /// </summary>
            public long id {
     get; set; }
            /// <summary>
            /// 类目的中文名
            /// </summary>
            public string? name {
     get; set; }
        }
        public class GetCategoryResult
        {
    
            /// <summary>
            /// 错误码
            /// </summary>
            public long errcode {
     get; set; }
            /// <summary>
            /// 错误信息
            /// </summary>
            public string? errmsg {
     get; set; }
            /// <summary>
            /// 类目列表
            /// </summary>
            [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
            public List<GetCategoryDataResult>? data {
     get; set; }
        }
        public class GetTemplateListResult
        {
    
            /// <summary>
            /// 错误码
            /// </summary>
            public long errcode {
     get; set; }
            /// <summary>
            /// 错误信息
            /// </summary>
            public string? errmsg {
     get; set; }
            /// <summary>
            /// 个人模板列表
            /// </summary>
            public List<GetTemplateListDataResult>? data {
     get; set; }
        }
        public class GetTemplateListDataResult
        {
    
            /// <summary>
            /// 添加至帐号下的模板 id,发送小程序订阅消息时所需
            /// </summary>
            public string? priTmplId {
     get; set; }
            /// <summary>
            /// 模版标题
            /// </summary>
            public string? title {
     get; set; }
            /// <summary>
            /// 模版内容
            /// </summary>
            public string? content {
     get; set; }
            /// <summary>
            /// 模板内容示例
            /// </summary>
            public string? example {
     get; set; }
            /// <summary>
            /// 模版类型,2 为一次性订阅,3 为长期订阅
            /// </summary>
            public long type {
     get; set; }
            /// <summary>
            /// 枚举参数值范围
            /// </summary>
            public List<KeywordEnumValueResult>? keywordEnumValueList {
     get; set; }
        }
        public class KeywordEnumValueResult
        {
    

            /// <summary>
            /// 枚举参数的 key
            /// </summary>
            public string? keywordCode {
     get; set; }
            /// <summary>
            /// 枚举参数值范围列表
            /// </summary>
            public List<string>? enumValueList {
     get; set; }
        }

        public class GetPubTemplateTitleListDataResult
        {
    
            /// <summary>
            /// 模版标题 id
            /// </summary>
            public long tid {
     get; set; }
            /// <summary>
            /// 模版标题
            /// </summary>
            public string? title {
     get; set; }
            /// <summary>
            ///  模版类型,2 为一次性订阅,3 为长期订阅
            /// </summary>
            public long type {
     get; set; }
            /// <summary>
            /// 模版所属类目 id
            /// </summary>
            public long categoryId {
     get; set; }
        }
        public class GetPubTemplateTitleListResult
        {
    
            /// <summary>
            /// 错误码
            /// </summary>
            public long errcode {
     get; set; }
            /// <summary>
            /// 错误信息
            /// </summary>
            public string? errmsg {
     get; set; }
            /// <summary>
            /// 模版标题列表总数
            /// </summary>
            public long count {
     get; set; }
            /// <summary>
            /// 模板标题列表
            /// </summary>
            public List<GetPubTemplateTitleListDataResult>? data {
     get; set; }
        }

        public class GetPubTemplateKeyWordsByIdDataResult
        {
    
            /// <summary>
            ///  关键词 id,选用模板时需要
            /// </summary>
            public long kid {
     get; set; }
            /// <summary>
            ///  关键词内容
            /// </summary>
            public string? name {
     get; set; }
            /// <summary>
            ///  关键词内容对应的示例
            /// </summary>
            public string? example {
     get; set; }
            /// <summary>
            /// 参数类型
            /// </summary>
            public string? rule {
     get; set; }
        }
        public class GetPubTemplateKeyWordsByIdResult
        {
    
            /// <summary>
            /// 错误码
            /// </summary>
            public long errCode {
     get; set; }
            /// <summary>
            /// 错误信息
            /// </summary>
            public string? errMsg {
     get; set; }
            /// <summary>
            /// 模版标题列表总数
            /// </summary>
            public long count {
     get; set; }
            /// <summary>
            /// 关键词列表
            /// </summary>
            public List<GetPubTemplateKeyWordsByIdDataResult>? data {
     get; set; }
        }
        public class AddTemplateParamter
        {
    
            /// <summary>
            /// 模板标题 id,可通过接口获取,也可登录小程序后台查看获取
            /// </summary>
            public string? tid {
     get; set; }
            /// <summary>
            /// 开发者自行组合好的模板关键词列表,关键词顺序可以自由搭配(例如[3, 5, 4] 或[4, 5, 3]),最多支持5个,最少2个关键词组合
            /// </summary>
            public List<long>? kidList {
     get; set; }
            /// <summary>
            /// 服务场景描述,15个字以内
            /// </summary>
            public string? sceneDesc {
     get; set; }
        }
        public class AddTemplateResult
        {
    

            /// <summary>
            /// 错误码
            /// 200014	模版 tid 参数错误	
            /// 200020	关键词列表 kidList 参数错误	
            /// 200021	场景描述 sceneDesc 参数错误	
            /// 200011	此账号已被封禁,无法操作	
            /// 200013	此模版已被封禁,无法选用	
            /// 200012	个人模版数已达上限,上限25个
            /// </summary>
            public long errCode {
     get; set; }
            /// <summary>
            /// 错误信息
            /// </summary>
            public string? errMsg {
     get; set; }
        }

        public class DeleteTemplateResult
        {
    
            /// <summary>
            /// 错误码
            /// </summary>
            public long errCode {
     get; set; }
            /// <summary>
            /// 错误信息
            /// </summary>
            public string? errMsg {
     get; set; }
        }

        public class DeleteTemplateParamter
        {
    
            /// <summary>
            /// 要删除的模板id
            /// </summary>
            public string priTmplId {
     get; set; }
            public DeleteTemplateParamter(string priTmplId)
            {
    
                this.priTmplId = priTmplId;
            }
        }

        public class SubscribeMessageSendParamter
        {
    
            /// <summary>
            ///  接收者(用户)的 openid
            /// </summary>
            public string? touser {
     get; set; }
            /// <summary>
            /// 所需下发的订阅模板id
            /// </summary>
            public string? template_id {
     get; set; }
            /// <summary>
            /// 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index? foo = bar)。该字段不填则模板无跳转。
            /// </summary>
            public string? page {
     get; set; }
            /// <summary>
            /// 模板内容,格式形如 { "key1": { "value": any }, "key2": { "value": any } }
            /// </summary>
            public object? data {
     get; set; }
            /// <summary>
            /// 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
            /// </summary>
            public string? miniprogram_state {
     get; set; }
            /// <summary>
            ///  进入小程序查看”的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN
            /// </summary>
            public string? lang {
     get; set; }
        }

        public class SubscribeMessageSendResult
        {
    
            /// <summary>
            /// 错误码
            /// </summary>
            public long errCode {
     get; set; }
            /// <summary>
            /// 错误信息
            /// </summary>
            public string? errMsg {
     get; set; }
        }

        public WeChat(string appid, string secret)
        {
    
            this.appid = appid;
            this.secret = secret;
        }
        /// <summary>
        /// 登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。更多使用方法详见 小程序登录。
        /// https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
        /// </summary>
        /// <param name="js_code">登录时获取的 code</param>
        /// <param name="grant_type">授权类型,此处只需填写 authorization_code</param>
        /// <returns></returns>
        public async Task<Code2SessionResult?> GetCode2Session(string js_code, string? grant_type = "authorization_code")
        {
    
            var result = await Get($"https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={js_code}&grant_type={grant_type}");
            return JsonConvert.DeserializeObject<Code2SessionResult>(result);
        }
        /// <summary>
        /// 解密获取用户信息 (不验证签名)
        /// </summary>
        /// <param name="iv">加密算法的初始向量</param>
        /// <param name="encryptedData">包括敏感数据在内的完整用户信息的加密数据</param>
        /// <param name="session_key">会话密钥</param> 
        /// <returns></returns>
        public UserInfo? GetUserInfo(string iv, string encryptedData, string session_key)
        {
    
            return JsonConvert.DeserializeObject<UserInfo>(AESDecrypt(encryptedData, session_key, iv));
        }
        /// <summary>
        /// 解密获取用户信息 (验证签名)
        /// </summary>
        /// <param name="iv">加密算法的初始向量</param>
        /// <param name="encryptedData">包括敏感数据在内的完整用户信息的加密数据</param>
        /// <param name="session_key">会话密钥</param>
        /// <param name="rawData">不包括敏感信息的原始数据字符串,用于计算签名</param>
        /// <param name="signature">使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息</param>
        /// <returns></returns>
        public UserInfo? GetUserInfo(string iv, string encryptedData, string session_key, string rawData, string signature)
        {
    
            CheckSignature(rawData, session_key, signature);
            return GetUserInfo(iv, encryptedData, session_key);
        }


        /// <summary>
        /// 获取小程序全局唯一后台接口调用凭据(access_token)。调用绝大多数后台接口时都需使用 access_token,开发者需要进行妥善保存。 如使用云开发,可通过云调用免维护 access_token 调用。
        /// </summary>
        /// <param name="grant_type">填写 client_credential</param>
        /// <returns></returns>
        public async Task<GetAccessTokenResult?> GetAccessToken(string grant_type = "client_credential")
        {
    
            var result = await Get($"https://api.weixin.qq.com/cgi-bin/token?grant_type={grant_type}&appid={appid}&secret={secret}");
            return JsonConvert.DeserializeObject<GetAccessTokenResult>(result);
        }
        /// <summary>
        /// 获取小程序账号的类目
        /// </summary>
        /// <param name="access_token">接口调用凭证</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public async Task<GetCategoryResult?> GetCategory(string access_token)
        {
    
            var result = await Get($"https://api.weixin.qq.com/wxaapi/newtmpl/getcategory?access_token={access_token}");
            return JsonConvert.DeserializeObject<GetCategoryResult>(result);
        }
        /// <summary>
        /// 获取帐号所属类目下的公共模板标题
        /// </summary>
        /// <param name="access_token">接口调用凭证</param>
        /// <param name="ids">类目 id,多个用逗号隔开</param>
        /// <param name="start">用于分页,表示从 start 开始。从 0 开始计数。</param>
        /// <param name="limit">用于分页,表示拉取 limit 条记录。最大为 30</param>
        /// <returns></returns>
        public async Task<GetPubTemplateTitleListResult?> GetPubTemplateTitleList(string access_token, string ids, long start = 0, long limit = 30)
        {
    
            var result = await Get($"https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatetitles?access_token={access_token}&ids={ids}&start={start}&limit={limit}");
            return JsonConvert.DeserializeObject<GetPubTemplateTitleListResult>(result);
        }
        /// <summary>
        /// 获取模板标题下的关键词列表
        /// </summary>
        /// <param name="access_token">接口调用凭证</param>
        /// <param name="tid">模板标题 id,可通过接口获取</param>
        /// <returns></returns>
        public async Task<GetPubTemplateKeyWordsByIdResult?> GetPubTemplateKeyWordsById(string access_token, string tid)
        {
    
            var result = await Get($"https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatekeywords?access_token={access_token}&tid={tid}");
            return JsonConvert.DeserializeObject<GetPubTemplateKeyWordsByIdResult>(result);
        }
        /// <summary>
        /// 获取当前帐号下的个人模板列表
        /// </summary>
        /// <param name="access_token">接口调用凭证</param>
        /// <returns></returns>
        public async Task<GetTemplateListResult?> GetTemplateList(string access_token)
        {
    
            var result = await Get($"https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate?access_token={access_token}");
            return JsonConvert.DeserializeObject<GetTemplateListResult>(result);
        }
        /// <summary>
        /// 组合模板并添加至帐号下的个人模板库
        /// </summary>
        /// <param name="access_token">接口调用凭证</param>
        /// <param name="addTemplateParamter"></param>
        /// <returns></returns>
        public async Task<AddTemplateResult?> AddTemplate(string access_token, AddTemplateParamter addTemplateParamter)
        {
    
            var result = await Post($"https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate?access_token={access_token}", addTemplateParamter);
            Console.WriteLine(result);
            return JsonConvert.DeserializeObject<AddTemplateResult>(result);
        }

        /// <summary>
        /// 删除帐号下的个人模板
        /// </summary>
        /// <param name="access_token">接口调用凭证</param>
        /// <param name="priTmplId">要删除的模板id</param>
        /// <returns></returns>
        public async Task<DeleteTemplateResult?> DeleteTemplate(string access_token, string priTmplId)
        {
    
            var result = await Post($"https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate?access_token={access_token}", new DeleteTemplateParamter(priTmplId));
            return JsonConvert.DeserializeObject<DeleteTemplateResult>(result);
        }
        /// <summary>
        /// 发送订阅消息
        /// </summary>
        /// <param name="access_token"></param>
        /// <param name="subscribeMessageSendParamter"></param>
        /// <returns></returns>
        public async Task<SubscribeMessageSendResult?> Send(string access_token, SubscribeMessageSendParamter subscribeMessageSendParamter)
        {
    
            var result = await Post($"https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={access_token}", subscribeMessageSendParamter);
            return JsonConvert.DeserializeObject<SubscribeMessageSendResult>(result);
        }




        /// <summary>
        /// Get 请求
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        static async Task<string> Get(string url)
        {
    
            var httpClient = new HttpClient();
            HttpResponseMessage response = await httpClient.GetAsync(url);
            return await response.Content.ReadAsStringAsync(); ;
        }
        /// <summary>
        /// Post 请求
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        static async Task<string> Post(string url, object? body = null)
        {
    
            var httpClient = new HttpClient();
            Console.WriteLine(JsonConvert.SerializeObject(body));
            var content = new StringContent(JsonConvert.SerializeObject(body));
            content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            HttpResponseMessage response = await httpClient.PostAsync(url, content);
            return await response.Content.ReadAsStringAsync();
        }
        /// <summary>
        /// 检查签名
        /// </summary>
        /// <param name="rawData">不包括敏感信息的原始数据字符串,用于计算签名</param>
        /// <param name="session_key"></param>
        /// <param name="signature">使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息</param>
        /// <exception cref="Exception"></exception>
        static void CheckSignature(string rawData, string session_key, string signature)
        {
    
            Console.WriteLine(SHA1Encryption(rawData + session_key));
            Console.WriteLine(signature);
            if (SHA1Encryption(rawData + session_key).ToUpper() != signature.ToUpper())
            {
    
                throw new Exception("CheckSignature 签名校验失败,数据可能损坏。");
            }
        }
        /// <summary>  
        /// SHA1 加密,返回大写字符串  
        /// </summary>  
        /// <param name="content">需要加密字符串</param>  
        /// <param name="encode">指定加密编码</param>  
        /// <returns>返回40位大写字符串</returns>  
        static string SHA1Encryption(string content, Encoding? encode = null)
        {
    
            try
            {
    
                if (encode == null) encode = Encoding.UTF8;
                SHA1 sha1 = SHA1.Create();
                byte[] bytes_in = encode.GetBytes(content);
                byte[] bytes_out = sha1.ComputeHash(bytes_in);
                sha1.Dispose();
                string result = BitConverter.ToString(bytes_out);
                result = result.Replace("-", "");
                return result;
            }
            catch (Exception ex)
            {
    
                throw new Exception("SHA1Encryption加密出错:" + ex.Message);
            }
        }
        /// <summary>
        /// Aes 解密
        /// </summary>
        /// <param name="encryptedData"></param>
        /// <param name="sessionKey"></param>
        /// <param name="iv"></param>
        /// <returns></returns>
        static string AESDecrypt(string encryptedData, string sessionKey, string iv)
        {
    
            try
            {
    
                var encryptedDataByte = Convert.FromBase64String(encryptedData);
                var aes = Aes.Create();
                aes.Key = Convert.FromBase64String(sessionKey);
                aes.IV = Convert.FromBase64String(iv);
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.PKCS7;
                var transform = aes.CreateDecryptor();
                var plainText = transform.TransformFinalBlock(encryptedDataByte, 0, encryptedDataByte.Length);
                var result = Encoding.Default.GetString(plainText);
                return result;
            }
            catch (Exception ex)
            {
    
                throw new Exception("AESDecrypt解密出错:" + ex.Message);
            }
        }
    }
}

编辑Program文件

修改Program.cs文件。

using AspNetCoreWeChatSubscribeMessage;
var builder = WebApplication.CreateBuilder(args);
//对接口返回的json对象进行全局的处理
builder.Services.AddControllers().AddNewtonsoftJson(options =>
{
    
    // 忽略循环引用
    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    // 为空忽略
    options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
});

builder.Services.AddSingleton<WeChat>(new WeChat("appid", "secret"));

var app = builder.Build();

//app.UseCors();

app.UseAuthorization();

app.MapControllers();

app.Run();

获取appid和secret
  1. 进入微信公众平台 https://mp.weixin.qq.com/ 。

  2. 进入开发设置中获取。

    image

开启订阅消息

左侧菜单点进去很容易就找到了。

当然在这里面定义模板是最好的选择~

image

新增接口进行测试

新建SubscribeMessageController.cs文件放在Controllers文件夹中。

using Microsoft.AspNetCore.Mvc;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace AspNetCoreWeChatSubscribeMessage.Controllers
{
    
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class SubscribeMessageController : ControllerBase
    {
    
        readonly WeChat _weChat;
        static string access_token = "";
        public SubscribeMessageController(WeChat weChat)
        {
    
            _weChat = weChat;
        }

        [HttpGet("{code}")]
        public async Task<IActionResult> GetOpenId(string code)
        {
    
            var res = await _weChat.GetCode2Session(code);
            // 注意: 这里是为了方便演示,开发的时候不应该去传给前端。
            // 为了数据不被篡改,开发者不应该把 session_key 传到小程序客户端等服务器外的环境。
            return new JsonResult(res);
        }
        [HttpGet]
        public async Task<IActionResult> GetAccessToken()
        {
    
            var result = await _weChat.GetAccessToken();
            // 注意: 这里是为了方便演示,开发的时候不应该去传给前端。
            // 建议使用redis,将access_token存储起来
            access_token = result?.access_token ?? "";
            return new JsonResult(result);
        }

        [HttpGet]
        public async Task<IActionResult> GetCategory()
        {
    
            return new JsonResult(await _weChat.GetCategory(access_token));
        }
        [HttpGet]
        public async Task<IActionResult> GetTemplateList()
        {
    
            return new JsonResult(await _weChat.GetTemplateList(access_token));
        }
        [HttpGet]
        public async Task<IActionResult> GetPubTemplateTitleList([FromQuery] string ids, [FromQuery] long start)
        {
    
            return new JsonResult(await _weChat.GetPubTemplateTitleList(access_token, ids, start));
        }
        [HttpGet]
        public async Task<IActionResult> GetPubTemplateKeyWordsById([FromQuery] string tid)
        {
    
            return new JsonResult(await _weChat.GetPubTemplateKeyWordsById(access_token, tid));
        }
        [HttpPost]
        public async Task<IActionResult> AddTemplate([FromBody] WeChat.AddTemplateParamter paramter)
        {
    
            return new JsonResult(await _weChat.AddTemplate(access_token, paramter));
        }
        [HttpDelete]
        public async Task<IActionResult> DeleteTemplate([FromQuery] string priTmplId)
        {
    
            return new JsonResult(await _weChat.DeleteTemplate(access_token, priTmplId));
        }
        [HttpPost]
        public async Task<IActionResult> Send([FromBody] WeChat.SubscribeMessageSendParamter paramter)
        {
    
            return new JsonResult(await _weChat.Send(access_token, paramter));
        }
    }
}
新建小程序

image

新增一个测试页面

样式简陋还请见谅(●’◡’●)~

image

index.wxml

新建n个点击事件,用于触发事件于后端接口或者微信小程序api。

<view bindtap="getOpenId">getOpenId</view>
<view bindtap="getAccessToken">getAccessToken</view>
<view bindtap="getCategory">getCategory</view>
<view bindtap="getTemplateList">getTemplateList</view>
<view bindtap="getPubTemplateTitleList">getPubTemplateTitleList</view>
<view bindtap="getPubTemplateKeyWordsById">getPubTemplateKeyWordsById</view>
<view bindtap="addtemplate">addtemplate</view>
<view bindtap="requestSubscribeMessage">requestSubscribeMessage</view>
<view bindtap="send">send</view>
<view bindtap="deleteTemplate">deleteTemplate</view>

index.ts

Page({
    
  getOpenId() {
    
    wx.login({
    
      success: (res) => {
    
        wx.request({
    
          url: `http://localhost:5000/api/SubscribeMessage/GetOpenId/${
      res.code}`,
          method: "GET",
          success: (res) => {
    
            console.log(res);
          }
        })
      }
    })
  },
  getAccessToken() {
    
    wx.request({
    
      url: `http://localhost:5000/api/SubscribeMessage/GetAccessToken`,
      method: "GET",
      success: (res) => {
    
        console.log(res);
      }
    })
  },
  getCategory() {
    
    wx.request({
    
      url: `http://localhost:5000/api/SubscribeMessage/GetCategory`,
      method: "GET",
      success: (res) => {
    
        console.log(res);
      }
    })
  },
  getTemplateList() {
    
    wx.request({
    
      url: `http://localhost:5000/api/SubscribeMessage/GetTemplateList`,
      method: "GET",
      success: (res) => {
    
        console.log(res);
      }
    })
  },
  getPubTemplateTitleList() {
    
    let ids = '612'
    let start = 600;
    wx.request({
    
      url: `http://localhost:5000/api/SubscribeMessage/GetPubTemplateTitleList?ids=${
      ids}&start=${
      start}`,
      method: "GET",
      success: (res) => {
    
        console.log(res);
      }
    })
  },
  getPubTemplateKeyWordsById() {
    
    let tid = '27400'
    wx.request({
    
      url: `http://localhost:5000/api/SubscribeMessage/GetPubTemplateKeyWordsById?tid=${
      tid}`,
      method: "GET",
      success: (res) => {
    
        console.log(res);
      }
    })
  },
  addtemplate() {
    
    wx.request({
    
      url: `http://localhost:5000/api/SubscribeMessage/AddTemplate`,
      method: "POST",
      data: {
    
        tid: "27400",
        kidList: [1, 3, 2],
        sceneDesc: "点赞"
      },
      success: (res) => {
    
        console.log(res);
      }
    })
  },
  requestSubscribeMessage() {
    
    wx.requestSubscribeMessage({
    
      tmplIds: ['lTSwnFx6q6_H3d12xUir9lzhlC5NAI_2PVUmQ8_kaf0'],
      success: (res) => {
    
        console.log(res)
      }
    })
  },
  send() {
    
    wx.request({
    
      url: `http://localhost:5000/api/SubscribeMessage/Send`,
      method: "POST",
      data: {
    
        touser: "o9TsK49Nuvub76UvhGw-eEsGCLV4",
        template_id: "lTSwnFx6q6_H3d12xUir9lzhlC5NAI_2PVUmQ8_kaf0",
        page: "/pages/test/index",
        data: {
    
          "thing1": {
    
            "value": "啦啦啦啦"
          },
          "thing3": {
    
            "value": "林一怂儿"
          },
          "time2": {
    
            "value": "2022年4月25日"
          }
        },
        miniprogram_state: "developer"
      },
      success: (res) => {
    
        console.log(res);
      }
    })
  },
  deleteTemplate() {
    
    let priTmplId = "lTSwnFx6q6_H3d12xUir9lzhlC5NAI_2PVUmQ8_kaf0";
    wx.request({
    
      url: `http://localhost:5000/api/SubscribeMessage/DeleteTemplate?priTmplId=${
      priTmplId}`,
      method: "DELETE",
      success: (res) => {
    
        console.log(res);
      }
    })
  }
})
操作步骤

只是检查一下接口,返回以及订阅消息有没有发送成功,直接看调试器中的Network啦~

image
操作步骤就是改代码,哈哈哈哈。

其实一般项目中用到的比较多的就是

小程序端

requestSubscribeMessage

服务端

send

记录一下为之后做小程序打一个基础~

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/linyisonger/article/details/124414829

智能推荐

海康威视网络摄像头开发流程(五)------- 直播页面测试_ezuikit 测试的url-程序员宅基地

文章浏览阅读3.8k次。1、将下载好的萤石js插件,添加到SoringBoot项目中。位置可参考下图所示。(容易出错的地方,在将js插件在html页面引入时,发生路径错误的问题)所以如果对页面中引入js的路径不清楚,可参考下图所示存放路径。2、将ezuikit.js引入到demo-live.html中。(可直接将如下代码复制到你创建的html页面中)<!DOCTYPE html><html lan..._ezuikit 测试的url

如何确定组态王与多动能RTU的通信方式_组态王ua-程序员宅基地

文章浏览阅读322次。第二步,在弹出的对话框选择,设备驱动—>PLC—>莫迪康—>ModbusRTU—>COM,根据配置软件选择的协议选期期,这里以此为例,然后点击“下一步”。第四步,把使用虚拟串口打勾(GPRS设备),根据需要选择要生成虚拟口,这里以选择KVCOM1为例,然后点击“下一步”设备ID即Modbus地址(1-255) 使用DTU时,为下485接口上的设备地址。第六步,Modbus的从机地址,与配置软件相同,这里以1为例,点击“下一步“第五步,Modbus的从机地址,与配置软件相同,这里以1为例,点击“下一步“_组态王ua

npm超详细安装(包括配置环境变量)!!!npm安装教程(node.js安装教程)_npm安装配置-程序员宅基地

文章浏览阅读9.4k次,点赞22次,收藏19次。安装npm相当于安装node.js,Node.js已自带npm,安装Node.js时会一起安装,npm的作用就是对Node.js依赖的包进行管理,也可以理解为用来安装/卸载Node.js需要装的东西_npm安装配置

火车头采集器AI伪原创【php源码】-程序员宅基地

文章浏览阅读748次,点赞21次,收藏26次。大家好,小编来为大家解答以下问题,python基础训练100题,python入门100例题,现在让我们一起来看看吧!宝子们还在新手村练级的时候,不单要吸入基础知识,夯实自己的理论基础,还要去实际操作练练手啊!由于文章篇幅限制,不可能将100道题全部呈现在此除了这些,下面还有我整理好的基础入门学习资料,视频和讲解文案都很齐全,用来入门绝对靠谱,需要的自提。保证100%免费这不,贴心的我爆肝给大家整理了这份今天给大家分享100道Python练习题。大家一定要给我三连啊~

Linux Ubuntu 安装 Sublime Text (无法使用 wget 命令,使用安装包下载)_ubuntu 安装sumlime text打不开-程序员宅基地

文章浏览阅读1k次。 为了在 Linux ( Ubuntu) 上安装sublime,一般大家都会选择常见的教程或是 sublime 官网教程,然而在国内这种方法可能失效。为此,需要用安装包安装。以下就是使用官网安装包安装的教程。打开 sublime 官网后,点击右上角 download, 或是直接访问点击打开链接,即可看到各个平台上的安装包。选择 Linux 64 位版并下载。下载后,打开终端,进入安装..._ubuntu 安装sumlime text打不开

CrossOver for Mac 2024无需安装 Windows 即可以在 Mac 上运行游戏 Mac运行exe程序和游戏 CrossOver虚拟机 crossover运行免安装游戏包-程序员宅基地

文章浏览阅读563次,点赞13次,收藏6次。CrossOver24是一款类虚拟机软件,专为macOS和Linux用户设计。它的核心技术是Wine,这是一种在Linux和macOS等非Windows操作系统上运行Windows应用程序的开源软件。通过CrossOver24,用户可以在不购买Windows授权或使用传统虚拟机的情况下,直接在Mac或Linux系统上运行Windows软件和游戏。该软件还提供了丰富的功能,如自动配置、无缝集成和实时传输等,以实现高效的跨平台操作体验。

随便推点

一个用聊天的方式让ChatGPT写的线程安全的环形List_为什么gpt一写list就卡-程序员宅基地

文章浏览阅读1.7k次。一个用聊天的方式让ChatGPT帮我写的线程安全的环形List_为什么gpt一写list就卡

Tomcat自带的设置编码Filter-程序员宅基地

文章浏览阅读336次。我们在前面的文章里曾写过Web应用中乱码产生的原因和处理方式,旧文回顾:深度揭秘乱码问题背后的原因及解决方式其中我们提到可以通过Filter的方式来设置请求和响应的encoding,来解..._filterconfig selectencoding

javascript中encodeURI和decodeURI方法使用介绍_js encodeur decodeurl-程序员宅基地

文章浏览阅读651次。转自:http://www.jb51.net/article/36480.htmencodeURI和decodeURI是成对来使用的,因为浏览器的地址栏有中文字符的话,可以会出现不可预期的错误,所以可以encodeURI把非英文字符转化为英文编码,decodeURI可以用来把字符还原回来_js encodeur decodeurl

Android开发——打包apk遇到The destination folder does not exist or is not writeable-程序员宅基地

文章浏览阅读1.9w次,点赞6次,收藏3次。前言在日常的Android开发当中,我们肯定要打包apk。但是今天我打包的时候遇到一个很奇怪的问题Android The destination folder does not exist or is not writeable,大意是目标文件夹不存在或不可写。出现问题的原因以及解决办法上面有说报错的中文大意是:目标文件夹不存在或不可写。其实问题就在我们的打包界面当中图中标红的Desti..._the destination folder does not exist or is not writeable

Eclipse配置高大上环境-程序员宅基地

文章浏览阅读94次。一、配置代码编辑区的样式 <1>打开Eclipse,Help —> Install NewSoftware,界面如下: <2>点击add...,按下图所示操作: name:随意填写,Location:http://eclipse-color-th..._ecplise高大上设置

Linux安装MySQL-5.6.24-1.linux_glibc2.5.x86_64.rpm-bundle.tar_linux mysql 安装 mysql-5.6.24-1.linux_glibc2.5.x86_6-程序员宅基地

文章浏览阅读2.8k次。一,下载mysql:http://dev.mysql.com/downloads/mysql/; 打开页面之后,在Select Platform:下选择linux Generic,如果没有出现Linux的选项,请换一个浏览器试试。我用的谷歌版本不可以,换一个别的浏览器就行了,如果还是不行,需要换一个翻墙的浏览器。 二,下载完后解压缩并放到安装文件夹下: 1、MySQL-client-5.6.2_linux mysql 安装 mysql-5.6.24-1.linux_glibc2.5.x86_64.rpm-bundle