HttpContent Headers 枚举不一致
HttpContent Headers inconsistent enumeration
我正在将 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
}
}
并且 运行 对其进行了一些单元测试:
[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
header 未在我的 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);
}
测试通过并捕获所有 header。甚至更多我也试过这个:
[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
header,但是 dto1
有。我什至尝试将 headers 放入 Factory-like 方法中,如下所示:
public static ContentDto FromContent<T>(T content) where T : HttpContent
{
// same as the constructor
}
看看StringContent
class对于Content-Length
header有没有什么特别之处,但是不管我用构造函数(使用基础 class HttpContent
)或通用方法 FromContent
(在本例中使用实际的 StringContent)结果是相同的。
所以我的问题是:
这是 HttpContent.Headers
的预期行为吗?
是否有一些特定于实际 HttpContent
类型的 header?
我在这里错过了什么?
注意:这是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)
}
HttpContent class 的 headers 属性似乎有一个非常奇怪的行为。不知何故,内容长度似乎是按规定计算的 here。它没有具体解决您的问题,但您可以使用类似于初始内容的新 httpContent object 进行测试。我很确定您将能够毫无问题地获取内容长度。
您的示例不完整。在调用扩展方法之前访问 ContentLength
属性 时,我只能重现您的问题。在你的代码中的某处 (很可能是//rest of setup) 你直接或间接调用了那个 属性 ,它很可能遵循延迟加载模式,然后它被包含在 header 下一次调用扩展方法时,它包含在构造的字符串中。它们不匹配,因为您在访问内容长度 属性.
之前生成了手动字符串
在 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;
}
}
您可以看到,如果您没有显式设置内容长度,那么当您第一次尝试访问它时,它将(延迟加载)添加到 headers。
这证明了我最初的理论,即在您 generated/flatten 您的字符串之后添加它,然后访问 ContentLength
属性 并解释了不一致的枚举。
我正在将 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
}
}
并且 运行 对其进行了一些单元测试:
[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
header 未在我的 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);
}
测试通过并捕获所有 header。甚至更多我也试过这个:
[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
header,但是 dto1
有。我什至尝试将 headers 放入 Factory-like 方法中,如下所示:
public static ContentDto FromContent<T>(T content) where T : HttpContent
{
// same as the constructor
}
看看StringContent
class对于Content-Length
header有没有什么特别之处,但是不管我用构造函数(使用基础 class HttpContent
)或通用方法 FromContent
(在本例中使用实际的 StringContent)结果是相同的。
所以我的问题是:
这是 HttpContent.Headers
的预期行为吗?
是否有一些特定于实际 HttpContent
类型的 header?
我在这里错过了什么?
注意:这是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)
}
HttpContent class 的 headers 属性似乎有一个非常奇怪的行为。不知何故,内容长度似乎是按规定计算的 here。它没有具体解决您的问题,但您可以使用类似于初始内容的新 httpContent object 进行测试。我很确定您将能够毫无问题地获取内容长度。
您的示例不完整。在调用扩展方法之前访问 ContentLength
属性 时,我只能重现您的问题。在你的代码中的某处 (很可能是//rest of setup) 你直接或间接调用了那个 属性 ,它很可能遵循延迟加载模式,然后它被包含在 header 下一次调用扩展方法时,它包含在构造的字符串中。它们不匹配,因为您在访问内容长度 属性.
在 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;
}
}
您可以看到,如果您没有显式设置内容长度,那么当您第一次尝试访问它时,它将(延迟加载)添加到 headers。
这证明了我最初的理论,即在您 generated/flatten 您的字符串之后添加它,然后访问 ContentLength
属性 并解释了不一致的枚举。