2016-07-24 49 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對象進行測試。我很確定你能夠毫無問題地獲得內容的長度。