为什么我在使用ObjectContent时无法从HttpClient SendAsync中捕获异常

Ada*_*son 6 c# dotnet-httpclient

我似乎无法捕获从System.Net.Http.HttpClient的SendAsync方法抛出的某些异常.具体来说,是从HttpWebRequest.ChechProtocol方法抛出的ProtocolViolationException.代码似乎死锁或挂起,永远不会输入.

我已经创建了一个NUnit单元测试来清楚地说明问题.

public class TestClass
{
    public bool TestProperty { get; set; }
}

[Test]
public async Task CanCatchExceptionFromHttpClientSendAsync()
{
    var caughtException = false;

    try
    {
        var request = new HttpRequestMessage(HttpMethod.Get, "http://somedomain.com")
        {
            Content = new ObjectContent<TestClass>(
                new TestClass { TestProperty = true },
                new JsonMediaTypeFormatter())
        };

        await new HttpClient().SendAsync(request);
    }
    catch (Exception)
    {
        caughtException = true;
    }

    Assert.IsTrue(caughtException);
}
Run Code Online (Sandbox Code Playgroud)

测试通过使用StringContent而不是ObjectContent.我错过了一些简单的事吗?

更新

基于StringContent允许捕获异常和ObjectContent死锁的事实,我已经能够推断出这与MediaTypeFormatter的WriteToStreamAsync方法有某种关系.

我设法通过将内容"预格式化"到ByteArrayContent来解决这个问题,如下所示:

private static async Task<HttpContent> CreateContent<T>(
    T value,
    string mediaType, 
    MediaTypeFormatter formatter)
{
    var type = typeof(T);
    var header = new MediaTypeHeaderValue(mediaType);

    HttpContent content;
    using (var stream = new MemoryStream())
    {
        await formatter.WriteToStreamAsync(type, value, stream, null, null);

        content = new ByteArrayContent(stream.ToArray());
    }

    formatter.SetDefaultContentHeaders(type, content.Headers, header);

    return content;
}
Run Code Online (Sandbox Code Playgroud)

然后像这样消费它:

var content = await CreateContent(
    new TestClass(), 
    "application/json", 
    new JsonMediaTypeFormatter());

var request = new HttpRequestMessage(HttpMethod.Get, "http://foo.com")
{
    Content = content
};
Run Code Online (Sandbox Code Playgroud)

这至少可以让捕获物正常工作,但我

The*_*ESJ 1

这是 HttpClient 中的一个已知错误。您可以通过自行缓冲、设置内容长度(如 ByteArrayContent 所做的那样)或使用分块传输编码(如果您的平台支持,但手机上不支持)来解决此问题。