Bagaimana cara mengekstrak nilai header khusus di penangan pesan API Web?


150

Saat ini saya memiliki penangan pesan di layanan API Web saya yang menimpa 'SendAsync' sebagai berikut:

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

Di dalam kode ini saya perlu memeriksa nilai header permintaan kustom tambahan bernama MyCustomID. Masalahnya adalah ketika saya melakukan hal berikut:

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

... Saya mendapatkan pesan kesalahan berikut:

Tidak dapat menerapkan pengindeksan dengan [] ke ekspresi tipe 'System.Net.Http.Headers.HttpRequestHeaders'

Bagaimana saya bisa mengakses satu header permintaan khusus melalui instance HttpRequestMessage( Dokumentasi MSDN ) yang diteruskan ke metode yang diganti ini?


apa yang terjadi jika Anda menggunakan request.Headers.Get("MyCustomID");?
udidu

2
Tidak ada Get' on the tipe HttpRequestHeaders`. Pesan: "Tidak dapat menyelesaikan simbol 'Dapatkan'" diproduksi.
atconway

Jawaban:


253

Coba sesuatu seperti ini:

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

Ada juga metode TryGetValues ​​pada Header yang dapat Anda gunakan jika Anda tidak selalu dijamin memiliki akses ke header.


26
Cek nol untuk GetValues ​​tidak menayangkan nilai apa pun karena tidak akan pernah mengembalikan nol. Jika tajuk tidak ada, Anda akan mendapatkan InvalidOperationException. Anda perlu menggunakan TryGetHeaders jika memungkinkan tajuk mungkin tidak ada dalam permintaan dan memeriksa respons yang salah ATAU mencoba / menangkap panggilan GetValues ​​(tidak disarankan).
Drew Marsh

4
@Meminta permintaan.Headers.Single (h => h.Key == "Otorisasi"); Apalagi kode melakukan hal yang sama!
Elisabeth

17
Mengapa tidak adilvar id = request.Headers.GetValues("MyCustomID").FirstOrDefault();
Gaui

3
@ SaeedNeamati karena nilai header bukan satu-ke-satu. Anda dapat memiliki Some-Header: onedan kemudian Some-Header: twodalam permintaan yang sama. Beberapa bahasa secara diam-diam membuang "satu" tetapi itu tidak benar. Ada di RFC tapi saya terlalu malas untuk menemukannya sekarang.
Cory Mawhorter

1
Poin Saeed adalah valid, kegunaan penting dan use case yang paling umum di sini adalah untuk mengambil satu nilai untuk header permintaan. Anda masih dapat memiliki operasi GetValues ​​untuk mengambil beberapa nilai untuk header permintaan (yang akan digunakan orang), tetapi 99% dari waktu mereka ingin hanya mengambil satu nilai untuk header permintaan tertentu, dan itu harus menjadi satu kapal.
Justin

39

Baris di bawah ini throws exceptionjika kunci tidak ada.

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

Bekerja di sekitar:

Sertakan System.Linq;

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

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

17

Untuk memperluas jawaban Youssef, saya menulis metode ini berdasarkan kekhawatiran Drew tentang header yang tidak ada, karena saya mengalami situasi ini selama pengujian 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);
    }

Berikut ini contoh penggunaannya:

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

Lihat juga jawaban @ doguhan-uluca untuk solusi yang lebih umum.


1
Funcdan Actionmerupakan konstruksi tanda tangan delegasi umum yang dibangun dalam .NET 3.5 dan di atasnya. Saya akan senang mendiskusikan pertanyaan spesifik tentang metode ini, tetapi saya akan merekomendasikan untuk belajar tentang itu terlebih dahulu.
neontapir

1
@neontapir (dan lainnya) parameter kedua digunakan untuk memberikan nilai default jika kunci tidak ditemukan. Parameter ketiga digunakan untuk 'mengubah' nilai kembali menjadi jenis yang diinginkan yang juga menentukan jenis yang akan dikembalikan. Sebagai contoh, jika 'X-MyGuid' tidak ditemukan, parameter 2 lambda pada dasarnya memasok panduan standar sebagai string (karena akan diambil dari Header) dan parameter ketiga Guid. Parameter ketiga akan menerjemahkan nilai string yang ditemukan atau default menjadi GUID.
Mikee

