博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
跨域访问之CORS
阅读量:4982 次
发布时间:2019-06-12

本文共 6771 字,大约阅读时间需要 22 分钟。

CORS:定义

 

2014年1月16日,W3C的和联合发布了的W3C正式推荐标准(W3C Recommendation)。该标准定义了在必须访问跨域资源时,浏览器与服务端应该如何沟通,它提供一种机制,允许客户端(如浏览器)对非源站点的资源发出访问请求。所有提供跨源资源请求的API都可以使用本规范中定义的算法。

出于安全性的考虑,用户代理(如浏览器)通常拒绝跨站的访问请求,但这会限制运行在用户代理的Web应用通过Ajax或者其他机制从另一个站点访问资源、获取数据。跨源资源共享(CORS)扩充了这个模型,通过使用自定义的HTTP响应头部(HTTP Response Header),通知浏览器资源可能被哪些跨源站点以何种HTTP方法获得。例如,浏览器在访问 http://example.com 站点的Web应用时,Web应用如果需要跨站访问另一站点的资源 http://hello-world.example,就需要使用该标准。http://hello-world.example 在HTTP的响应头部中定义 Access-Control-Allow-Origin: http://example.org,通知浏览器允许 http://example.org 跨源从 http://hello-world.example上获取资源。

 

 

 

CORS 图解

 

 

过程说明:

1 浏览器发出跨域访问请求,request header 携带 origin

2 服务器收到请求,服务器返回response header

3 浏览区检查reponse header ,如果Access-Control-Allow-Origin : 包含request header的Origin,那么浏览器认为这是安全操作,允许操作服务端返回的数据,否则浏览器认为非同源数据,不允许操作。

 

 

目前主流的浏览器基本都支持CORS规范,所以实现CORS跨域访问的关键就是Server 端返回的respone中添加 Access-Control-Allow-Origin:对应的请求origin.

 

Web API 如何实现跨域请求:

 

服务端代码

 

类说明:

 

 

  1. CorsAttribute:请求跨域的标记

 

  1. CorsMessageHandler:

拦截请求,确认请求是否允许跨域,如果允许,那么给response header 添加跨域请求 Access-Control-Allow-Origin,否则返回

{

  "Message": "Cross-origin request denied"

}

 

 

 

代码:

