Comment extraire la valeur d'en-tête personnalisée dans le gestionnaire de messages de l'API Web?

150

J'ai actuellement un gestionnaire de messages dans mon service API Web qui remplace 'SendAsync' comme suit:

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
  //implementation
}

Dans ce code, je dois inspecter une valeur d'en-tête de demande ajoutée personnalisée nommée MyCustomID. Le problème est lorsque je fais ce qui suit:

if (request.Headers.Contains("MyCustomID"))  //OK
    var id = request.Headers["MyCustomID"];  //build error - not OK

...Je reçois le message d'erreur suivant:

Impossible d'appliquer l'indexation avec [] à une expression de type «System.Net.Http.Headers.HttpRequestHeaders»

Comment puis-je accéder à un en- tête de demande personnalisé unique via l' instance HttpRequestMessage( MSDN Documentation ) transmise à cette méthode remplacée?

atconway
la source
que se passe-t-il si vous utilisez request.Headers.Get("MyCustomID");?
udidu
2
Il n'y a pas de Get' on the type HttpRequestHeaders. Le message: "Impossible de résoudre le symbole 'Get'" est généré.
atconway

Réponses:

253

Essayez quelque chose comme ceci:

IEnumerable<string> headerValues = request.Headers.GetValues("MyCustomID");
var id = headerValues.FirstOrDefault();

Il existe également une méthode TryGetValues ​​sur les en-têtes que vous pouvez utiliser si vous n'êtes pas toujours assuré d'avoir accès à l'en-tête.

Youssef Moussaoui
la source
26
La vérification null pour GetValues ​​ne sert aucune valeur car elle ne retournera jamais null. Si l'en-tête n'existe pas, vous obtiendrez une InvalidOperationException. Vous devez utiliser TryGetHeaders s'il est possible que l'en-tête n'existe pas dans la demande et rechercher une fausse réponse OU essayer / attraper autour de l'appel GetValues ​​(non recommandé).
Drew Marsh
4
@Drew request.Headers.Single (h => h.Key == "Autorisation"); Beaucoup moins de code faisant la même chose!
Elisabeth
17
Pourquoi pas justevar id = request.Headers.GetValues("MyCustomID").FirstOrDefault();
Gaui
3
@SaeedNeamati car les valeurs d'en-tête ne sont pas un-à-un. Vous pouvez avoir Some-Header: oneet puis Some-Header: twodans la même demande. Certaines langues ignorent silencieusement «un», mais c'est incorrect. C'est dans la RFC mais je suis trop paresseux pour le trouver maintenant.
Cory Mawhorter
1
Le point de Saeed est valide, la convivialité est importante et le cas d'utilisation le plus courant ici est de récupérer une valeur pour un en-tête de demande. Vous pouvez toujours avoir une opération GetValues ​​pour récupérer plusieurs valeurs pour un en-tête de demande (que les gens utiliseront rarement), mais 99% du temps, ils voudront simplement récupérer une valeur pour un en-tête de demande spécifique, et cela devrait être un seul doublure.
Justin
39

La ligne ci throws exception- dessous si la clé n'existe pas.

IEnumerable<string> headerValues = request.Headers.GetValues("MyCustomID");

Solution de contournement :

Inclure System.Linq;

IEnumerable<string> headerValues;
var userId = string.Empty;

     if (request.Headers.TryGetValues("MyCustomID", out headerValues))
     {
         userId = headerValues.FirstOrDefault();
     }           
SharpCoder
la source
17

Pour développer la réponse de Youssef, j'ai écrit cette méthode en me basant sur les préoccupations de Drew concernant l'en-tête inexistant, car j'ai rencontré cette situation pendant les tests unitaires.

private T GetFirstHeaderValueOrDefault<T>(string headerKey, 
   Func<HttpRequestMessage, string> defaultValue, 
   Func<string,T> valueTransform)
    {
        IEnumerable<string> headerValues;
        HttpRequestMessage message = Request ?? new HttpRequestMessage();
        if (!message.Headers.TryGetValues(headerKey, out headerValues))
            return valueTransform(defaultValue(message));
        string firstHeaderValue = headerValues.FirstOrDefault() ?? defaultValue(message);
        return valueTransform(firstHeaderValue);
    }

Voici un exemple d'utilisation:

GetFirstHeaderValueOrDefault("X-MyGuid", h => Guid.NewGuid().ToString(), Guid.Parse);

Jetez également un œil à la réponse de @ doguhan-uluca pour une solution plus générale.

néontapir
la source
1
Funcet Actionsont des constructions de signature de délégué génériques intégrées à .NET 3.5 et versions ultérieures. Je serais heureux de discuter de questions spécifiques sur la méthode, mais je recommanderais d'abord d'en apprendre davantage.
neontapir
1
@neontapir (et autres) le deuxième paramètre est utilisé pour fournir une valeur par défaut si la clé n'est pas trouvée. Le troisième paramètre est utilisé pour «transformer» la valeur de retour pour qu'elle soit du type souhaité qui spécifie également le type à renvoyer. Selon l'exemple, si 'X-MyGuid' n'est pas trouvé, le paramètre 2 lambda fournit essentiellement un guid par défaut sous forme de chaîne (comme il aurait été récupéré à partir de Header) et le troisième paramètre Guid.Parse traduira la valeur de chaîne trouvée ou par défaut. dans un GUID.
Mikee
@neontapir d'où vient la requête dans cette fonction? (et s'il est nul, comment un nouveau HttpRequestMessage () aura-t-il des en-têtes? n'est-il pas logique de simplement renvoyer la valeur par défaut si Request est nul?
mendel
Cela fait deux ans, mais si je me souviens bien, un nouveau HttpRequestMessageest initialisé avec une collection d'en-têtes vide, qui n'est pas nulle. Cette fonction finit par renvoyer la valeur par défaut si la requête est nulle.
neontapir
@mendel, neontapir J'ai essayé d'utiliser l'extrait de code ci-dessus et je pense que la "Demande" à la ligne 2 du corps de la méthode devrait être soit un champ privé dans la classe contenant la méthode, soit être passée en paramètre (de type HttpRequestMessage) à la méthode
Sudhanshu Mishra
12

