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



我想根据Accept标头中请求的媒体类型选择控制器的操作。

例如,我有一个名为主题的资源。其指定路线为:

GET/subjects/{subjectId:int}

通常情况下,浏览器正在请求text/html,这很好。默认的媒体格式化程序可以处理这个问题。

现在,当使用指定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.
        }
    }
}

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

当然,我可以手动检查动作主体中的头部,并将其重定向到另一个动作,但这充其量看起来很粗糙。

.NET 4.5中是否有一种机制可以用来实现我忽略或遗漏的功能

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

在互联网上搜索了一段时间后,我想到最好通过创建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;
        }
    }
}

它旨在与属性路由一起使用,可以按如下方式应用:

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

最新更新