所以必须拉出来示众
上周我因为要调用微信开放平台的接口,很狠被微信摆了一道,特别生气,必须来吐槽,不然我这消化是好不了的,这世界膈应我的千千万,微信开放平台你算哪根葱。
事件是这样的,用的是 asp.net core 10.0,要调用的接口是微信开放平台的获得加密scheme码的接口。
为了便于演示,这里将部分代码摘录如下。
public record GenerateSchemeResponse(int errcode, string errmsg, string openlink);
app.MapGet(
"/generateScheme",
async () =>
{
var client = new HttpClient();
var response = await client.PostAsJsonAsync(
"https://api.weixin.qq.com/wxa/generatescheme?access_token=access_token",
new
{
is_expire = false,
expire_type = (int?)null,
expire_interval = (int?)null,
expire_time = (long?)null,
jump_wxa = new
{
path = "",
query = "",
env_version = "release"
}
});
response.EnsureSuccessStatusCode();
var msg = await response.Content.ReadFromJsonAsync<GenerateSchemeResponse>();
return msg;
})
.WithName("generateScheme");
呐,你别管这access_token的值为啥就是access_token,这就是个例子晓得伐,实际代码里不是这样。
来,Run it。
System.Net.Http.HttpRequestException: Response status code does not indicate success: 412 (Precondition Failed).
at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
at Program.<>c.<<<Main>$>b__0_0>d.MoveNext() in *********\WebApplication1\Program.cs:line 35
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Http.RequestDelegateFactory.<ExecuteTaskOfT>g__ExecuteAwaited|134_0[T](Task`1 task, HttpContext httpContext, JsonTypeInfo`1 jsonTypeInfo)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
如你所见,必须报错。错误码是412,这是个HTTP错误代码。
那么这个错误代表什么意思呢?不知道。小程序的文档页面里,有一堆错误码列表,但是所有的错误码列表里,就是没有告诉你412错误代码是啥意思的。
并且,如果你抓包的话,你会发现,这个错误的响应极其简单清晰,可以说什么信息都没有。

通常来说,对于API接口的响应异常,我通常会立刻开始反思,是不是自己哪里搞错了。
放到这个例子里,我首先就开始怀疑,是不是参数错了。比如这个AK是不是搞错了?没编码?难道请求参数里的数据没有的话不能用null而是得忽略?
于是我在这里折腾了半个小时,各种换参数,编码,解码,期间还查了百度、逛了CSDN和掘金,并没有什么收获,但是所得到的响应永远是个雷打不动的412,且没有任何其它信息,查到的资料千篇一律,并没有提到这个412错误代码为何方神圣。
把这个请求的所有数据原封不动的复制到Postman中,突然发现Postman可以得到正确的响应:不管如何,起码人家不是412错误代码。
那么事情便很简单了不是,一定是这请求出了问题。

对比了一下Postman和程序的请求,第一感觉是,是不是User-Agent丢失导致的。历史上看到过一些WAF把这个头的缺失当做异常请求来进行拦截。
这个头……倒也是好加。
client.DefaultRequestHeaders.Add("User-Agent", "PostmanRuntime/7.51.0");
好加的东西不一定好用,涛声依旧了。
那……是Accept头导致的问题?……哦……不是。
那……总不会是Cache-Control头导致的问题吧?……哦……也不是。
那是……Connection或Accept-Encoding导致的问题?……嗐……还不是。
删来删去,删掉Transfer-Encoding的时候,突然发现,412失踪了,取而代之的是说AK过期。
嗯??因为Transfer-Encoding: chunked编码导致的问题?……这他喵的不是RFC规范里的吗??
最初定义于 RFC 2616(HTTP/1.1 早期标准,1999 年发布),在 3.6.1 节规定 chunked 传输编码,14.41 节定义 Transfer-Encoding 头字段中的 chunked 取值,要求所有 HTTP/1.1 接收端必须支持该编码。
果然还得是你腾讯,狂到没边。
但话又说回来,这里为什么会有chunked编码?
目光重新转回 PostAsJsonAsync函数,这才发现它并不是HttpClient的标准方法,而是asp.net core中扩展出来的一个方法。我们来看一下这个方法的实现。

它最终生成了一个JsonContent对象并作为请求主体。那么我们再看一下这个对象的内部是怎么工作的。

呐,计算长度的时候直接返回了0,因此请求发出的时候并不知道内容多长,因此也就必须采用分块编码(chunked)。
那如果我们想要避免怎么办呢?那倒也好办。
await client.PostAsync(
"https://api.weixin.qq.com/wxa/generatescheme?access_token=access_token",
new StringContent(JsonSerializer.Serialize(
new
{
is_expire = false,
expire_type = (int?)null,
expire_interval = (int?)null,
expire_time = (long?)null,
jump_wxa = new
{
path = "",
query = "",
env_version = "release"
}
}))
)
为啥我要这样多此一举呢?那还不是因为腾讯狂嘛。
腾讯:你的一小时,我要定了,你看是玩王者荣耀还是伺候微信开放平台,你自己选吧。

