首页 > 学院 > 开发设计 > 正文

WebApi系列~在WebApi中实现Cors访问

2019-11-15 02:31:05
字体:
来源:转载
供稿:网友

WebApi系列~在WebApi中实现Cors访问

回到目录

说在前

Cors是个比较热的技术,这在蒋金楠的博客里也有体现,Cors简单来说就是“跨域资源访问”的意思,这种访问我们指的是Ajax实现的异步访问,形象点说就是,一个A网站公开一些接口方法,对于B网站和C网站可以通过发xmlhttPRequest请求来调用A网站的方法,对于xmlhttprequest封装比较好的插件如jquery的$.ajax,它可以让开发者很容易的编写AJAX异步请求,无论是Get,Post,Put,Delete请求都可以发送。

Cors并不是什么新的技术,它只是对HTTP请求头进行了一个加工,还有我们的Cors架构里,对jsonp也有封装,让开发者在使用jsonp访问里,编写的代码量更少,更直观,呵呵。(Jsonp和Json没什么关系,它是从一个URI返回一个Script响应块,所以,JSONP本身是和域名没关系的,而传统上的JSON是走xmlhttprequest的,它在默认情况下,是不能跨域访问的)

做在后

一  下面先说一下,对jsonp的封装

1 注册jsonp类型,在global.asax里application_Start方法中

 GlobalConfiguration.Configuration.Formatters.Insert(0, new EntityFrameworks.Web.Core.JsonpMediaTypeFormatter());

2 编写JsonpMediaTypeFormatter这个类型中实现了对jsonp请求的响应,并在响应流中添加指定信息,如callback方法名。

 /// <summary>    /// 对jsonp响应流的封装    /// </summary>    public class JsonpMediaTypeFormatter : JsonMediaTypeFormatter    {        public string Callback { get; private set; }        public JsonpMediaTypeFormatter(string callback = null)        {            this.Callback = callback;        }        public override Task WriteToStreamAsync(            Type type,            object value,            Stream writeStream,            HttpContent content,            TransportContext transportContext)        {            if (string.IsNullOrEmpty(this.Callback))            {                return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);            }            try            {                this.WriteToStream(type, value, writeStream, content);                return Task.FromResult<AsyncVoid>(new AsyncVoid());            }            catch (Exception exception)            {                TaskCompletionSource<AsyncVoid> source = new TaskCompletionSource<AsyncVoid>();                source.SetException(exception);                return source.Task;            }        }        private void WriteToStream(            Type type,            object value,            Stream writeStream,            HttpContent content)        {            JsonSerializer serializer = JsonSerializer.Create(this.SerializerSettings);            using (StreamWriter streamWriter = new StreamWriter(writeStream, this.SupportedEncodings.First()))            using (JsonTextWriter jsonTextWriter = new JsonTextWriter(streamWriter) { CloSEOutput = false })            {                jsonTextWriter.WriteRaw(this.Callback + "(");                serializer.Serialize(jsonTextWriter, value);                jsonTextWriter.WriteRaw(")");            }        }        public override MediaTypeFormatter GetPerRequestFormatterInstance(            Type type,            HttpRequestMessage request,            MediaTypeHeaderValue mediaType)        {            if (request.Method != HttpMethod.Get)            {                return this;            }            string callback;            if (request.GetQueryNameValuePairs().ToDictionary(pair => pair.Key,                 pair => pair.Value).TryGetValue("callback", out callback))            {                return new JsonpMediaTypeFormatter(callback);            }            return this;        }        [StructLayout(LayoutKind.Sequential, Size = 1)]        private struct AsyncVoid        {        }    }

二  对指定域名实现友好的跨域资源访问

1 在global.asax中注册这个HttpHandler,使它对HTTP的处理进行二次加工,它可以有同步和异步两个版本,本例中实现异步方式实现

 //对指定URI的网站进行跨域资源的共享 GlobalConfiguration.Configuration.MessageHandlers.Add(new EntityFrameworks.Web.Core.Handlers.CorsMessageHandler());

下面是MessageHandlers原代码,实现对HTTP请求的二次处理

    /// <summary>    /// 跨域资源访问的HTTP处理程序    /// </summary>    public class CorsMessageHandler : DelegatingHandler    {        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)        {            //得到描述目标Action的HttpActionDescriptor            HttpMethod originalMethod = request.Method;            bool isPreflightRequest = request.IsPreflightRequest();            if (isPreflightRequest)            {                string method = request.Headers.GetValues("access-Control-Request-Method").First();                request.Method = new HttpMethod(method);            }            HttpConfiguration configuration = request.GetConfiguration();            HttpControllerDescriptor controllerDescriptor = configuration.Services.GetHttpControllerSelector().SelectController(request);            HttpControllerContext controllerContext = new HttpControllerContext(request.GetConfiguration(), request.GetRouteData(), request)            {                ControllerDescriptor = controllerDescriptor            };            HttpActionDescriptor actionDescriptor = configuration.Services.GetActionSelector().SelectAction(controllerContext);            //根据HttpActionDescriptor得到应用的CorsAttribute特性            CorsAttribute corsAttribute = actionDescriptor.GetCustomAttributes<CorsAttribute>().FirstOrDefault() ??                controllerDescriptor.GetCustomAttributes<CorsAttribute>().FirstOrDefault();            if (null == corsAttribute)            {                return base.SendAsync(request, cancellationToken);            }            //利用CorsAttribute实施授权并生成响应报头            IDictionary<string, string> headers;            request.Method = originalMethod;            bool authorized = corsAttribute.TryEvaluate(request, out headers);            HttpResponseMessage response;            if (isPreflightRequest)            {                if (authorized)                {                    response = new HttpResponseMessage(HttpStatusCode.OK);                }                else                {                    response = request.CreateErrorResponse(HttpStatusCode.BadRequest, corsAttribute.ErrorMessage);                }            }            else            {                response = base.SendAsync(request, cancellationToken).Result;            }            //添加响应报头            if (headers != null && headers.Any())                foreach (var item in headers)                    response.Headers.Add(item.Key, item.Value);            return Task.FromResult<HttpResponseMessage>(response);        }    }

2 添加Cors特性,以便处理可以跨域访问的域名,如B网站和C网站

    /// <summary>    /// Cors特性    /// </summary>   [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]       public class CorsAttribute : Attribute    {        public Uri[] AllowOrigins { get; private set; }        public string ErrorMessage { get; private set; }        public CorsAttribute(params string[] allowOrigins)        {            this.AllowOrigins = (allowOrigins ?? new string[0]).Select(origin => new Uri(origin)).ToArray();        }        public bool TryEvaluate(HttpRequestMessage request, out IDictionary<string, string> headers)        {            headers = null;            string origin = null;            try            {                origin = request.Headers.GetValues("Origin").FirstOrDefault();            }            catch (Exception)            {                this.ErrorMessage = "Cross-origin request denied";                return false;            }            Uri originUri = new Uri(origin);            if (this.AllowOrigins.Contains(originUri))            {                headers = this.GenerateResponseHeaders(request);                return true;            }            this.ErrorMessage = "Cross-origin request denied";            return false;        }        private IDictionary<string, string> GenerateResponseHeaders(HttpRequestMessage request)        {            //设置响应头"Access-Control-Allow-Methods"            string origin = request.Headers.GetValues("Origin").First();            Dictionary<string, string> headers = new Dictionary<string, string>();            headers.Add("Access-Control-Allow-Origin", origin);            if (request.IsPreflightRequest())            {                //设置响应头"Access-Control-Request-Headers"                //和"Access-Control-Allow-Headers"                headers.Add("Access-Control-Allow-Methods",
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表