.NET Core 使用 WebApiClient 获取小程序码(wxacode.getUnlimited)
🏷️ .NET Core WebApiClient 微信小程序
1. 安装 WebApiClient.JIT 和 Microsoft.AspNetCore.Http
bash
Install-Package WebApiClient.JIT -Version 1.0.8
Install-Package Microsoft.AspNetCore.Http -Version 2.1.1
2. IAuthApi.cs
csharp
/// <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; }
}
3. IWxacodeApi.cs
因为 wxacode.getUnlimited 返回的类型可能是 Json 也可能是图片,所以这里返回值类型为 HttpResponseMessage
。
csharp
/// <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; }
}
4. WXApiFactory.cs
csharp
/// <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 => { });
}
}
5. ESSearchApiExtension.cs
csharp
/// <summary>
/// 微信 Api 扩展类
/// </summary>
public static class ESSearchApiExtension
{
/// <summary>
/// 使用微信 Api
/// </summary>
/// <param name="app"></param>
public static void UseWXApi(this IApplicationBuilder app)
{
WXApiFactory.Register();
}
}
6. Startup.cs
csharp
/// <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();
}
7. WXAccessTokenUtils.cs
csharp
/// <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;
}
}
8. QRCodeController.cs
失败时返回的是 Json 字符串,成功时返回的是图片的 buffer。
这里通过返回消息的 ContentType 来判断是哪种类型。
csharp
/// <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;
}
9. QRCode.wxml
html
<image wx:if="{{result.isSuccess}}" src='data:image/jpeg;base64,{{result.msg}}' />