Làm cách nào để trích xuất giá trị tiêu đề tùy chỉnh trong trình xử lý thông báo API Web?


150

Tôi hiện có một trình xử lý tin nhắn trong dịch vụ API Web của mình ghi đè 'SendAsync' như sau:

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

Trong mã này, tôi cần kiểm tra một giá trị tiêu đề yêu cầu được thêm tùy chỉnh có tên MyCustomID. Vấn đề là khi tôi làm như sau:

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

... Tôi nhận được thông báo lỗi sau:

Không thể áp dụng lập chỉ mục với [] cho một biểu thức kiểu 'System.Net.Http.Headers.HttpRequestHeaders'

Làm thế nào tôi có thể truy cập vào một đơn tiêu đề yêu cầu tùy chỉnh qua HttpRequestMessage( Tài liệu MSDN ) ví dụ thông qua vào phương pháp ghi đè này?


Điều gì xảy ra nếu bạn đang sử dụng request.Headers.Get("MyCustomID");?
udidu

2
Không có Get' on the loại httpRequestHeaders`. Thông báo: "Không thể giải quyết biểu tượng 'Nhận'" được tạo.
atconway

Câu trả lời:


252

Hãy thử một cái gì đó như thế này:

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

Ngoài ra còn có phương pháp TryGetValues ​​trên Tiêu đề mà bạn có thể sử dụng nếu không phải lúc nào bạn cũng được đảm bảo có quyền truy cập vào tiêu đề.


26
Kiểm tra null cho GetValues ​​không phục vụ bất kỳ giá trị nào vì nó sẽ không bao giờ trả về null. Nếu tiêu đề không tồn tại, bạn sẽ nhận được UnlimitedOperationException. Bạn cần sử dụng TryGetHeaders nếu có thể tiêu đề có thể không tồn tại trong yêu cầu và kiểm tra phản hồi sai HOẶC thử / bắt xung quanh lệnh gọi GetValues ​​(không được khuyến nghị).
Drew Marsh

4
@Drew request.Headers.Single (h => h.Key == "Ủy quyền"); Ít mã hơn làm như vậy!
Elisabeth

17
Tại sao không chỉvar id = request.Headers.GetValues("MyCustomID").FirstOrDefault();
Gaui

3
@SaeedNeamati vì các giá trị tiêu đề không phải là một đối một. Bạn có thể có Some-Header: onevà sau đó Some-Header: twotrong cùng một yêu cầu. Một số ngôn ngữ âm thầm loại bỏ "một" nhưng điều đó không chính xác. Đó là trong RFC nhưng tôi quá lười để tìm thấy nó bây giờ.
Cory Mawhorter

1
Điểm của Saeed là hợp lệ, khả năng sử dụng là quan trọng và trường hợp sử dụng phổ biến nhất ở đây là truy xuất một giá trị cho tiêu đề yêu cầu. Bạn vẫn có thể có thao tác GetValues ​​để truy xuất nhiều giá trị cho tiêu đề yêu cầu (mọi người sẽ thường xuyên sử dụng), nhưng 99% thời gian họ sẽ chỉ muốn truy xuất một giá trị cho tiêu đề yêu cầu cụ thể và đó phải là một giá trị lót.
Justin

39

Dòng bên dưới throws exceptionnếu khóa không tồn tại.

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

Làm việc xung quanh:

Bao gồm System.Linq;

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

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

17

Để mở rộng câu trả lời của Youssef, tôi đã viết phương pháp này dựa trên mối quan tâm của Drew về tiêu đề không tồn tại, bởi vì tôi đã gặp phải tình huống này trong quá trình thử nghiệm đơn vị.

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

Đây là một ví dụ sử dụng:

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

Ngoài ra, hãy xem câu trả lời của @ doguhan-uluca để có giải pháp tổng quát hơn.


1
FuncActionlà các cấu trúc chữ ký đại biểu chung được tích hợp trong .NET 3.5 trở lên. Tôi rất vui lòng thảo luận các câu hỏi cụ thể về phương pháp này, nhưng tôi khuyên bạn nên tìm hiểu về những câu hỏi đó trước tiên.
neontapir

1
@neontapir (và những người khác) tham số thứ hai được sử dụng để cung cấp giá trị mặc định nếu không tìm thấy khóa. Tham số thứ ba được sử dụng để 'biến đổi' giá trị trả về là loại mong muốn cũng chỉ định loại được trả về. Theo ví dụ, nếu không tìm thấy 'X-MyGuid', tham số 2 lambda về cơ bản cung cấp một hướng dẫn mặc định dưới dạng một chuỗi (như nó đã được lấy từ Header) và tham số thứ ba Guid.Pude sẽ dịch giá trị chuỗi tìm thấy hoặc mặc định vào một HƯỚNG DẪN.
Mikee

