为 post SendAsync 设置 HttpRequestMessage.Content (json) 会导致意外字符错误

Setting HttpRequestMessage.Content (json) for post SendAsync results in Unexpected character error

设置:

  1. (服务器).Net Core 2 API

  2. (客户端) 用 C# 编码的 AWS Lambda 函数(设置必须生成 JWT 的方式)。

  3. 使用 JWT 的 IDS3(对于发送请求的方法很重要)

IDS3 和 JWT 部分工作正常。拨打电话并点击 .Net Core 2 API 控制器工作正常。

问题是我收到以下错误:

Unexpected character encountered while parsing value: {. Path '', line 1, position 1.\"]

代码及解释:

StringContent stringContent = new StringContent(eventStr, Encoding.UTF8, "application/json"); 
using (HttpClient client = new HttpClient()) {
    HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, myApiURL);
    requestMessage.Content = stringContent;
    client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", jwtToken);
    HttpResponseMessage thing = client.SendAsync(requestMessage).Result;
    string actualResponse = thing.Content.ReadAsStringAsync().Result;        
}

eventStr 是 AWS 传递给函数处理程序的 json 字符串。

actualResponse 包含错误:Unexpected character encountered while parsing value: {. Path '', line 1, position 1.\"]

我检查了正在生成的 StringContent,它绝对正确。

事情的 API 方面发生的事情是发现 JWT 非常正确,控制器构造函数被触发,操作永远不会被触发,表面上是因为数据有效负载(eventStr)不是'正确附加到 HttpRequestMessage

API 操作的方法签名:

          public async Task<ActionResult> Post([FromBody] string Message)

如果那不是正确的地方,那是什么?

我建议您使用 Flurl 库,它会为您节省很多时间。

因此您可以将代码切换为以下内容(请注意我对某些行进行了注释,您可以根据请求的使用情况删除这些行):

try
{
    //Needed if request sent over Https
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

    //OPTIONAL: set certificates validation, from a static container of global configuration
    Flurl.Configure(settings =>
    {
        settings.HttpClientFactory = new MyHttpClientFactory();
    });

    var result = await myApiUrl
        .WithHeader("Authorization", $"Bearer {jwtToken}")
        .PostJsonAsync(requestMessage)
        .ReceiveString(); //For response as string
        .ReceiveJson<MyClass>(); // to map a json result to an object
}
catch(FlurlHttpException ex)
{
    throw ex.InnerException;
}

使用 MyHttpClientFactory class:

public class MyHttpClientFactoryDev : DefaultHttpClientFactory
{

    public override HttpMessageHandler CreateMessageHandler()
    {   
        //If you want to validate all certs
        return new HttpClientHandler
        {

            ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyError) => true

        };

        //If you need to verify certificate signature
        var handler = new HttpClientHandler();
        handler.ClientCertificates.Add(X509Certificate.CreateFromCertFile("path to cert"));

        return handler;

    }
}

希望你会发现这有用。

问题不在于 json。

所以 AWS lambda 函数被 AWS 发送了一个匿名类型。基本上只是一个快速松散的对象。

由于 POST,我需要通过 StringContent 变量将其作为字符串发送。但是,如果我序列化它,Core API 控制器会说 "Hey, I know this! It's JSON. Let's deserialize it." 并且确实如此。

鉴于操作需要一个字符串,控制器说 "There are no actions that accept this object I have here. This is a Bad Request."

但是,如果我将其作为纯字符串发送(例如,“{key1:value1”} 而不是“{\”key1\”: \”value1\”}”),键周围没有引号和值,控制器和动作说 "HEY! This is a string. I got this."

那么问题是它在那个时候是无效的 json,有点像 JSON。所以它没有被动作的胆量正确对待。

就目前而言,在没有进一步了解和研究的情况下,解决方法是更改​​操作签名以期望类型为 Object 的参数,并在方法中专门对其进行反序列化。

虽然这可行,但感觉不是正确的解决方案,因为我现在实质上是打开对任何对象类型的调用,而不是我们真正想要的对象类型。这是有道理的,因为 AWS Lambda 函数被传递了一个匿名类型(即:对象)。

更新:https://weblog.west-wind.com/posts/2017/Sep/14/Accepting-Raw-Request-Body-Content-in-ASPNET-Core-API-Controllers

本文介绍了可用于纠正此问题的解决方案类型。缺点是 Core API 并不知道如何处理字符串。作者列出了几种解决方案。我选择实现的是扩展输入格式化程序。我将在这里总结一些过程:

新 class -> RawRequestBodyFormatter : InputFormatter

CTOR -> SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));

override Boolean CanRead

override async Task<InputFormatterResult> ReadRequestBodyAsync

检查 context.HttpContext.Request.ContentType 是否包含您的 text/plain 修饰符。

在启动中,添加以下内容:

services.AddMvc(o => o.InputFormatters.Insert(0, new RawRequestBodyFormatter()));