.NET Core 3.1 中 HttpResponseMessage.Content 的意外空行为

Unexpected null behavior with HttpResponseMessage.Content in .NET core 3.1

我正在编写一段代码,它基本上是 .NET 核心 3.1 class 库中的 API 客户端 .
我用的是Visual Studio2019企业版16.5.5.

我启用了可空引用类型功能,以便在 Visual Studio 中享受编译器对空值的警告。这是我的 class 库项目的 csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <Nullable>enable</Nullable>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Flurl" Version="2.8.2" />
    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.0.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="System.Text.Json" Version="4.7.2" />
  </ItemGroup>
</Project>

在我的代码中,我想确保响应内容类型实际上是 application/json,这是我的 API 的预期返回类型:

var responseMediaType = response.Content.Headers.ContentType.MediaType;

通过检查所涉及对象的类型注释和 visual studio 智能感知,我可以读到 none 我要取消引用的对象预计是 null(visual studio intellisense 表示 'Content is not null here'、'Headers is not null here' 等等……)。

.NET core github repository seems to confirm that calling the getter for property Content of class HttpResponseMessage never returns null的简单检查。通过检查代码,似乎允许将 null 值设置为 属性,但是当调用 getter 时,基础字段通过以下方式更改为 new EmptyContent() ??= 运算符。

所以,根据我的理解,这个 属性 的 getter 永远不会 returns null,根据 属性 类型(即 不可空引用类型 HttpContent)和 visual studio 智能感知。到目前为止,还不错。

前段时间我写了一段类似的代码,我们的一位客户遇到了一个微妙的错误。由于请求查询字符串非常长,被调用端点返回了一个 414 URI too long 响应 没有响应内容 。在这种情况下,取消引用 Content 属性 以检测响应 MIME 类型会导致 NullReferenceException.NET core 2.2 class 库 发生了这种情况。

为了避免两次犯同样的错误,我为我全新的 .NET core 3.1 代码添加了一个单元测试,我在其中为调用的 API 使用了一个模拟,以这种方式配置(为了重现之前困扰我的相同场景):

_messageHandlerMock
        .Protected()
        .Setup<Task<HttpResponseMessage>>(
          "SendAsync",
          ItExpr.IsAny<HttpRequestMessage>(),
          ItExpr.IsAny<CancellationToken>()
        )
        .Returns(async () =>
        {
          await Task.Delay(2).ConfigureAwait(false);

          return new HttpResponseMessage
          {
            StatusCode = HttpStatusCode.RequestUriTooLong,
            Content = null
          };
        });

当取消引用 response.Content.Headers 时,测试失败并引发 NullReferenceException,因为 response.Content 的值为 null

由于 visual studio 智能感知建议,Content 属性 的不可空引用类型和上面链接的 HttpResponseMessage class 的源代码。

我错过了什么?

您正在查看的代码是 8 天前才提交的。这是您可能使用的版本:

https://github.com/dotnet/runtime/blob/4b03513e20e0caf9ee34817eb74356903f1bb33e/src/libraries/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs#L44

如您所见returns 没有空检查的内容。

3 天前推出了一个更新,现在可能会使用此代码,但我不确定。我建议更新或更改您的代码以不将内容设置为空。