HttpContent Headers - несогласованное перечисление
Я преобразую 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
не записывается на моем 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
делает. Я даже пытался получить заголовки внутри Factory -подобного метода следующим образом:
public static ContentDto FromContent<T>(T content) where T : HttpContent
{
// same as the constructor
}
чтобы узнать, есть ли что-то особенное в классе 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)
}
Ответы
Ответ 1
Ваш пример неполный. Я только смог восстановить вашу проблему, когда я обратился к свойству 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 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
и объяснили несогласованное перечисление.
Ответ 2
Кажется, что класс HttpContent имеет довольно странное поведение с свойствами заголовков. Как-то длина контента, по-видимому, вычисляется, как указано здесь. Это не касается конкретной проблемы, но вы можете сделать тест с новым объектом httpContent, аналогичным исходному. Я уверен, что вы сможете получить длину контента без проблем.