Parse Accept Header

2019-03-17 10:06发布

问题:

Does anyone have any suggestions (or a regular expression) for parsing the HTTP Accept header?

I am trying to do some content-type negotiation in ASP.NET MVC. There doesn't seem to be a built in way (which is fine, because there are a lot of schools of thought here), but the parsing is not entirely trivial and I would rather not re-invent the wheel if someone has already done it well and is willing to share.

回答1:

Have you seen this article? It gives a pretty comprehensive implementation for parsing the Accept header and subsequently doing something useful with it.



回答2:

As of .NET 4.5 (I think—Microsoft have made info on framework versions < 4.5 rather obscure these days), you can use one of the the built in parsers from System.Net.Http.Headers:

public IOrderedEnumerable<MediaTypeWithQualityHeaderValue> GetMediaTypes(string headerValue) =>
    headerValue?.Split(',')
        .Select(MediaTypeWithQualityHeaderValue.Parse)
        .OrderByDescending(mt => mt.Quality.GetValueOrDefault(1));

Then you can do something like this:

var headerValue = "application/json, text/javascript, */*; q=0.01";
var mediaTypes = GetMediaTypes(headerValue);

Giving you a nice list of all the media types, where the preferred option is the first item. Here's a LINQPad Dump of the mediaTypes result from the example:

Hat tip to this answer, for getting me on the right track.



回答3:

I've written a parser in PHP. It's not complex, but it will give you an array of mime types in order of preference.



回答4:

Found another implementation in php here



回答5:

After reading the xml.com article I decided to not write a function for the Accept header myself ;)

Fortunately the article points to a good library: https://code.google.com/p/mimeparse/ - in my case I need it as a Node.js module: https://github.com/kriskowal/mimeparse



回答6:

The RFC is quite complex. If the regex where to follow these rules to the letter, it would become several lines long.

If you already have the Accept-header, and ignore the quotes and the parameters, you could do something like this to match each pair:

/([^()<>@,;:\\"\/[\]?={} \t]+)\/([^()<>@,;:\\"\/[\]?={} \t]+)/

* is included in the character class, so it does not need any special case in the regex.



回答7:

Building on https://stackoverflow.com/a/49011308/275501 from https://stackoverflow.com/users/43140/mark-bell above:

public class MyController : Controller
{

    [HttpGet]
    [Route("/test")]
    public ActionResult Index() {

        // does this request accept HTML?
        var acceptsHTML = IsAcceptable("text/html");
        var model = FetchViewModel();
        return acceptsHTML ? (ActionResult) View(model) : Ok(model);

    }

    private bool IsAcceptable(string mediaType) =>
        Request.Headers["Accept"].Any(headerValue =>
            !string.IsNullOrWhiteSpace(headerValue) &&
            headerValue.Split(",").Any(segment => MediaTypeHeaderValue.Parse(segment).MediaType == mediaType));

    private object FetchViewModel() {

        return new { Description = "To be completed" };

    }

}