【问题标题】:Unexpected null behavior with HttpResponseMessage.Content in .NET core 3.1.NET 核心 3.1 中的 HttpResponseMessage.Content 出现意外的空行为
【发布时间】:2020-05-14 22:38:37
【问题描述】:

我正在编写一段代码,它基本上是 .NET core 3.1 类库中的一个 API 客户端
我正在使用 Visual Studio 2019 企业版 16.5.5

我已启用可空引用类型功能,以便在 Visual Studio 中享受针对空值的编译器警告。这是我的类库项目的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 intellisense,我可以读到我要取消引用的所有对象都不会是 null(Visual Studio intellisense 说“这里的内容不为空” , '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 永远不会返回 null,具体取决于属性类型(不可为 null 的引用类型HttpContent)和视觉工作室智能感知。到目前为止,一切顺利。

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

为了避免两次犯同样的错误,我为我全新的 .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 类的源代码。

我错过了什么?

【问题讨论】:

  • 您确定正在使用您的模拟(以及 ReturnsAsync 中的回调)吗?我似乎记得几个月前我在尝试模拟没有被调用的相同受保护方法时遇到的一个非常相似的问题。不记得问题最终是什么或我是如何解决的。
  • @pinkfloydx33 我确定实际使用了模拟。如果您需要一些代码示例来模拟 HTTP 客户端,请遵循本指南 gingter.org/2018/07/26/…。我发现它很有用,我们总是将这种方法用于此类单元测试。

标签: c# .net-core dotnet-httpclient c#-8.0 nullable-reference-types


【解决方案1】:

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

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

如您所见,它返回的内容没有进行空值检查。

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

【讨论】:

  • 非常感谢。我被视觉工作室智能感知关于可空性的建议愚弄了。出于某种奇怪的原因,它似乎使用了最新版本代码的类型注释。
猜你喜欢
  • 1970-01-01
  • 2022-07-08
  • 1970-01-01
  • 1970-01-01
  • 2021-10-02
  • 1970-01-01
  • 2020-09-09
  • 2018-07-31
  • 1970-01-01
相关资源
最近更新 更多