@neontapir Yêu cầu đến từ đâu trong chức năng này? (và nếu không, làm thế nào một httpRequestMessage () mới có bất kỳ tiêu đề nào? không có nghĩa là chỉ trả về giá trị mặc định nếu Yêu cầu là null?
mendel

Đã hai năm, nhưng nếu tôi nhớ lại, một cái mới HttpRequestMessageđược khởi tạo với bộ sưu tập Headers trống, không phải là null. Hàm này cuối cùng trả về giá trị mặc định nếu yêu cầu là null.
neontapir

@mendel, neontapir Tôi đã thử sử dụng đoạn mã trên và tôi tin rằng "Yêu cầu" trên dòng 2 của phần thân phương thức phải là một trường riêng trong lớp có chứa phương thức hoặc được truyền dưới dạng tham số (loại httpRequestMessage) cho phương pháp
Sudhanshu Mishra

12

Tạo một phương thức mới - ' Trả về một giá trị Tiêu đề HTTP riêng lẻ ' và gọi phương thức này với giá trị khóa mọi lúc khi bạn cần truy cập nhiều Giá trị khóa từ 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();
        }

Điều gì xảy ra nếu MyCustomID không phải là một phần của yêu cầu .. nó trả về ngoại lệ null.
Prasad Kanaparthi

10

Để tiếp tục mở rộng về giải pháp của @ neontapir, đây là một giải pháp chung hơn có thể áp dụng cho httpRequestMessage hoặc HttpResponseMessage và không yêu cầu các biểu thức hoặc chức năng được mã hóa bằng tay.

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

Sử dụng mẫu:

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

Trông thật tuyệt, nhưng GetFirstHeaderValueOrDefaultcó hai tham số, vì vậy nó phàn nàn về việc thiếu param khi gọi là sử dụng mẫu var myValue = response.GetFirstHeaderValueOrDefault<int>("MyValue");Tôi có thiếu gì không?
Jeb50

Đã thêm lớp tĩnh mới, thay thế Phản hồi cho Yêu cầu. Được gọi từ bộ điều khiển API, như var myValue = myNameSpace.HttpRequestMessageExtension.GetFirstHeaderValueOrDefault<int>("productID");đã nhận Không có đối số nào được đưa ra tương ứng với tham số chính thức bắt buộc 'headerKey' của 'HttpRequestMessageExtension.GetFirstHeaderValueOrDefault <T> (HttpRequestMessage, chuỗi)'
Jeb50

@ Jeb50 bạn đang khai báo using HttpResponseMessageExtensionstrên tệp bạn đang cố sử dụng tiện ích mở rộng này phải không?
Doguhan Uluca

4

Đối với ASP.Net Core, có một giải pháp dễ dàng nếu muốn sử dụng trực tiếp param trong phương thức của bộ điều khiển: Sử dụng chú thích [FromHeader].

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

Thông tin bổ sung: Trong trường hợp của tôi, "myParam" phải là một chuỗi, int luôn là 0.


4

Đối với ASP.NET, bạn có thể lấy tiêu đề trực tiếp từ tham số trong phương thức điều khiển bằng thư viện / gói đơn giản này . Nó cung cấp một [FromHeader]thuộc tính giống như bạn có trong ASP.NET Core :). Ví dụ:

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

Giải pháp một dòng

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

Điều gì xảy ra nếu MyCustomID không phải là một phần của yêu cầu .. nó trả về ngoại lệ null.
Prasad Kanaparthi

1
@PrasadKanaparthi thì bạn nên sử dụng một câu trả lời khác như stackoverflow.com/a/25640256/4275342 . Bạn thấy rằng không có bất kỳ kiểm tra null, vì vậy, đó requestnullgì? Nó cũng có thể. Hoặc nếu MyCustomIDmột chuỗi rỗng hoặc không bằng foo? Nó phụ thuộc vào ngữ cảnh, vì vậy câu trả lời này chỉ mô tả cách thức, và tất cả xác thực và logic kinh doanh bạn cần thêm bằng cách riêng của mình
Roman Marusyk

Bạn không đồng ý rằng mã đang hoạt động và có thể trả về giá trị tiêu đề?
Roman Marusyk

1
Nó hoạt động tốt .. nếu "MyCustomID" là một phần của yêu cầu. Có, tất cả xác nhận cần phải được chăm sóc
Prasad Kanaparthi

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

biến thể hiện đại :)


Điều gì xảy ra nếu MyCustomID không phải là một phần của yêu cầu .. nó trả về ngoại lệ null.
Prasad Kanaparthi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.