2015-02-17 15 views
6

Ich möchte eine Aktion meines Controllers basierend auf dem im Accept-Header angeforderten Medientyp auswählen.Ist es möglich, eine Aktion mit AttributeRouting in .NET MVC basierend auf dem Medientyp des Accept-Headers auszuwählen?

Zum Beispiel habe ich eine Ressource namens ein Thema. Seine zugewiesenen Route ist:

GET/Themen/{subjectId: int}

Normalerweise wird der Browser anfordert text/html, was in Ordnung ist. Der Standard Media Formatter behandelt das großartig.

Jetzt habe ich benutzerdefinierte Logik, die ich ausführen möchte, wenn auf die gleiche Route mit einem Akzeptieren-Header zugegriffen wird, der application/pdf als den akzeptierten Medientyp angibt.

Ich könnte einen benutzerdefinierten Medienformatierer erstellen, aber nach meinem Verständnis würde dies bedeuten, dass jede Route, die mit dem Accept-Header auf application/pdf festgelegt wurde, auch diesen Medienformatierer durchlaufen würde. Das ist inakzeptabel.

In Java gibt es eine Anmerkung @Produces genannt:

Die @Produces Anmerkung verwendet wird, um die MIME-Medientypen oder Darstellungen eine Ressource erzeugen kann angeben und zurück an den Client senden. Wenn @Produces auf Klassenebene angewendet wird, können alle Methoden in einer Ressource standardmäßig die angegebenen MIME-Typen erstellen. Wenn sie auf die Methodenebene angewendet wird, überschreibt die Annotation alle @Produces-Annotationen , die auf Klassenebene angewendet werden.

Dies würde erlauben Sie mir folgendes zu tun:

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

Es gibt keine Erzeugt Attribut in .NET, die ich natürlich nicht finden, so dass dies nicht funktioniert. Ich konnte auch kein ähnliches Attribut finden.

Ich könnte natürlich manuell den Header innerhalb des Körpers der Aktion überprüfen, und es zu einer anderen Aktion umleiten, aber das scheint bestenfalls hackish.

Gibt es einen Mechanismus in .NET 4.5, den ich verwenden kann, um das abzurufen, das ich übersehen oder vermisse?

(Ich verwende MVC 5.2.2 von NuGet Repository)

+0

Es scheint möglich, mit einem benutzerdefinierten Controller-Selektor zu verwenden: http: // stackoverflow.com/questions/25941590/asp-net-mvc-intercepting-routing-und-Weiterleitung an verschiedene Aktionen-Routen – mostruash

+0

@mostruash Sieht nicht aus wie ein Controller-Selektor ist, was ich will, aber es hat mich angefangen zu suchen in Aktionsselektoren, die ähnlich sind. Ich werde graben und sehen, ob ich es herausfinden kann. – crush

+0

Ich denke jetzt, dass ich möglicherweise eine Route Einschränkung auf diese verwenden könnte, aber ich bin mir noch nicht sicher. – crush

Antwort

5

Nach einer Weile um das Internet suchen, kam ich auf die Idee, dass dies am besten durch die Schaffung eines ActionMethodSelectorAttribute erreicht werden.

Das Folgende ist eine sehr naive, First-Pass-Implementierung eines ProducesAttribute, die ich mit der eventuellen Absicht schrieb Java nachahmen Erzeugt Anmerkung:

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

Es soll mit Attribut Routing verwendet werden soll, und kann wie folgt angewendet werden:

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