HttpContent标头不一致的枚举

Lui*_*iso 10 c# http-headers httpcontent dotnet-httpclient

我正在转变HttpContent为以下dto:

public class ContentDto 
{
     public string ContentType {get; set;}
     public string Headers {get; set; }
     public object Data { get; set; }

     public ContentDto(HttpContent content)
     {
          Headers = content.Headers.Flatten();
          // rest of the setup
     }
}
Run Code Online (Sandbox Code Playgroud)

我正在运行一些单元测试:

[Fact]
public void CanBuild()
{
     var content = new StringContent("some json", Enconding.UTF8, "application/json");
     var dto = new ContentDto(content);

     var contentHeaders = content.Headers.Flatten();

     Assert.Equal(contentHeaders, dto.Headers);
}
Run Code Online (Sandbox Code Playgroud)

并且该测试失败,因为Content-Length标题没有被我的dto捕获.但是,如果我这样做:

[Fact]
public void CanBuild()
{
     var content = new StringContent("some json", Enconding.UTF8, "application/json");

     var contentHeaders = content.Headers.Flatten();

     var dto = new ContentDto(content);

     Assert.Equal(contentHeaders, dto.Headers);
}
Run Code Online (Sandbox Code Playgroud)

测试通过并捕获所有标头.更多我也试过这个:

 [Fact]
 public void CanBuild()
 {
     var content = new StringContent("some json", Enconding.UTF8, "application/json");

     var dto = new ContentDto(content);

     var contentHeaders = content.Headers.Flatten();

     var dto1 = new ContentDto(content);

     Assert.Equal(contentHeaders, dto.Headers);                
     Assert.Equal(contentHeaders, dto1.Headers);
}
Run Code Online (Sandbox Code Playgroud)

它失败,因为dto没有Content-Length标题,但dto1确实如此.我甚至尝试在类似Factory的方法中获取标题:

 public static ContentDto FromContent<T>(T content) where T : HttpContent
 {
      // same as the constructor
 }
Run Code Online (Sandbox Code Playgroud)

看看StringContent关于Content-Length标题的类是否有一些特殊的东西,但它没有任何区别,无论我使用构造函数(使用基类HttpContent)还是泛型方法FromContent(在这种情况下使用实际的StringContent)结果是相同.

所以我的问题是:

那是预期的行为HttpContent.Headers吗?
是否有一些特定于实际HttpContent类型的标题?
我在这里错过了什么?

注意:这是Flatten扩展方法的代码:

 public static string Flatten(this HttpHeaders headers)
 {
      var data = headers.ToDictionary(h => h.Key, h => string.Join("; ", h.Value))
                        .Select(kvp => $"{kvp.Key}: {kvp.Value}");

      return string.Join(Environment.NewLine, data)
 }
Run Code Online (Sandbox Code Playgroud)

Nko*_*osi 4

你的例子不完整。ContentLength我只能在调用扩展方法之前访问该属性时才能重新创建您的问题。在代码中的某个位置(很可能//其余的设置),您直接或间接调用该属性,该属性很可能遵循延迟加载模式,然后在下次调用扩展方法时将其包含在标头中,并且将其包含在内在构建的字符串中。它们不匹配,因为您在访问内容长度属性之前生成手动字符串。

在HttpContentHeaders.ContentLength的源代码中

public long? ContentLength
{
    get
    {
        // 'Content-Length' can only hold one value. So either we get 'null' back or a boxed long value.
        object storedValue = GetParsedValues(HttpKnownHeaderNames.ContentLength);

        // Only try to calculate the length if the user didn't set the value explicitly using the setter.
        if (!_contentLengthSet && (storedValue == null))
        {
            // If we don't have a value for Content-Length in the store, try to let the content calculate
            // it's length. If the content object is able to calculate the length, we'll store it in the
            // store.
            long? calculatedLength = _calculateLengthFunc();

            if (calculatedLength != null)
            {
                SetParsedValue(HttpKnownHeaderNames.ContentLength, (object)calculatedLength.Value);
            }

            return calculatedLength;
        }

        if (storedValue == null)
        {
            return null;
        }
        else
        {
            return (long)storedValue;
        }
    }
    set
    {
        SetOrRemoveParsedValue(HttpKnownHeaderNames.ContentLength, value); // box long value
        _contentLengthSet = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以看到,如果您没有显式设置内容长度,那么当您第一次尝试访问它时,它会将其添加(延迟加载)到标题中。

这证明了我最初的理论,即在生成/展平字符串之后添加它,然后访问该ContentLength属性并解释了不一致的枚举。