HttpClient PostAsJsonAsync在.NET Core和Classic .NET中表现不同

Sig*_*hol 8 .net asp.net-web-api .net-core

我在IIS中托管经典.NET WebAPI端点,该端点接受POST请求以上载文档.

我创建了两个连接到WebAPI并上传文档的控制台应用程序:一个是经典的.NET(v4.6.2)控制台应用程序,另一个是.NET Core控制台应用程序.代码是相同的,但服务对每个代码的响应不同.WebAPI似乎无法读取.NET Core POST请求的请求主体,但能够使用Classic .NET POST请求执行此操作.

有什么我想念的吗?

WebAPI端点

HTTP://本地主机/的WebAPI /库/ API /文档

[Route("api/Documents")]
public class DocumentsController : ApiController
{
    [HttpPost()]
    public void Create([FromBody] Document document)
    {
        if (document == null)
        {
            Debug.WriteLine("Posted document is null.");
        }
        else
        {
            Debug.WriteLine($"Document text: {document.Text}");
        }
    }
}

public class Document
{
    public string Text { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

.NET Core/Classic .NET控制台应用程序

class Program
{
    static void Main(string[] args)
    {
        RunAsAsync().Wait();
    }

    static async Task RunAsAsync()
    {
        var httpClient = new HttpClient()
        {
            BaseAddress = new Uri("http://api-localhost"),
        };

        httpClient.DefaultRequestHeaders.Accept.Clear();
        httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        await httpClient.PostAsJsonAsync("/library/api/documents", new Document() { Text = "The document text" });
    }
}

public class Document
{
    public string Text { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

问题是:当我将调试器附加到WebAPI时,输出以下内容:

来自Classic .NET控制台应用程序的POST请求:

文档文本:文档文本

来自.NET Core控制台应用程序的POST请求:

发布的文件为空.

我无法解释行为上的差异.

一些额外的信息:

.NET Core控制台应用程序的project.json文件:

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },

  "dependencies": {
    "Microsoft.AspNet.WebApi.Client": "5.2.3",
    "Microsoft.NETCore.App": {
      "type": "platform",
      "version": "1.0.1"
    },
    "System.Net.Http": "4.3.0",
    "System.Runtime.Serialization.Xml": "4.3.0"

  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": [
        "dnxcore50",
        "portable-net451+win8"
      ]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

经典.NET packages.config:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net462" />
  <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net462" />
</packages>
Run Code Online (Sandbox Code Playgroud)

来自Fiddler,原始的.NET Core帖子:

POST http://localhost/library/api/documents HTTP/1.1
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Accept: application/json
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: api-localhost

1c
{"Text":"The document text"}
0
Run Code Online (Sandbox Code Playgroud)

这是原始的经典.NET POST:

POST http://localhost/library/api/documents HTTP/1.1 Accept:
application/json Content-Type: application/json; charset=utf-8 Host:
api-localhost Content-Length: 28 Expect: 100-continue Connection:
Keep-Alive

{"Text":"The document text"}
Run Code Online (Sandbox Code Playgroud)

在我看来,奇怪的"1c"和"0"可能与我的问题有关.但我不知道他们应该如何/为什么应该在那里.

更新:一些观察

我还创建了一个非常基本的.NET Core WebAPI端点,它基本上与最初描述的Classic .NET完全相同.事实证明,这可以接受来自两个控制台应用程序的POST请求.

我还为.NET Core创建了第三个控制台应用程序,我跳过了使用PostAsJsonAsync,而是使用PostAsync和StringContent:

public class Program
{
    static void Main(string[] args)
    {
        RunAsAsync().Wait();
    }

    static async Task RunAsAsync()
    {
        var httpClient = new HttpClient()
        {
            BaseAddress = new Uri("http://localhost"),
        };

        httpClient.DefaultRequestHeaders.Accept.Clear();
        httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        var document = new Document() { Text = "The document text." };

        var content = JsonConvert.SerializeObject(document);

        HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");

        await httpClient.PostAsync("/library2/api/documents", httpContent);
    }
}

public class Document
{
    public string Text { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

在/ library/...和/ library2/...之间交替,.NET Core和Classic .NET WebAPI都接受来自此应用程序的POST请求.

摘要

在服务器端,我有两个WebAPI

  • / library - Classic .NET
  • / library2 - .NET Core

在客户端,我有三个相同控制台应用程序的变体:

  • ConsoleApplication1 - 使用PostAsJsonAsync的Classic .NET
    • 适用于两个WebAPI都可以正常工作
  • ConsoleApp1 - 使用PostAsJsonAsync的.NET Core
    • 使用/ library(Classic .NET)失败,可以正常使用/ library2(.NET Core)
  • ConsoleApp2 - 使用PostAsync的.NET Core
    • 适用于两个WebAPI都可以正常工作

这里显而易见的解决方法是永远不要在.NET Core客户端应用程序中使用PostAsJsonAsync,这很遗憾,因为它非常方便.:(

-S

Sig*_*hol 3

根本问题似乎是 Classic .NET WebAPI 无法在当前最新版本 (v5.2.3) 中处理分块请求。我假设这是一个错误,并作为暂时降级到 v5.0 的解决方法。

特别感谢@Developer 提供的有用意见。

-S

  • 我找不到关于这个问题的官方微软帖子,有人知道这个包是在哪里构建的吗?一直在 github 中查找,但找不到它们...我确实认为我们需要提交一个问题...因为微软似乎没有意识到这一点。 (2认同)