Come estrarre il valore dell'intestazione personalizzato nel gestore dei messaggi dell'API Web?


150

Al momento ho un gestore di messaggi nel mio servizio API Web che sovrascrive "SendAsync" come segue:

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

All'interno di questo codice ho bisogno di ispezionare un valore di intestazione di richiesta aggiunto personalizzato denominato MyCustomID. Il problema è quando eseguo le seguenti operazioni:

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

... Ricevo il seguente messaggio di errore:

Impossibile applicare l'indicizzazione con [] a un'espressione di tipo "System.Net.Http.Headers.HttpRequestHeaders"

Come posso accedere a una singola intestazione di richiesta personalizzata tramite l' istanza HttpRequestMessage( Documentazione MSDN ) passata a questo metodo ignorato?


cosa succede se stai usando request.Headers.Get("MyCustomID");?
udidu,

2
Non esiste alcun Get' on the tipo di HttpRequestHeaders. Viene visualizzato il messaggio "Impossibile risolvere il simbolo 'Ottieni'".
atconway,

Risposte:


253

Prova qualcosa del genere:

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

C'è anche un metodo TryGetValues ​​sulle intestazioni che puoi usare se non sei sempre sicuro di avere accesso all'intestazione.


26
Il controllo null per GetValues ​​non serve alcun valore in quanto non restituirà mai null. Se l'intestazione non esiste otterrai un InvalidOperationException. È necessario utilizzare TryGetHeaders se è possibile che l'intestazione potrebbe non esistere nella richiesta e verificare la presenza di una risposta errata O provare / intercettare la chiamata GetValues ​​(non consigliato).
Ha disegnato Marsh il

4
@Drew request.Headers.Single (h => h.Key == "Autorizzazione"); Molto meno codice facendo lo stesso!
Elisabeth,

17
Perché non solovar id = request.Headers.GetValues("MyCustomID").FirstOrDefault();
Gaui,

3
@SaeedNeamati perché i valori dell'intestazione non sono uno a uno. Puoi avere Some-Header: onee quindi Some-Header: twonella stessa richiesta. Alcune lingue scartano silenziosamente "una" ma non è corretto. È nella RFC ma sono troppo pigro per trovarlo ora.
Cory Mawhorter,

1
Il punto di Saeed è valido, l'usabilità è importante e il caso d'uso più comune qui è recuperare un valore per un'intestazione di richiesta. Puoi ancora avere un'operazione GetValues ​​per recuperare più valori per un'intestazione di richiesta (che le persone raramente useranno), ma il 99% delle volte vorranno solo recuperare un valore per un'intestazione di richiesta specifica, e quello dovrebbe essere uno rivestimento.
Giustino,

39

La riga seguente throws exceptionse la chiave non esiste.

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

Aggirare:

Includi System.Linq;

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

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

17

Per espandere la risposta di Youssef, ho scritto questo metodo basando le preoccupazioni di Drew sull'intestazione inesistente, perché mi sono imbattuto in questa situazione durante i test delle unità.

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

Ecco un esempio di utilizzo:

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

Dai anche un'occhiata alla risposta di @ doguhan-uluca per una soluzione più generale.


1
Funce Actionsono costrutti generici di firma delegata integrati in .NET 3.5 e versioni successive. Sarei felice di discutere domande specifiche sul metodo, ma consiglierei prima di conoscerli.
Neontapir,

1
@neontapir (e altri) il secondo parametro viene utilizzato per fornire un valore predefinito se la chiave non viene trovata. Il terzo parametro viene utilizzato per "trasformare" il valore restituito in modo che sia del tipo desiderato, che specifica anche il tipo da restituire. Nell'esempio, se 'X-MyGuid' non viene trovato, il parametro 2 lambda fornisce sostanzialmente una guida predefinita come stringa (come sarebbe stata recuperata dall'intestazione) e il terzo parametro Guid.Parse tradurrà il valore della stringa trovata o predefinita in un GUID.
Mikee,

