MVC C# Web App 中的 400 Bad Request 但不是 Postman

400 Bad Request in MVC C# Web App but not Postman

我已经使用了以下方法几个小时了,但无法PUT 我对外部 Ellucian Ethos API.

的更新
public async Task<string> UpdatePersonH11()
{
    var token = await GetAccessTokenAsync("TokenApi", "value");
    var guid = await GetPersonGUID(token);
    Uri personsURI = new Uri(string.Format("https://URLtoAPI" + guid));

    H11Model h11Data = new H11Model
    {
        h11 = new h11
        {
            extendedPersonUser2 = "2021/FA",
            extendedPersonUser3 = "OUT",
            extendedPersonUser4 = DateTime.Now.ToShortDateString() 
        }
    };

    using (var client = new HttpClient())
    {
        client.BaseAddress = personsURI;
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Add("Accept", "application/vnd.hedtech.integration.v12+json");
        client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate, br");
        client.DefaultRequestHeaders.Add("Connection", "keep-alive");
        client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
      
        var responseString = "";
        var content = JsonSerializer.Serialize(h11Data);

        **using (HttpResponseMessage response = await client.PutAsJsonAsync(personsURI, content))** Debugger stops here and gives me a 400 Bad Request
        {
            if (response.IsSuccessStatusCode)
            {
                responseString = await response.Content.ReadAsStringAsync();                   
            }
            else
            {
                log.Debug("Error in response." + response);
                return "";
            }
            return responseString;
        }
    }
}

这是 Json 正在为变量 content 序列化以通过 PutAsJsonAsync 发送的 json

{
    "h11":  
    {
        "extendedPersonUser2":"2021/FA",
        "extendedPersonUser3":"OUT",
        "extendedPersonUser4":"8/5/2021",
    }
}

H11型号

public class H11Model
{        
    [JsonProperty("h11")]
    public h11 h11 { get; set; }
}

public class h11
{
    [JsonProperty("extendedPersonUser2")]
    public string extendedPersonUser2 { get; set; }

    [JsonProperty("extendedPersonUser3")]
    public string extendedPersonUser3 { get; set; }

    [JsonProperty("extendedPersonUser4")]
    public string extendedPersonUser4 { get; set; }

    [JsonProperty("extendedPersonUser5")]
    public string extendedPersonUser5 { get; set; }

    [JsonProperty("extendedPersonUser6")]
    public string extendedPersonUser6 { get; set; }

    [JsonProperty("extendedPersonUser7")]
    public string extendedPersonUser7 { get; set; }

    [JsonProperty("extendedPersonUser8")]
    public string extendedPersonUser8 { get; set; }

    [JsonProperty("extendedPersonUser9")]
    public string extendedPersonUser9 { get; set; }

    [JsonProperty("extendedPersonUser10")]
    public string extendedPersonUser10 { get; set; }
}

我读到 PutAsJsonAsync 不需要先序列化内容,但是当我注释掉 var content = JsonSerializer.Serialize(h11Data); 时,我得到一个 406 Not Acceptable 错误,这让我相信我确实需要先序列化内容。

这是我从调试器返回的请求消息:

