ASP.NET Core web api 基于接受的动作选择 header

ASP.NET Core web api action selection based on Accept header

我想 return 基于请求的接受 header 对同一特征(实体列表)的两个不同格式的响应,它是针对 "json" 和"html" 个请求。

asp.net 核心是否支持 select 基于 Accept Header 请求的相同路由的不同操作?

if(Request.Headers["Content-Type"] == "application/json")
{
    return OK(json);
}
else
{
    return View();
}

可以吗?

我深入研究了 .net 核心源代码并寻找其他具有类似行为的属性,例如 Microsoft.AspNetCore.Mvc.HttpGetMicrosoft.AspNetCore.Mvc.ProducesAttribute。 这两个属性都实现了一个 Microsoft.AspNetCore.Mvc.ActionConstraints.IActionConstraint 接口,aspnetcore.mvc 使用该接口来控制控制器内的动作选择。

所以我实现了一个简化的 ProducesAttribute (a "tribute") 来检查接受 header。

    /// <summary>
    /// A filter that specifies the supported response content types. The request accept header is used to determine if it is a valid action
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class AcceptHeaderAttribute : Attribute, IActionConstraint
    {

        public AcceptHeaderAttribute(string contentType, params string[] otherContentTypes)
        {            
            if (contentType == null)
                throw new ArgumentNullException(nameof(contentType));

            // We want to ensure that the given provided content types are valid values, so
            // we validate them using the semantics of MediaTypeHeaderValue.
            MediaTypeHeaderValue.Parse(contentType);

            for (var i = 0; i < otherContentTypes.Length; i++)
            {
                MediaTypeHeaderValue.Parse(otherContentTypes[i]);
            }

            ContentTypes = GetContentTypes(contentType, otherContentTypes);
        }

        public MediaTypeCollection ContentTypes
        {
            get; set;
        }

        public int Order
        {
            get
            {
                return 0;
            }
        }

        private bool IsSubsetOfAnyContentType(string requestMediaType)
        {
            var parsedRequestMediaType = new MediaType(requestMediaType);
            for (var i = 0; i < ContentTypes.Count; i++)
            {
                var contentTypeMediaType = new MediaType(ContentTypes[i]);
                if (parsedRequestMediaType.IsSubsetOf(contentTypeMediaType))
                {
                    return true;
                }
            }
            return false;
        }

        public bool Accept(ActionConstraintContext context)
        {
            var requestAccept = context.RouteContext.HttpContext.Request.Headers[HeaderNames.Accept];
            if (StringValues.IsNullOrEmpty(requestAccept))
                return true;

            if (IsSubsetOfAnyContentType(requestAccept))
                return true;

            return false;
        }

        private MediaTypeCollection GetContentTypes(string firstArg, string[] args)
        {
            var completeArgs = new List<string>();
            completeArgs.Add(firstArg);
            completeArgs.AddRange(args);

            var contentTypes = new MediaTypeCollection();
            foreach (var arg in completeArgs)
            {
                contentTypes.Add(arg);
            }

            return contentTypes;
        }
    }

您可以使用此属性装饰任何动作。

请注意,更改并允许指定要检查的 header 和值很容易。