I am transforming HttpContent
into the following 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
}
}
And am running some unit tests on it:
[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);
}
And that test fails since the Content-Length
header is not being captured on my dto. However if I do:
[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);
}
The test passes and all headers are captured. Even more I also tried this:
[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);
}
and it fails since dto
doesn't have the Content-Length
header, but dto1
does. I even tried getting the headers inside a Factory-like method like this:
public static ContentDto FromContent<T>(T content) where T : HttpContent
{
// same as the constructor
}
to see if there was something special about the StringContent
class regarding the Content-Length
headers, but it made no difference, no matter if I used the constructor (which uses the base class HttpContent
) or the generic method FromContent
(using the actual StringContent in this case) the result was the same.
So my questions are:
Is that the intended behavior of HttpContent.Headers
?
Are there some headers specific to the actual HttpContent
type?
What am I missing here?
Note: This is the code for the Flatten
extension method:
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)
}
Your example is incomplete. I was only able to recreate your issue when I accessed the ContentLength
property before calling the extension method. Somewhere in your code (most probably //rest of setup) you are either directly or indirectly calling that property which is most probably following a lazy loading pattern and it is then included in the header when next you call your extension method and it is included in the constructed string. They don't match because you are generating your manual string before accessing the content length property.
In the source code for 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;
}
}
you can see that if you did not explicitly set a content length then it will add it (lazy load) to the headers when you first try to access it.
This proves my original theory about it being added after you generated/flatten your string and then accessed the ContentLength
property and explains the inconsistent enumeration.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With