wxacode.getUnlimited
- 安装 WebApiClient.JIT 和 Microsoft.AspNetCore.Http
Install-Package WebApiClient.JIT -Version 1.0.8 Install-Package Microsoft.AspNetCore.Http -Version 2.1.1
- IAuthApi.cs
/// <summary> /// 权限API /// </summary> public interface IAuthApi : IHttpApi { /// <summary> /// 获取小程序全局唯一后台接口调用凭据(access_token) /// </summary> /// <param name="request"></param> /// <returns></returns> [HttpGet("https://api.weixin.qq.com/cgi-bin/token")] ITask<GetAccessTokenResponse> GetAccessToken(GetAccessTokenRequest request); } /// <summary> /// 获取小程序全局唯一后台接口调用凭据 参数 /// </summary> public class GetAccessTokenRequest { /// <summary> /// 填写 client_credential /// </summary> [AliasAs("grant_type")] public string GrantType { get; set; } = "client_credential"; /// <summary> /// 小程序唯一凭证,即 AppID /// </summary> [AliasAs("appid")] public string AppId { get; set; } /// <summary> /// 小程序唯一凭证密钥,即 AppSecret /// </summary> [AliasAs("secret")] public string Secret { get; set; } } /// <summary> /// 获取小程序全局唯一后台接口调用凭据 返回值 /// </summary> public class GetAccessTokenResponse : WXBaseResponse { /// <summary> /// 接口调用凭证 /// </summary> [AliasAs("access_token")] public string AccessToken { get; set; } /// <summary> /// 凭证有效时间,单位:秒。目前是7200秒之内的值。 /// </summary> [AliasAs("expires_in")] public int ExpiresIn { get; set; } } /// <summary> /// 微信基础返回信息 /// </summary> public class WXBaseResponse { /// <summary> /// 错误码: /// -1 : 系统繁忙,此时请开发者稍候再试; /// 0 : 请求成功; /// ... ... /// </summary> [AliasAs("errcode")] public int ErrCode { get; set; } /// <summary> /// 错误信息 /// </summary> [AliasAs("errmsg")] public string ErrMsg { get; set; } }
- IWxacodeApi.cs
因为 wxacode.getUnlimited 返回的类型可能是 Json 也可能是图片,所以这里返回值类型为
HttpResponseMessage
。/// <summary> /// 小程序码API /// </summary> public interface IWxacodeApi : IHttpApi { /// <summary> /// 获取小程序码,适用于需要的码数量极多的业务场景。通过该接口生成的小程序码,永久有效,数量暂无限制。 /// </summary> /// <param name="request"></param> /// <param name="access_token"></param> /// <returns></returns> [HttpPost("https://api.weixin.qq.com/wxa/getwxacodeunlimit")] ITask<HttpResponseMessage> GetUnlimited([JsonContent] GetUnlimitedRequest request, [PathQuery] string access_token); } /// <summary> /// 获取小程序码 参数 /// </summary> public class GetUnlimitedRequest { /// <summary> /// 参数 : 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) /// </summary> [AliasAs("scene")] public string Scene { get; set; } /// <summary> /// 主页 : 必须是已经发布的小程序存在的页面(否则报错),例如 pages/index/index, 根路径前不要填加 /,不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面 /// </summary> [AliasAs("page")] public string Page { get; set; } /// <summary> /// 二维码的宽度,单位 px,最小 280px,最大 1280px /// </summary> [AliasAs("width")] public int width { get; set; } = 430; /// <summary> /// 接口调用凭证 /// </summary> [AliasAs("auto_color")] public bool AutoColor { get; set; } = false; /// <summary> /// auto_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} 十进制表示 /// </summary> [AliasAs("line_color")] public RGBColor LineColor { get; set; } /// <summary> /// 是否需要透明底色,为 true 时,生成透明底色的小程序 /// </summary> [AliasAs("is_hyaline")] public bool IsHyaline { get; set; } = false; } /// <summary> /// RGB /// </summary> public class RGBColor { /// <summary> /// R /// </summary> [AliasAs("r")] public int R { get; set; } /// <summary> /// G /// </summary> [AliasAs("g")] public int G { get; set; } /// <summary> /// B /// </summary> [AliasAs("b")] public int B { get; set; } }
- WXApiFactory.cs
/// <summary> /// 微信API工厂类 /// </summary> public class WXApiFactory { /// <summary> /// 注册API /// </summary> public static void Register() { // 权限API HttpApi.Register<IAuthApi>().ConfigureHttpApiConfig(c => { }); // 小程序码API HttpApi.Register<IWxacodeApi>().ConfigureHttpApiConfig(c => { }); } }
- ESSearchApiExtension.cs
/// <summary> /// 微信 Api 扩展类 /// </summary> public static class ESSearchApiExtension { /// <summary> /// 使用微信Api /// </summary> /// <param name="app"></param> public static void UseWXApi(this IApplicationBuilder app) { WXApiFactory.Register(); } }
- Startup.cs
/// <summary> /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. /// </summary> /// <param name="app"></param> /// <param name="env"></param> public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // ... app.UseMvc(); // ... // 使用微信API app.UseWXApi(); }
- WXAccessTokenUtils.cs
/// <summary> /// 微信接口调用凭证通用方法 /// </summary> public class WXAccessTokenUtils { /// <summary> /// 刷新接口调用凭证 /// </summary> /// <param name="appId"></param> public static async Task<GetAccessTokenResponse> RefreshAccessTokenAsync(string appId) { var response = await HttpApi.Resolve<IAuthApi>().GetAccessToken(new GetAccessTokenRequest() { AppId = appId, Secret = ZConfig.GetConfigString(ApolloConfigKey.AppSecret), }); if (response.ErrCode == 0) { // 将接口调用凭据保存到缓存 RedisUtil.Set($"{CacheKey.WXAccessTokenPrefix}_{appId}", new AccessTokenCacheModel() { AccessToken = response.AccessToken, ExpiresTime = DateTime.Now.AddSeconds(response.ExpiresIn), }, response.ExpiresIn / 60); } return response; } /// <summary> /// 获取接口调用凭证 /// </summary> /// <param name="appId"></param> /// <returns></returns> public static async Task<string> GetAccessTokenAsync(string appId) { var cacheKey = $"{CacheKey.WXAccessTokenPrefix}_{appId}"; // 将接口调用凭据保存到缓存 var cachedAccessToken = RedisUtil.Get<AccessTokenCacheModel>(cacheKey); if (cachedAccessToken == null || cachedAccessToken.ExpiresTime < DateTime.Now) { // 没有缓存或凭证已过期时,刷新凭证 var response = await RefreshAccessTokenAsync(appId); if (response != null && response.ErrCode == 0) { return response.AccessToken; } else { return string.Empty; } } else if(cachedAccessToken.ExpiresTime.AddMinutes(-5) < DateTime.Now) { // 即将过期时重新获取凭证 var response = await RefreshAccessTokenAsync(appId); if (response != null && response.ErrCode != 0) { return response.AccessToken; } else { return cachedAccessToken.AccessToken; } } return cachedAccessToken.AccessToken; } }
- QRCodeController.cs
失败时返回的是 Json 字符串,成功时返回的是图片的 buffer。
这里通过返回消息的 ContentType 来判断是哪种类型。/// <summary> /// 获取小程序码 /// </summary> /// <param name="request"></param> /// <returns></returns> [HttpPost("wxacode-unlimited")] public async Task<Result> GetWXACodeUnlimited(GetUnlimitedRequest request) { var appId = ZConfig.GetConfigString(ApolloConfigKey.AppID); var accessToken = await WXAccessTokenUtils.GetAccessTokenAsync(appId); var responseMessage = await HttpApi.Resolve<IWxacodeApi>().GetUnlimited(request, accessToken); if (responseMessage == null || responseMessage.StatusCode != System.Net.HttpStatusCode.OK) { return new Result() { IsSuccess = false, Msg = "访问微信API异常", }; } if (responseMessage.Content.Headers.ContentType.MediaType == "image/jpeg") { return new Result() { IsSuccess = true, Msg = Convert.ToBase64String(await responseMessage.Content.ReadAsByteArrayAsync()), }; } else if (responseMessage.Content.Headers.ContentType.MediaType == "application/json") { var response = JsonHelper.DeserializeObject<WXBaseResponse>(await responseMessage.Content.ReadAsStringAsync()); return new Result() { IsSuccess = false, Msg = response.ErrMsg, }; } return new Result() { IsSuccess = false, Msg = "未处理的二维码类型,请联系客服。", }; } /// <summary> /// 获取小程序码图片 /// </summary> /// <param name="request"></param> /// <returns></returns> [HttpPost("wxacode-unlimited-img")] public async Task GetWXACodeUnlimitedImage(GetUnlimitedRequest request) { var appId = ZConfig.GetConfigString(ApolloConfigKey.ZYXY_AppID); var accessToken = await WXAccessTokenUtils.GetAccessTokenAsync(appId); var responseMessage = await HttpApi.Resolve<IWxacodeApi>().GetUnlimited(request, accessToken); if (responseMessage != null && responseMessage.StatusCode == HttpStatusCode.OK && responseMessage.Content.Headers.ContentType.MediaType == "image/jpeg") { Response.Clear(); Response.ContentType = responseMessage.Content.Headers.ContentType.MediaType; await Response.Body.WriteAsync(await responseMessage.Content.ReadAsByteArrayAsync()); return; } Response.StatusCode = (int)HttpStatusCode.InternalServerError; }
- QRCode.wxml
<image wx:if="{{result.isSuccess}}" src='data:image/jpeg;base64,{{result.msg}}' />