尚无已经注册的AppId,请先使用AccessTokenContainer.Register完成注册(全局执行一次即可)!模块:WeChat_OfficialAccount

最近一个朋友在咨询我微信公众号推送消息的问题。因为我在17年的时候做了一年的微信公众号开发,自然有一丢丢经验,但是那时的服务端是用Spring Boot 开发的,这次是.net开发,而且时间也有点久了。所以在开发过程中遇到了一些问题。接下来,我就来和盆友们唠嗑唠嗑。

.net开发公众号的各位盆友应该知道Senparc这个神器,其封装了微信公众号/小程序等接口,开箱即用。因为我在15年的时,使用ABP+Senparc开发了一个公众号网页,深感其带来的方便快捷。下面,我们进入正题吧。
因为我不能进入到微信公众号后端,所以朋友就把AppIdAppSecret给我,然后就只有这么搞:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

var isGLobalDebug = true;//设置全局 Debug 状态
var senparcSetting = SenparcSetting.BuildFromWebConfig(isGLobalDebug);
var register = RegisterService.Start(senparcSetting).UseSenparcGlobal();//CO2NET全局注册,必须!

var isWeixinDebug = true;//设置微信 Debug 状态
var senparcWeixinSetting = SenparcWeixinSetting.BuildFromWebConfig(isWeixinDebug);


senparcWeixinSetting.WeixinAppId = appId;
senparcWeixinSetting.WeixinAppSecret = appSecret;


register.UseSenparcWeixin(senparcWeixinSetting, senparcSetting).RegisterMpAccount(senparcWeixinSetting, "【盛派网络小助手】公众号");

//根据appId判断获取
if (!AccessTokenContainer.CheckRegistered(appId)) //检查是否已经注册
{
AccessTokenContainer.RegisterAsync(appId, appSecret); //如果没有注册则进行注册
}
string linkUrl = "http://www.baidu.com"; //点击详情后跳转后的链接地址,为空则不跳转

//为模版中的各属性赋值
var templateData = new
{
first = new TemplateDataItem("您好,您的订单已支付成功!", "#000000"),
product = new TemplateDataItem("旺旺大礼包", "#000000"),
price = new TemplateDataItem("99.8元", "#000000"),
time = new TemplateDataItem("2016-11-09 16:50:38", "#000000"),
remark = new TemplateDataItem("感谢您的光临~", "#000000")
};
string access_token = AccessTokenContainer.GetAccessToken(appId);
SendTemplateMessageResult sendResult = TemplateApi.SendTemplateMessage(access_token, openId, templateId, linkUrl, templateData);

//发送成功
if (sendResult.errcode.ToString() == "请求成功")
{
Response.Write("请求成功");
}
else
{
Response.Write("出现错误:" + sendResult.errmsg);
}
Response.Write("ok");


肯定是我太菜,所以我就百度一番,顺手又改了几行代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var isGLobalDebug = true;//设置全局 Debug 状态
var senparcSetting = SenparcSetting.BuildFromWebConfig(isGLobalDebug);
var register = RegisterService.Start(senparcSetting).UseSenparcGlobal();//CO2NET全局注册,必须!

var isWeixinDebug = true;//设置微信 Debug 状态
var senparcWeixinSetting = SenparcWeixinSetting.BuildFromWebConfig(isWeixinDebug);

register.UseSenparcWeixin(senparcWeixinSetting, senparcSetting);
AccessTokenContainer.RegisterAsync(appId, appSecret); //如果没有注册则进行注册

string linkUrl = "http://www.baidu.com"; //点击详情后跳转后的链接地址,为空则不跳转

//为模版中的各属性赋值
var templateData = new
{
first = new TemplateDataItem("您好,您的订单已支付成功!", "#000000"),
product = new TemplateDataItem("旺旺大礼包", "#000000"),
price = new TemplateDataItem("99.8元", "#000000"),
time = new TemplateDataItem("2016-11-09 16:50:38", "#000000"),
remark = new TemplateDataItem("感谢您的光临~", "#000000")
};
string access_token = AccessTokenContainer.GetAccessToken(appId);
SendTemplateMessageResult sendResult = TemplateApi.SendTemplateMessage(access_token, openId, templateId, linkUrl, templateData);

//发送成功
if (sendResult.errcode.ToString() == "请求成功")
{
Response.Write("请求成功");
}
else
{
Response.Write("出现错误:" + sendResult.errmsg);
}
Response.Write("ok");

但是还是报同样的错误,顿时绝望至极,我到底是又多菜啊?但是这个人虽然菜,可是也不是刚才说的那样脆弱,所以我就去Senparc的github看看。嗯可以看到Senparc非常友好的抛出这个错误,

