Jak wyodrębnić niestandardową wartość nagłówka w programie obsługi komunikatów interfejsu API sieci Web?

150

Obecnie mam program obsługi komunikatów w mojej usłudze interfejsu API sieci Web, która zastępuje „SendAsync” w następujący sposób:

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

W tym kodzie muszę sprawdzić niestandardową wartość nagłówka żądania o nazwie MyCustomID . Problem pojawia się, gdy wykonuję następujące czynności:

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

... pojawia się następujący komunikat o błędzie:

Nie można zastosować indeksowania z [] do wyrażenia typu „System.Net.Http.Headers.HttpRequestHeaders”

Jak mogę uzyskać dostęp do pojedynczego niestandardowego nagłówka żądania za pośrednictwem wystąpienia HttpRequestMessage( Dokumentacja MSDN ) przekazanego do tej zastąpionej metody?

atconway
źródło
co się stanie, jeśli używasz request.Headers.Get("MyCustomID");?
udidu
2
Nie ma Get' on the typu HttpRequestHeaders. Wyświetlany jest komunikat: „Nie można rozwiązać symbolu„ Pobierz ””.
atconway,

Odpowiedzi:

252

Spróbuj czegoś takiego:

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

Istnieje również metoda TryGetValues ​​w nagłówkach, której możesz użyć, jeśli nie zawsze masz gwarancję dostępu do nagłówka.

Youssef Moussaoui
źródło
26
Sprawdzanie wartości null dla GetValues ​​nie podaje żadnej wartości, ponieważ nigdy nie zwróci wartości null. Jeśli nagłówek nie istnieje, otrzymasz InvalidOperationException. Musisz użyć TryGetHeaders, jeśli to możliwe, że nagłówek może nie istnieć w żądaniu i sprawdzić, czy nie ma fałszywej odpowiedzi lub spróbować / złapać wywołanie GetValues ​​(niezalecane).
Drew Marsh
4
@Drew request.Headers.Single (h => h.Key == "Autoryzacja"); Znacznie mniej kodu robi to samo!
Elisabeth
17
Dlaczego nie tylkovar id = request.Headers.GetValues("MyCustomID").FirstOrDefault();
Gaui
3
@SaeedNeamati, ponieważ wartości nagłówka nie są jeden do jednego. Możesz mieć, Some-Header: onea następnie Some-Header: twow tej samej prośbie. Niektóre języki po cichu odrzucają „jedynkę”, ale to nieprawda. Jest w RFC, ale jestem zbyt leniwy, żeby go teraz znaleźć.
Cory Mawhorter
1
Punkt Saeeda jest ważny, użyteczność jest ważna, a najczęstszym przypadkiem użycia jest tutaj pobranie jednej wartości dla nagłówka żądania. Nadal możesz mieć operację GetValues ​​do pobierania wielu wartości dla nagłówka żądania (których ludzie będą używać), ale w 99% przypadków będą chcieli po prostu pobrać jedną wartość dla określonego nagłówka żądania, a to powinno być jedno liniowiec.
Justin
39

Wiersz poniżej, throws exceptionjeśli klucz nie istnieje.

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

Obejść:

Uwzględnij System.Linq;

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

     if (request.Headers.TryGetValues("MyCustomID", out headerValues))
     {
         userId = headerValues.FirstOrDefault();
     }           
SharpCoder
źródło
17

Aby rozwinąć odpowiedź Youssefa, napisałem tę metodę w oparciu o obawy Drew dotyczące nieistnienia nagłówka, ponieważ napotkałem taką sytuację podczas testów jednostkowych.

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

Oto przykład użycia:

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

Spójrz także na odpowiedź @ doguhan-uluca, aby uzyskać bardziej ogólne rozwiązanie.