using System;using System.Collections.Generic;using System.Linq;using System.Net.Http;using System.Threading;using System.Threading.Tasks;using System.Web;using System.Web.Http;using System.Web.Http.Controllers;using System.Net;using System.Diagnostics; namespace Hbb0b0.MVC.CORS{     ///     /// 跨域属性    /// 此处参考    /// asp.net webapi 2    ///    ///     public class CorsAttribute : Attribute    {         ///         /// 允许的站点列表        ///         public Uri[] AllowOrigins        {            get;            private set;        }         ///         /// 错误提示        ///         public string ErrorMessage        {            get;            set;        }         ///         /// 构造函数        ///         ///         public CorsAttribute(params string[] allowOrigins)        {            this.AllowOrigins = (allowOrigins ?? new string[0]).Select(p => new Uri(p)).ToArray();        }          ///         /// 获取请求头信息,判断是否请求被允许跨域        ///         ///         ///         /// 
public bool TryEvaluate(HttpRequestMessage request, out IDictionary
headers) { headers = null; string origin = request.Headers.GetValues(HttpHeaderKeys.ORIGIN).FirstOrDefault(); Uri originUrl = new Uri(origin); if (this.AllowOrigins.Contains(originUrl)) { headers = this.GenerateResposeHeaders(request); return true; } this.ErrorMessage = "Cross-origin request denied"; return false; } ///
/// 添加跨域许可请求头 /// ///
///
private IDictionary
GenerateResposeHeaders(HttpRequestMessage request) { string origin = request.Headers.GetValues("Origin").First(); Dictionary
headers = new Dictionary
(); headers.Add(HttpHeaderKeys.ACCESS_CONTROL_ALLOW_ORIGIN, origin); if (request.IsPreflightRequest()) { headers.Add(HttpHeaderKeys.ACCESS_CONTROL_ALLOW_ORIGIN, "*"); string requestHeaders = request.Headers.GetValues(HttpHeaderKeys.ACCESS_CONTROL_ALLOW_REQUEST_HEADERS).FirstOrDefault(); if (!string.IsNullOrEmpty(requestHeaders)) { headers.Add(HttpHeaderKeys.ACCESS_CONTROL_ALLOW_REQUEST_HEADERS, requestHeaders); } } return headers; } } ///
/// Http Header keys /// public class HttpHeaderKeys { public const string ORIGIN = "Origin"; public const string ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; public const string ACCESS_CONTROL_ALLOW_REQUEST_METHOD = "Access-Control-Allow-Request-Methods"; public const string ACCESS_CONTROL_ALLOW_REQUEST_HEADERS = "Access-Control-Allow-Request-Headers"; } ///
/// 判断是否是非简单跨域请求扩展方法 /// public static class HttpRequestMessageExtensions { public static bool IsPreflightRequest(this HttpRequestMessage request) { return request.Method == HttpMethod.Options && request.Headers.GetValues(HttpHeaderKeys.ORIGIN).Any() && request.Headers.GetValues(HttpHeaderKeys.ACCESS_CONTROL_ALLOW_REQUEST_METHOD).Any(); } } public class CorsMessageHandler : DelegatingHandler { protected override Task
SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { //Debugger.Break(); HttpMethod originalMethod = request.Method; bool isPreflightRequest = request.IsPreflightRequest(); if (isPreflightRequest) { string method = request.Headers.GetValues(HttpHeaderKeys.ACCESS_CONTROL_ALLOW_REQUEST_HEADERS).FirstOrDefault(); request.Method = new HttpMethod(method); } HttpConfiguration config = request.GetConfiguration(); HttpControllerDescriptor description = config.Services.GetHttpControllerSelector().SelectController(request); HttpControllerContext context = new HttpControllerContext(request.GetConfiguration(), request.GetRouteData(), request) { ControllerDescriptor = description }; HttpActionDescriptor actionDescriptor = config.Services.GetActionSelector().SelectAction(context); CorsAttribute corsAttribute = actionDescriptor.GetCustomAttributes
().FirstOrDefault(); if (corsAttribute == null) { return base.SendAsync(request, cancellationToken); } IDictionary
headers; request.Method = originalMethod; HttpResponseMessage response = null; //判断请求是否被授权 bool authorized = corsAttribute.TryEvaluate(request, out headers); //处理非简单跨域请求 if (isPreflightRequest) { if (authorized) { response = new HttpResponseMessage(HttpStatusCode.OK); } } //简单跨域请求 else { //如果授权,返回请求资源 if (authorized) { response = base.SendAsync(request, cancellationToken).Result; } //非授权,返回错误信息 else { response = request.CreateErrorResponse(HttpStatusCode.BadRequest, corsAttribute.ErrorMessage); } } //添加header if (headers!=null) { foreach (var item in headers) { response.Headers.Add(item.Key, item.Value); } } return Task.FromResult
(response); } } }

 

 

 

客户端代码:

 

@{    Layout = null;}      
View

 

运行结果

 

 

Request Header:

 

 

Response header:

 

 

 

结论

CORS 作为W3C官方正式的跨域资源访问方案,不像JSONP 是一种临时方案。

CORS 不对请求类型做限制,get, post 都支持,JSONPzhi只支持get. 所以 CORS比JSONP更通用,更是主流的跨域资源访问请求解决方案。

 

 

本文参考了<<asp.net  webapi 2  框架解密>>,此书的确是深入剖析asp.net 框架的好书。

转载于:https://www.cnblogs.com/hbb0b0/p/7189709.html

你可能感兴趣的文章
GenePix Pro 3.0
查看>>
html移动端 -- meta-模板 + rem
查看>>
js-控制浏览器和移动端的后退按钮 . popstate
查看>>
[QT][SQLITE]学习记录二 日期查询
查看>>
hdu 4455 Substrings
查看>>
web传参
查看>>
Python 查找binlog文件
查看>>
Git——如何将本地项目提交至远程仓库
查看>>
Convert CString to std::string
查看>>
3 - Selenium元素定位和操作
查看>>
GCC C语言 DLL范例,含源码
查看>>
冲刺第一天(补发)
查看>>
iOS开发Xcode中切换显示语言实现国际化
查看>>
C++模板学习
查看>>
nginx
查看>>
大数据平台搭建-hadoop集群的搭建
查看>>
安装一些包管理的记录 win10
查看>>
Android RecyclerView notifyDataSetChanged不起作用
查看>>
AndroidStudio3.0 Canary 8注解报错Annotation processors must be explicitly declared now.
查看>>
Android 一个改进的okHttp封装库
查看>>