Is it possible to select an action with AttributeRouting in .NET MVC based on the Media Type of the Accept header?

I want to select my controller action based on the type of media requested in the Accept header.

For example, I have a resource called a subject. Assigned route:

GET / subject / {subjectId: int}

Usually the browser requests text/html , and that’s fine. By default, Media Formatter handles this wonderful version.

Now I have custom logic that I want to execute when the same route is accessed using the accept header, indicating application/pdf as the accepted media type.

I could create my own Media Formatter, but, in my opinion, this would mean that any route that is requested with the Accept header set in application/pdf will also run through this Media Formatter. This is unacceptable.

There is an annotation in Java called @Produces :

@Produces annotations are used to indicate the types of MIME media or presentation that a resource can produce and send back to the client. If @Produces is applied at the class level, all methods in the resource can return the specified default MIME types. If applied at the method level, the annotation overrides any annotations @Produces applies at the class level.

This will allow me to do the following:

 namespace MyNamespace { [RoutePrefix("subjects")] public class SubjectsController : Controller { [Route("{subjectId:int}")] [HttpGet] public ActionResult GetSubject(int subjectId) { } [Route("{subjectId:int}")] [HttpGet] [Produces("application/pdf")] public ActionResult GetSubjectAsPdf(int subjectId) { //Run my custom logic here to generate a PDF. } } } 

There is no Produces attribute in .NET that I can find, so this will not work. I also could not find a similar attribute.

I could, of course, manually check the header inside the body of the action and redirect it to another action, but this seems hacker at best.

Is there a mechanism in .NET 4.5 that I can use to remove this that I am missing or missing?

(I am using MVC 5.2.2 from the NuGet repository)

+6
source share
1 answer

After doing some searching on the Internet, I came up with the idea that this is best done by creating an ActionMethodSelectorAttribute .

The following is a very naive, first version of the ProducesAttribute implementation that I wrote with the possible intention of emulating Java Produces annotations:

 namespace YourNamespace { using System; using System.Collections.Generic; using System.Net; using System.Net.Mime; using System.Web.Mvc; [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public sealed class ProducesAttribute : ActionMethodSelectorAttribute { private readonly ISet<ContentType> acceptableMimeTypes; public ProducesAttribute(params string[] acceptableMimeTypes) { this.acceptableMimeTypes = new HashSet<ContentType>(); foreach (string acceptableMimeType in acceptableMimeTypes) this.acceptableMimeTypes.Add(new ContentType(acceptableMimeType)); } public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo) { string acceptHeader = controllerContext.RequestContext.HttpContext.Request.Headers[HttpRequestHeader.Accept.ToString()]; string[] headerMimeTypes = acceptHeader.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries); foreach (var headerMimeType in headerMimeTypes) { if (this.acceptableMimeTypes.Contains(new ContentType(headerMimeType))) return true; } return false; } } } 

It is intended for use with attribute routing and can be used as follows:

 public sealed class MyController : Controller { [Route("subjects/{subjectId:int}")] //My route [Produces("application/pdf")] public ActionResult GetSubjectAsPdf(int subjectId) { //Here you would return the PDF representation. } [Route("subjects/{subjectId:int}")] public ActionResult GetSubject(int subjectId) { //Would handle all other routes. } } 
+5
source

Source: https://habr.com/ru/post/982646/


All Articles