@neontapir da dove proviene la richiesta in questa funzione? (e se è null come avrà un nuovo HttpRequestMessage () con delle intestazioni? Non ha senso restituire il valore predefinito se Request è null?
mendel

Sono passati due anni, ma se ricordo, un nuovo HttpRequestMessageviene inizializzato con una raccolta di intestazioni vuota, che non è nulla. Questa funzione finisce per restituire il valore predefinito se la richiesta è nulla.
neontapir,

@mendel, neontapir Ho provato a utilizzare lo snippet di cui sopra e credo che la "Richiesta" nella riga 2 del corpo del metodo debba essere un campo privato nella classe contenente il metodo o essere passata come parametro (di tipo HttpRequestMessage) a il metodo
Sudhanshu Mishra il

12

Crea un nuovo metodo: " Restituisce un singolo valore di intestazione HTTP " e chiama questo metodo con valore chiave ogni volta che devi accedere a più valori chiave da 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();
        }

Cosa succede se MyCustomID non fa parte della richiesta .. restituisce un'eccezione nulla.
Prasad Kanaparthi,

10

Per espandere ulteriormente la soluzione di @ neontapir, ecco una soluzione più generica che può essere applicata allo stesso modo a HttpRequestMessage o HttpResponseMessage e non richiede espressioni o funzioni codificate a mano.

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

Esempio di utilizzo:

var myValue = response.GetFirstHeaderValueOrDefault<int>("MyValue");

Sembra fantastico, ma GetFirstHeaderValueOrDefaultha due parametri, quindi si lamenta del parametro mancante quando si chiama come esempio di utilizzo var myValue = response.GetFirstHeaderValueOrDefault<int>("MyValue");Mi sto perdendo qualcosa?
Jeb50,

Aggiunta la nuova classe statica, sostituito Response for Request. Chiamato dal controller API, come var myValue = myNameSpace.HttpRequestMessageExtension.GetFirstHeaderValueOrDefault<int>("productID");ottenuto Non sono stati forniti argomenti che corrispondono al parametro formale richiesto 'headerKey' di 'HttpRequestMessageExtension.GetFirstHeaderValueOrDefault <T> (HttpRequestMessage, string)'
Jeb50

@ Jeb50 stai dichiarando using HttpResponseMessageExtensionssul file che stai tentando di utilizzare questa estensione?
Doguhan Uluca,

4

Per ASP.Net Core esiste una soluzione semplice se si desidera utilizzare il parametro direttamente nel metodo del controller: utilizzare l'annotazione [FromHeader].

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

Ulteriori informazioni: Nel mio caso "myParam" doveva essere una stringa, int era sempre 0.


4

Per ASP.NET è possibile ottenere l'intestazione direttamente dal parametro nel metodo controller utilizzando questa semplice libreria / pacchetto . Fornisce un [FromHeader]attributo proprio come in ASP.NET Core :). Per esempio:

    ...
    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}");
            ...
        }
    }

4

Una soluzione di linea

var id = request.Headers.GetValues("MyCustomID").FirstOrDefault();

Cosa succede se MyCustomID non fa parte della richiesta .. restituisce un'eccezione nulla.
Prasad Kanaparthi,

1
@PrasadKanaparthi allora dovresti usare un'altra risposta come stackoverflow.com/a/25640256/4275342 . Si vede che non v'è alcun controllo nullo, quindi, ciò che è requestè null? È anche possibile. O cosa succede se MyCustomIDuna stringa vuota o non è uguale a foo? Dipende dal contesto, quindi questa risposta descrive solo il modo e tutta la convalida e la logica aziendale che devi aggiungere da solo
Roman Marusyk,

Non sei d'accordo che il codice funziona e può restituire il valore dell'intestazione?
Roman Marusyk,

1
Funziona bene .. se "MyCustomID" fa parte della richiesta di richiesta. Sì, tutte le convalide devono essere prese in considerazione
Prasad Kanaparthi il

4
request.Headers.FirstOrDefault( x => x.Key == "MyCustomID" ).Value.FirstOrDefault()

variante moderna :)


Cosa succede se MyCustomID non fa parte della richiesta .. restituisce un'eccezione nulla.
Prasad Kanaparthi,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.