26

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.

Nathan
  • 7,243
  • 6
  • 46
  • 67
Doug McClean
  • 14,047
  • 6
  • 44
  • 69

7 Answers7

9

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

Hank Gay
  • 67,855
  • 33
  • 155
  • 219
7

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:

LINQPad dump of results

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

Mark Bell
  • 27,980
  • 22
  • 113
  • 143
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.

Jrgns
  • 23,760
  • 18
  • 69
  • 76
1

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

Adrian Gschwend
  • 644
  • 6
  • 16
1

Found another implementation in php here

1

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

    }

}    
goofballLogic
  • 34,243
  • 8
  • 40
  • 53
0

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.

Markus Jarderot
  • 83,508
  • 19
  • 134
  • 137