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

后端 未结 2 1292
遥遥无期
遥遥无期 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.

        /// 
        /// A filter that specifies the supported response content types. The request accept header is used to determine if it is a valid action
        /// 
        [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();
                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.

提交回复
热议问题