Question Enumération incohérente des en-têtes HttpContent


Je me transforme HttpContent dans le dto suivant:

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

Et j'exécute des tests unitaires dessus:

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

Et ce test échoue depuis le Content-Length l'en-tête n'est pas capturé sur mon dto. Cependant si je fais:

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

Le test réussit et tous les en-têtes sont capturés. Encore plus j'ai aussi essayé ceci:

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

et il échoue depuis dto n'a pas le Content-Length en-tête, mais dto1 Est-ce que. J'ai même essayé d'obtenir les en-têtes à l'intérieur d'un En usine méthode comme celle-ci:

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

pour voir s'il y avait quelque chose de spécial sur le StringContent classe concernant le Content-Length les en-têtes, mais cela ne fait aucune différence, peu importe si j'utilise le constructeur (qui utilise la classe de base HttpContent) ou la méthode générique FromContent (en utilisant le StringContent réel dans ce cas) le résultat était le même.

Donc mes questions sont:

Est-ce le comportement prévu de HttpContent.Headers?
Y a-t-il des en-têtes spécifiques au réel HttpContent type?
Qu'est-ce que j'oublie ici?

Remarque: Ceci est le code pour le Flatten méthode d'extension:

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

10
2017-07-24 16:56


origine


Réponses:


Votre exemple est incomplet. Je n'ai pu recréer votre problème que lorsque j'ai accédé à la ContentLength propriété avant d'appeler la méthode d'extension. Quelque part dans votre code (le plus probablement // reste de la configuration) vous appelez directement ou indirectement cette propriété qui suit probablement un modèle de chargement paresseux et elle est ensuite incluse dans l'en-tête lorsque vous appelez votre méthode d'extension et elle est incluse dans la chaîne construite. Ils ne correspondent pas car vous générez votre chaîne manuelle avant d'accéder à la propriété de longueur du contenu.

Dans le code source de 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;
    }
}

Vous pouvez voir que si vous n'avez pas défini explicitement une longueur de contenu, il l'ajoutera (chargement différé) aux en-têtes lorsque vous essayez pour la première fois d'y accéder.

Cela prouve que ma théorie originale a été ajoutée après avoir généré / aplati votre chaîne, puis a accédé à la ContentLength propriété et explique l'énumération incohérente.


4
2017-08-03 01:39



Il semble que la classe HttpContent ait un comportement assez étrange avec les propriétés des en-têtes. D'une certaine manière, la longueur du contenu semble être calculée comme il est indiqué ici. Il ne traite pas spécifiquement votre problème, mais vous pouvez effectuer un test avec un nouvel objet httpContent similaire à celui initial. Je suis presque certain que vous pourrez obtenir la longueur du contenu sans problème.


0
2017-07-28 08:05