发文章
发文工具
撰写
网文摘手
文档
视频
思维导图
随笔
相册
原创同步助手
其他工具
图片转文字
文件清理
AI助手
留言交流
说在前 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", "*");
string requestHeaders = request.Headers.GetValues("Access-Control-Request-Headers").FirstOrDefault();
if (!string.IsNullOrEmpty(requestHeaders)) { headers.Add("Access-Control-Allow-Headers", requestHeaders); } } return headers; } }
/// <summary> /// HttpRequestMessage扩展方法 /// </summary> public static class HttpRequestMessageExtensions { public static bool IsPreflightRequest(this HttpRequestMessage request) { return request.Method == HttpMethod.Options && request.Headers.GetValues("Origin").Any() && request.Headers.GetValues("Access-Control-Request-Method").Any(); } }
3 下面是为指定的API类型添加指定域名访问的特性 [CorsAttribute("http://localhost:11879/", "http://localhost:5008/")]/*需要加在类上*/ public class ValuesController : ApiController { //代码省略 } 下面看一下實例的結果:
上图中分别使用了jsonp和json两种方法,看一下它们的响应结果
可以看到,jsonp实现上是一种远程JS方法的调用,客户端发起一个HTTP请求,这通过callback参数(一串随机数)来区别多个客户端,每个客户端的请求callback都是不同的,它们由服务器端处理数据,再通过callback随机数去为指定客户端返回数据。 感谢您的阅读!
来自: 昵称10504424 > 《工作》
0条评论
发表
请遵守用户 评论公约
前端通信进阶
比如,像这样的一个路径ws://websocket.example.com/,就是一个websocket 通信. 通常的实时通信并不会传输大量的内容, 所以,对于HTTP协议...
为 RESTful API 配置 CORS 实现跨域请求
为 RESTful API 配置 CORS 实现跨域请求。Access-Control-Allow-Origin: *Access-Control-Allow-Methods: POST, GET, OPTIONSAccess-Control-Allow-Headers: X-Requested-With, Content-Type, AcceptAc...
前端跨域请求原理及实践
var data = ''var data = {'' + ''name: $(''#name'').val() + '' - server 3001 jso...
Python学习教程:Python的cors跨域模块主要做了什么?
CORS 跨域 实现思路及相关解决方案
CORS 定义CORS 对比 JSONPCORS,BROWSER支持情况主要用途Ajax请求跨域资源的异常CORS 实现思路安全说明CORS 几种解决方案。CORSFilter&l...
跨域资源共享 CORS 详解
除了Origin字段,"预检"请求的头信息包括两个特殊字段。该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信...
WebAPi接口安全之公钥私钥加密
WebAPi接口安全之公钥私钥加密WebAPi使用公钥私钥加密介绍和使用。1 public class CustomerMessageProcesssingHandler : MessageProcess...
ASP.NET Web API
ASP.NET Web API ASP.NET Web API ASP.NET Web API 2 对 CORS 的支持。除域之外,CORS 还可以让服务器指明允许使用的 HTTP 方法、客户端可以发送的 HTTP 请求标头、客户端可以读取的 HTTP 响应标头...
Ajax跨域、Json跨域、Socket跨域和Canvas跨域等同源策略限制的解决方法
} # non-OPTIONS indicates a normal CORS request if ($request_method = ''GET'') { set $cors ''${cors}get...
微信扫码,在手机上查看选中内容