2016-07-24 65 views
10

我转化HttpContent分为以下DTO:HttpContent头不一致枚举

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 
    } 
} 

和我在其上运行一些单元测试:

[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); 
} 

而且,由于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); 
} 

测试通过并捕获所有头文件。更我也试过这样:

[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); 
} 

,自dto失败没有Content-Length头,但dto1一样。我甚至尝试得到一个厂般方法像这里面的标题:

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

,看看是否有什么特别的关于Content-Length头的StringContent类,但它并没有区别,不管我使用构造函数(使用基类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) 
} 
+0

ToDictionary中的项目顺序不能保证,这是否产生相同的结果?headers.ToDictionary(h => h.Key,h => string.Join(“;”,h.Value)) .Orderby(x => x.Key).Select(kvp => $“{kvp.Key}:{kvp.Value}”)'? –

+0

@AkashKava问题不在于命令。问题是'Content-Length'标题并不总是在那里。 – Luiso

+0

@Luiso,你可以展示[mcve],以便你的问题可以被准确地复制。这将有助于找到解决问题的办法。 – Nkosi

回答

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; 
    } 
} 

你可以看到,如果你没有明确设置内容长度,然后将它(延迟加载)添加到页眉当你第一次尝试访问它。

这证明了我的原始理论,它是在您生成/拉平您的字符串之后添加的,然后访问ContentLength属性并解释了不一致的枚举。

+0

我刚刚加倍检查了我的代码,我看不到我可以在哪里访问'Content-Length'属性,至少不是一个明显的属性,但我可能会错过它。非常感谢你 – Luiso

+1

我在你的'ContentDto'中注意到你有'Data'属性。你如何以及在哪里填充该财产。如果您正在读取htto内容的流或字符串,则源代码会在读取流之前显示它调用内容长度,以便知道要读取多少内容。这可能是你的罪魁祸首。 – Nkosi

+0

是的,你是对的,那正是发生了什么事。非常感谢你非常有帮助 – Luiso

0

看来,HttpContent类与头性能相当奇怪的行为。不知何故,内容长度似乎是按照here的规定计算的。它没有专门解决您的问题,但您可以使用与最初的httpContent对象相似的新的httpContent对象进行测试。我很确定你能够毫无问题地获得内容的长度。