那么它是什么时候给我们抛出错误呢?这就要看看代码了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// <summary>
/// 获取凭证接口
/// </summary>
/// <param name="grant_type">获取access_token填写client_credential</param>
/// <param name="appid">第三方用户唯一凭证</param>
/// <param name="secret">第三方用户唯一凭证密钥,既appsecret</param>
/// <returns></returns>
[ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.GetToken", true)]
public static AccessTokenResult GetToken(string appid, string secret, string grant_type = "client_credential")
{
//注意:此方法不能再使用ApiHandlerWapper.TryCommonApi(),否则会循环
var url = string.Format(Config.ApiMpHost + "/cgi-bin/token?grant_type={0}&appid={1}&secret={2}",
grant_type.AsUrlData(), appid.AsUrlData(), secret.AsUrlData());

AccessTokenResult result = Get.GetJson<AccessTokenResult>(url);//此处为最原始接口,不再使用重试获取的封装

if (Config.ThrownWhenJsonResultFaild && result.errcode != ReturnCode.请求成功)
{
var unregisterAppIdEx = new UnRegisterAppIdException(null, $"尚无已经注册的AppId,请先使用AccessTokenContainer.Register完成注册(全局执行一次即可)!模块:{NeuChar.PlatformType.WeChat_OfficialAccount}");
throw unregisterAppIdEx;//抛出异常
}

return result;
}

就是说,只要不是成功状态,都是提示尚无已经注册的AppId,请先使用AccessTokenContainer.Register完成注册(全局执行一次即可)!模块:WeChat_OfficialAccount(我能说这样有点坑吗?),那么我猜想应该是调用接口出错了,那么就看看是怎么请求的接口呢?继续看代码吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/// <summary>
/// 获取可用Token
/// </summary>
/// <param name="appId"></param>
/// <param name="getNewToken">是否强制重新获取新的Token</param>
/// <returns></returns>
public static string GetAccessToken(string appId, bool getNewToken = false)
{
return GetAccessTokenResult(appId, getNewToken).access_token;
}

/// <summary>
/// 获取可用AccessTokenResult对象
/// </summary>
/// <param name="appId"></param>
/// <param name="getNewToken">是否强制重新获取新的Token</param>
/// <returns></returns>
public static AccessTokenResult GetAccessTokenResult(string appId, bool getNewToken = false)
{
if (!BaseContainer<AccessTokenBag>.CheckRegistered(appId))
{
throw new UnRegisterAppIdException(appId, $"此appId({appId})尚未注册,请先使用AccessTokenContainer.Register完成注册(全局执行一次即可)!", null);
}
AccessTokenBag accessTokenBag = BaseContainer<AccessTokenBag>.TryGetItem(appId);
using (BaseContainer<AccessTokenBag>.Cache.BeginCacheLock("MP.AccessTokenContainer", appId, 0, default(TimeSpan)))
{
if (getNewToken || accessTokenBag.AccessTokenExpireTime <= SystemTime.Now)
{
accessTokenBag.AccessTokenResult = CommonApi.GetToken(accessTokenBag.AppId, accessTokenBag.AppSecret, "client_credential");
accessTokenBag.AccessTokenExpireTime = ApiUtility.GetExpireTime(accessTokenBag.AccessTokenResult.expires_in);
BaseContainer<AccessTokenBag>.Update(accessTokenBag, null);
}
}
return accessTokenBag.AccessTokenResult;
}

public class CommonApi
{
/// <summary>
/// 获取凭证接口
/// </summary>
/// <param name="grant_type">获取access_token填写client_credential</param>
/// <param name="appid">第三方用户唯一凭证</param>
/// <param name="secret">第三方用户唯一凭证密钥,既appsecret</param>
/// <returns></returns>
[ApiBind(Senparc.NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.GetToken", true)]
public static AccessTokenResult GetToken(string appid, string secret, string grant_type = "client_credential")
{
AccessTokenResult json = Get.GetJson<AccessTokenResult>(string.Format(Config.ApiMpHost + "/cgi-bin/token?grant_type={0}&appid={1}&secret={2}", grant_type.AsUrlData(), appid.AsUrlData(), secret.AsUrlData()), null, null);
if (Config.ThrownWhenJsonResultFaild && json.errcode != 0)
{
throw new UnRegisterAppIdException(null, $"尚无已经注册的AppId,请先使用AccessTokenContainer.Register完成注册(全局执行一次即可)!模块:{Senparc.NeuChar.PlatformType.WeChat_OfficialAccount}", null);
}
return json;
}
}

根据代码我我就拼接了一个http的地址,直接在浏览器访问,得到了这样的结果:

1
{"errcode":40164,"errmsg":"invalid ip 183.221.39.24 ipv6 ::ffff:183.221.39.24, not in whitelist hint: [uBiTIa01561501]"}

一看就知道,请求地址没有加入到白名单里面,所以我让朋友去登录公众平台,
开发->基本配置->IP白名单->查看->修改->将IP地址添加进去,最后,OK了。

You forgot to set the qrcode for Alipay. Please set it in _config.yml.
You forgot to set the qrcode for Wechat. Please set it in _config.yml.
You forgot to set the business and currency_code for Paypal. Please set it in _config.yml.
You forgot to set the url Patreon. Please set it in _config.yml.
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×