Créez une nouvelle méthode - « Renvoie une valeur d'en-tête HTTP individuelle » et appelez cette méthode avec une valeur de clé à chaque fois que vous devez accéder à plusieurs valeurs de clé à partir de HttpRequestMessage.

public static string GetHeader(this HttpRequestMessage request, string key)
        {
            IEnumerable<string> keys = null;
            if (!request.Headers.TryGetValues(key, out keys))
                return null;

            return keys.First();
        }
SRI
la source
Que faire si MyCustomID ne fait pas partie de la requête ... il renvoie une exception nulle.
Prasad Kanaparthi le
10

Pour développer davantage la solution de @ neontapir, voici une solution plus générique qui peut s'appliquer à HttpRequestMessage ou HttpResponseMessage de la même manière et ne nécessite pas d'expressions ou de fonctions codées à la main.

using System.Net.Http;
using System.Collections.Generic;
using System.Linq;

public static class HttpResponseMessageExtensions
{
    public static T GetFirstHeaderValueOrDefault<T>(
        this HttpResponseMessage response,
        string headerKey)
    {
        var toReturn = default(T);

        IEnumerable<string> headerValues;

        if (response.Content.Headers.TryGetValues(headerKey, out headerValues))
        {
            var valueString = headerValues.FirstOrDefault();
            if (valueString != null)
            {
                return (T)Convert.ChangeType(valueString, typeof(T));
            }
        }

        return toReturn;
    }
}

Exemple d'utilisation:

var myValue = response.GetFirstHeaderValueOrDefault<int>("MyValue");
Doguhan Uluca
la source
Ça a l'air génial, mais GetFirstHeaderValueOrDefaulta deux paramètres, donc il se plaint du paramètre manquant lors de l'appel en tant qu'exemple d'utilisation var myValue = response.GetFirstHeaderValueOrDefault<int>("MyValue");Suis-je manquant quelque chose?
Jeb50
Ajout de la nouvelle classe statique, remplacement de Response for Request. Appelé depuis le contrôleur d'API, comme var myValue = myNameSpace.HttpRequestMessageExtension.GetFirstHeaderValueOrDefault<int>("productID");obtenu Il n'y a pas d'argument donné qui correspond au paramètre formel requis 'headerKey' de 'HttpRequestMessageExtension.GetFirstHeaderValueOrDefault <T> (HttpRequestMessage, string)'
Jeb50
@ Jeb50 déclarez-vous using HttpResponseMessageExtensionssur le fichier que vous essayez d'utiliser cette extension?
Doguhan Uluca
4

Pour ASP.Net Core, il existe une solution simple si vous souhaitez utiliser le paramètre directement dans la méthode du contrôleur: utilisez l'annotation [FromHeader].

        public JsonResult SendAsync([FromHeader] string myParam)
        {
        if(myParam == null)  //Param not set in request header
        {
           return null;
        }
        return doSomething();
    }   

Informations supplémentaires: Dans mon cas, "myParam" devait être une chaîne, int était toujours 0.

Reiner
la source
4

Pour ASP.NET, vous pouvez obtenir l'en-tête directement à partir du paramètre dans la méthode du contrôleur à l'aide de cette bibliothèque / package simple . Il fournit un [FromHeader]attribut comme vous l'avez dans ASP.NET Core :). Par exemple:

    ...
    using RazHeaderAttribute.Attributes;

    [Route("api/{controller}")]
    public class RandomController : ApiController 
    {
        ...
        // GET api/random
        [HttpGet]
        public IEnumerable<string> Get([FromHeader("pages")] int page, [FromHeader] string rows)
        {
            // Print in the debug window to be sure our bound stuff are passed :)
            Debug.WriteLine($"Rows {rows}, Page {page}");
            ...
        }
    }
lawrenceagbani
la source
4

Solution une ligne

var id = request.Headers.GetValues("MyCustomID").FirstOrDefault();
Roman Marusyk
la source
Que faire si MyCustomID ne fait pas partie de la requête ... il renvoie une exception nulle.
Prasad Kanaparthi le
1
@PrasadKanaparthi alors vous devriez utiliser une autre réponse comme stackoverflow.com/a/25640256/4275342 . Vous voyez qu'il n'y a pas de contrôle nul, alors qu'est-ce que requestc'est null? C'est également possible. Ou que faire si MyCustomIDune chaîne vide ou non égale à foo? Cela dépend du contexte, donc cette réponse décrit simplement la manière, et toute la validation et la logique métier que vous devez ajouter par vous-même
Roman Marusyk
N'êtes-vous pas d'accord que le code fonctionne et peut renvoyer une valeur d'en-tête?
Roman Marusyk le
1
Cela fonctionne très bien .. si "MyCustomID" fait partie de la demande de demande. Oui, toute validation doit être prise en charge
Prasad Kanaparthi
4
request.Headers.FirstOrDefault( x => x.Key == "MyCustomID" ).Value.FirstOrDefault()

variante moderne :)

Konstantin Salavatov
la source
Que faire si MyCustomID ne fait pas partie de la requête ... il renvoie une exception nulle.
Prasad Kanaparthi le