neontapir
źródło
1
Funci Actionsą ogólnymi konstrukcjami podpisów delegatów wbudowanymi w .NET 3.5 i nowsze. Chętnie omówię konkretne pytania dotyczące metody, ale polecam najpierw poznać te pytania.
neontapir
1
@neontapir (i inne) drugi parametr jest używany do podania wartości domyślnej, jeśli klucz nie zostanie znaleziony. Trzeci parametr służy do „przekształcenia” zwracanej wartości na żądany typ, który również określa typ, który ma zostać zwrócony. W przykładzie, jeśli nie znaleziono `` X-MyGuid '', parametr 2 lambda w zasadzie dostarcza domyślny identyfikator guid jako ciąg (tak jak zostałby pobrany z nagłówka), a trzeci parametr Guid.Parse przetłumaczy znalezioną lub domyślną wartość ciągu do identyfikatora GUID.
Mikee,
@neontapir, skąd pochodzi żądanie w tej funkcji? (a jeśli ma wartość null, w jaki sposób nowa HttpRequestMessage () będzie miała jakieś nagłówki? Czy nie ma sensu zwracać wartości domyślnej, jeśli Request jest null?
mendel
Minęły dwa lata, ale jeśli sobie przypominam, nowa HttpRequestMessagejest inicjowana z pustą kolekcją Headers, która nie jest null. Ta funkcja w końcu zwraca wartość domyślną, jeśli żądanie ma wartość null.
neontapir
@mendel, neontapir Próbowałem użyć powyższego fragmentu i uważam, że „Request” w linii 2 treści metody powinno albo być polem prywatnym w klasie zawierającej metodę, albo zostać przekazane jako parametr (typu HttpRequestMessage) do metoda
Sudhanshu Mishra
12

Utwórz nową metodę - „ Zwraca indywidualną wartość nagłówka HTTP ” i wywołuj tę metodę z wartością klucza za każdym razem, gdy chcesz uzyskać dostęp do wielu wartości klucza z 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
źródło
Co jeśli MyCustomID nie jest częścią żądania… zwraca wyjątek o wartości null.
Prasad Kanaparthi
10

Aby jeszcze bardziej rozwinąć rozwiązanie @ neontapir, oto bardziej ogólne rozwiązanie, które można zastosować w równym stopniu do HttpRequestMessage lub HttpResponseMessage i nie wymaga ręcznie zakodowanych wyrażeń ani funkcji.

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

Przykładowe użycie:

var myValue = response.GetFirstHeaderValueOrDefault<int>("MyValue");
Doguhan Uluca
źródło
Wygląda świetnie, ale GetFirstHeaderValueOrDefaultma dwa parametry, więc narzeka na brakujące parametry podczas wywoływania jako użycie próbki. var myValue = response.GetFirstHeaderValueOrDefault<int>("MyValue");Czy czegoś mi brakuje?
Jeb50
Dodano nową klasę statyczną, zastąpiono odpowiedź na żądanie. Wywołane z kontrolera API, jak var myValue = myNameSpace.HttpRequestMessageExtension.GetFirstHeaderValueOrDefault<int>("productID");otrzymano Nie podano argumentu, który odpowiada wymaganemu parametrowi formalnemu „headerKey” z „HttpRequestMessageExtension.GetFirstHeaderValueOrDefault <T> (HttpRequestMessage, string)”
Jeb50
@ Jeb50 czy deklarujesz using HttpResponseMessageExtensionsw pliku, którego próbujesz użyć tego rozszerzenia?
Doguhan Uluca
4

W przypadku ASP.Net Core istnieje proste rozwiązanie, jeśli chcesz użyć parametru bezpośrednio w metodzie kontrolera: użyj adnotacji [FromHeader].

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

Dodatkowe informacje: W moim przypadku „myParam” musiało być ciągiem znaków, int zawsze wynosił 0.

Reiner
źródło
4

W przypadku ASP.NET można uzyskać nagłówek bezpośrednio z parametru w metodzie kontrolera za pomocą tej prostej biblioteki / pakietu . Zapewnia [FromHeader]atrybut, tak jak w ASP.NET Core :). Na przykład:

    ...
    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
źródło
4

Rozwiązanie jednoprzewodowe

var id = request.Headers.GetValues("MyCustomID").FirstOrDefault();
Roman Marusyk
źródło
Co jeśli MyCustomID nie jest częścią żądania… zwraca wyjątek o wartości null.
Prasad Kanaparthi
1
@PrasadKanaparthi, powinieneś użyć innej odpowiedzi, takiej jak stackoverflow.com/a/25640256/4275342 . Widzisz, że nie ma żadnego czeku zerowego, więc co to requestjest null? Jest to również możliwe. A co, jeśli MyCustomIDjest pustym łańcuchem lub nie równa się foo? To zależy od kontekstu, więc ta odpowiedź po prostu opisuje sposób i całą weryfikację i logikę biznesową, które musisz dodać samodzielnie
Roman Marusyk
Czy nie zgadzasz się, że kod działa i może zwrócić wartość nagłówka?
Roman Marusyk
1
Działa dobrze… jeśli „MyCustomID” jest częścią żądania żądania. Tak, trzeba zadbać o wszystkie walidacje
Prasad Kanaparthi
4
request.Headers.FirstOrDefault( x => x.Key == "MyCustomID" ).Value.FirstOrDefault()

wariant nowoczesny :)

Konstantin Salavatov
źródło
Co jeśli MyCustomID nie jest częścią żądania… zwraca wyjątek o wartości null.
Prasad Kanaparthi