@neontapir dari mana Permintaan berasal dari fungsi ini? (dan jika itu nol, bagaimana HttpRequestMessage () baru akan memiliki header? Tidakkah masuk akal untuk mengembalikan nilai default jika Permintaan nol?
mendel

Sudah dua tahun, tetapi jika saya ingat, yang baru HttpRequestMessagediinisialisasi dengan koleksi Header kosong, yang bukan nol. Fungsi ini akhirnya mengembalikan nilai default jika permintaan nol.
neontapir

@endel, neontapir Saya sudah mencoba menggunakan cuplikan di atas dan saya percaya "Permintaan" pada baris 2 dari badan metode harus berupa bidang pribadi di kelas yang berisi metode atau diteruskan sebagai parameter (dari tipe HttpRequestMessage) ke metode
Sudhanshu Mishra

12

Buat metode baru - ' Mengembalikan nilai HTTP Header individu ' dan memanggil metode ini dengan nilai kunci setiap kali Anda perlu mengakses beberapa Nilai kunci dari 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();
        }

Bagaimana jika MyCustomID bukan bagian dari permintaan .. itu mengembalikan null pengecualian.
Prasad Kanaparthi

10

Untuk memperluas lebih lanjut pada solusi @ neontapir, berikut ini adalah solusi yang lebih umum yang dapat diterapkan pada HttpRequestMessage atau HttpResponseMessage secara setara dan tidak memerlukan ekspresi atau fungsi kode tangan.

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

Penggunaan sampel:

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

Terlihat hebat, tetapi GetFirstHeaderValueOrDefaultmemiliki dua parameter, jadi ia mengeluh tentang kehilangan param ketika memanggil sebagai penggunaan sampel. var myValue = response.GetFirstHeaderValueOrDefault<int>("MyValue");Apakah saya kehilangan sesuatu?
Jeb50

Menambahkan kelas statis baru, menggantikan Response for Request. Dipanggil dari pengontrol API, seperti yang var myValue = myNameSpace.HttpRequestMessageExtension.GetFirstHeaderValueOrDefault<int>("productID");didapat. Tidak ada argumen yang diberikan yang sesuai dengan parameter formal yang diperlukan 'headerKey' dari 'HttpRequestMessageExtension.GetFirstHeaderValueOrDefault <T> (HttpRequestMessage, string)'
Jeb50

@ Jeb50 apakah Anda mendeklarasikan using HttpResponseMessageExtensionsfile yang Anda coba gunakan ekstensi ini?
Doguhan Uluca

4

Untuk ASP.Net Core ada solusi mudah jika ingin menggunakan param secara langsung dalam metode controller: Gunakan anotasi [FromHeader].

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

Info Tambahan: Dalam kasus saya "myParam" harus berupa string, int selalu 0.


4

Untuk ASP.NET Anda bisa mendapatkan header langsung dari parameter dalam metode controller menggunakan library / paket sederhana ini . Ini menyediakan [FromHeader]atribut seperti yang Anda miliki di ASP.NET Core :). Sebagai contoh:

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

Solusi satu baris

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

Bagaimana jika MyCustomID bukan bagian dari permintaan .. itu mengembalikan null pengecualian.
Prasad Kanaparthi

1
@ ParasKanaparthi maka Anda harus menggunakan jawaban lain seperti stackoverflow.com/a/25640256/4275342 . Anda melihat bahwa tidak ada pemeriksaan nol, jadi, apa requestitu null? Itu juga mungkin. Atau bagaimana jika MyCustomIDstring kosong atau tidak sama dengan foo? Tergantung pada konteksnya, jadi jawaban ini hanya menjelaskan caranya, dan semua validasi dan logika bisnis yang Anda perlu tambahkan sendiri
Roman Marusyk

Tidakkah Anda setuju bahwa kode tersebut berfungsi dan dapat mengembalikan nilai tajuk?
Roman Marusyk

1
Ini berfungsi dengan baik .. jika "MyCustomID" adalah bagian dari permintaan permintaan. Ya, semua validasi perlu dijaga
Prasad Kanaparthi

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

varian modern :)


Bagaimana jika MyCustomID bukan bagian dari permintaan .. itu mengembalikan null pengecualian.
Prasad Kanaparthi
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.