是否可以根据Accept标头的媒体类型在.NET MVC中选择具有AttributeRouting的Action?

cru*_*ush 6 asp.net-mvc .net-4.5 attributerouting asp.net-mvc-5

我想根据Accept标头中请求的媒体类型选择我的Controller的Action.

例如,我有一个名为subject的资源.其分配的路线是:

GET /臣//subjectId:int}

通常,浏览器正在请求text/html,这很好.默认的Media Formatter可以处理这个问题.

现在,我有一个自定义逻辑,我希望在访问同一路径时使用一个接受标头指定application/pdf为接受的媒体类型.

我可以创建一个自定义媒体格式化程序,但是,据我所知,这意味着任何设置为Accept标头的请求路径application/pdf也将通过此媒体格式化程序运行.这是无法接受的.

在Java中,有一个名为的注释@Produces:

@Produces注释用于指定资源可以生成并发送回客户端的MIME媒体类型或表示.如果在类级别应用@Produces,则资源中的所有方法都可以默认生成指定的MIME类型.如果在方法级别应用,则注释将覆盖在类级别应用的任何@Produces注释.

这将允许我执行以下操作:

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.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,我找不到.NET中的Produces属性,所以这不起作用.我也找不到类似的属性.

我当然可以手动检查动作正文中的标题,并将其重定向到另一个动作,但这似乎充其量只是hackish.

.NET 4.5中是否有一种机制可以用来解决我忽略或丢失的问题?

(我正在使用NuGet存储库中的MVC 5.2.2)

cru*_*ush 5

在互联网上搜索了一段时间之后,我想出了这样的想法,即最好通过创建一个ActionMethodSelectorAttribute.

以下是ProducesAttribute我编写的一个非常幼稚的首次实现,其最终目的是模仿Java的Produces注释:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它适用于属性路由,可以按如下方式应用:

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.
    }
}
Run Code Online (Sandbox Code Playgroud)