{
    Method: PUT, 
    RequestUri: 'API URI', 
    Version: 1.1, 
    Content: System.Net.Http.ObjectContent`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=token]],
    Headers:
    {
        Authorization: Bearer 'token is here'
        Accept: application/vnd.hedtech.integration.v12+json
        Content-Type: application/json; charset=utf-8
        Content-Length: 293
    }
}

这是回复信息:

{
    StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
    {
        Connection: keep-alive
        pragma: no-cache
        hedtech-ethos-integration-application-id: GUID
        hedtech-ethos-integration-application-name: Colleague
        vary: origin
        access-control-expose-headers: x-max-page-size,x-media-type,x-total-count,x-content-restricted,hedtech-ethos-integration-application-id,hedtech-ethos-integration-application-name,hedtech-ethos-integration-proxy-generated
        Cache-Control: no-cache
        Date: Fri, 06 Aug 2021 13:41:56 GMT
        Server: Microsoft-IIS/10.0
        Content-Length: 447
        Content-Type: text/plain; charset=utf-8
        Expires: -1
    }
}

如果有人能帮我指出正确的方向,我将不胜感激。我以前没有以这种方式使用过 API,所以这对我来说是一个新领域,我对此感到困惑。

编辑:有效的最终代码(片段):

H11Model h11Data = new H11Model
        {
            h11 = new h11
            {
                extendedPersonUser5 = "OUT", // fall term
                extendedPersonUser6 = DateTime.Now.ToShortDateString()
            }
        };

        using (var client = new HttpClient())
        {
            client.BaseAddress = personsURI;
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Add("Accept", "application/vnd.hedtech.integration.v12+json");
            client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate, br");
            client.DefaultRequestHeaders.Add("Connection", "keep-alive");
            client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");                                       

            var responseString = "";
            //var content = JsonSerializer.Serialize(h11Data);

            using (HttpResponseMessage response = await client.PutAsJsonAsync(personsURI, h11Data))
            {
                if (response.IsSuccessStatusCode)
                {
                    try
                    {
                        responseString = await response.Content.ReadAsStringAsync();
                    }
                    catch (NotSupportedException ex) // When content type is not valid
                    {
                        log.Debug("The content type is not supported.", ex);
                    }
                    catch (JsonException ex) // Invalid JSON
                    {
                        log.Debug("Invalid JSON.", ex);
                    }
                }
                else
                {
                    log.Debug("Error in response." + response);
                    return "";
                }
                return responseString;
            }
        }
    

如果服务没有覆盖 HTTP 代码的含义并按照 RFC 中的描述使用它们,那么

406 Not Acceptable

The 406 (Not Acceptable) status code indicates that the target resource does not have a current representation that would be acceptable to the user agent, according to the proactive negotiation header fields received in the request (Section 5.3), and the server is unwilling to supply a default representation.

简而言之,AcceptAccept-EncodingAccept-CharsetAccept-Language HTTP header 的值无效或未按服务预期定义。

在所描述的情况下,至少有两个这样的 header 通过 HttpClient.DefaultRequestHeaders collection 设置。它们的值需要与服务文档进行比较。并且需要注意的是,在一般情况下 Accept-Encoding header 应该设置为 HttpClientHandler.AutomaticDecompression 属性 而不是 DefaultRequestHeaders collection,否则它将被忽略。

400 Bad Request

The 400 (Bad Request) status code indicates that the server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).

例如,服务期望{ "h11": { "extendedPersonUser2": ...JSONobject;但取而代之的是,服务接收 "{ \"h11\": { \"extendedPersonUser2\": ... 并且无法反序列化它。

正如评论中已经提到的,PutAsJsonAsync extension method and HttpClient.PutAsync method with StringContent之间存在差异。第一个向指定的 Uri 发送 PUT 请求,其中包含在请求 body 中序列化为 JSON 的值。 PutAsync 方法发送内容参数定义的原始内容。

因此,示例中的代码

   string content = JsonSerializer.Serialize(h11Data);
   ... = await client.PutAsJsonAsync(personsURI, content);

使用 PutAsJsonAsync 并发送另外序列化为 JSON 的 JSON 字符串。结果服务收到以下内容 "{ \"h11\": { \"extendedPersonUser2\": ....

通常,当使用PutAsJsonAsync扩展方法时,不需要额外的JSON序列化。内容 object 可以直接传递给 PutAsJsonAsync 扩展方法。

   ... = await client.PutAsJsonAsync(personsURI, h11Data);

在这种情况下,请求将作为 { "h11": { "extendedPersonUser2": ....

发送

有多个 PutAsJsonAsync 扩展方法重载也接受 JsonSerializerOptions 自定义 JSON 序列化。重要的是要注意 PutAsJsonAsync 在内部使用 HttpClient.PutAsync 方法和 StringContent 来发送请求。