回到目錄
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的,它在默認情況下,是不能跨域訪問的)
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",
|
新聞熱點
疑難解答