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

后端 未结 2 1285
遥遥无期
遥遥无期 2020-12-10 03:29

I want to return two different formatted responses for the same feature (a list of entities) based on the accept header of the request, it is for a \"json\" and a \"html\" r

相关标签:
2条回答
  • 2020-12-10 03:59

    I dived into the .net core source code and looked for other attributes that do some similar behaviour such as Microsoft.AspNetCore.Mvc.HttpGet or Microsoft.AspNetCore.Mvc.ProducesAttribute. Both attributes implements an Microsoft.AspNetCore.Mvc.ActionConstraints.IActionConstraint interface wich is used by aspnetcore.mvc to control the selection of actions inside a controller.

    So i implemented a simplified ProducesAttribute (a "tribute") to check for the accept 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;
            }
        }
    

    You can decorate any action with this attribute.

    Note that it is easy to change and allow to specify the header you want to check and the value.

    0 讨论(0)
  • 2020-12-10 04:06
    if(Request.Headers["Content-Type"] == "application/json")
    {
        return OK(json);
    }
    else
    {
        return View();
    }
    

    Thats ok?

    0 讨论(0)
提交回复
热议问题