前后端分离之CORS和WebApi
目前的项目是前端mv*+api的方式进行开发的,以前都是没有跨域的方案,前后端人员在同一个解决方案里边进行开发,前端人员要用IIS或VS来开发和调试Api,这样就很不方便,迫切需要跨域访问Api.
评选了很多解决方案最终选择,CORS+WebApi
cors科普:http://www.ruanyifeng.com/blog/2016/04/cors.html
cors网站:http://enable-cors.org/
mvc源码:https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Http.Cors/CorsMessageHandler.cs
快速入门可以看一些教程,自己要扩展源码是一条捷径.
示例代码:https://github.com/gutun/aspnet/tree/master/cors
1.新建WebApi项目实现CORS跨域
1.1 新建一个新的项目CrossDomain
1.2. 安装 Microsoft.AspNet.WebApi.Cors
Install-Package Microsoft.AspNet.WebApi.Cors
1.3. 配置App_Start目录下的 WebApiConfig文件
Config中要启用 CORS的支持我选择默认的MediaType为json方式。
1.4 新增UserController,在里边新增两个方法,get用来ping,代表url是通的,post模拟真正的数据提交,我们所有的api访问走post, request的入参和出参可以定义通用的实体。这里模拟post提交数据解析成UserInfo的实例。
2.JQuery Ajax跨域
<!DOCTYPE html> <html lang="en"> <head> <title></title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="http://cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script> </head> <body> <script> $.ajax({ url:'http://localhost:64542/api/user', type:'POST', data:{"Id":"1","Name":"张三"}, dataType:'json', //Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain //contentType: 'application/json; charset=utf-8', cache: false, crossDomain: true, success:function(data){ alert(data); } }); </script> </body> </html>
使用Nodejs本地服务器访问WebApi项目,成功的访问到了api/User,状态是200.
3.IE8,IE9支持CORS.
CORS在浏览器的支持情况,IE8和IE9是部分兼容,86%的浏览器是支持的,占了大部分,为了支持IE8和IE9我找到了一个补丁jquery.transport.xdr.min.js 用来弥补在IE8和IE9下的不足。
https://github.com/gfdev/javascript-jquery-transport-xdr.js
<!DOCTYPE html> <html lang="en"> <head> <title></title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="http://cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script> <script src="http://cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script> <!--[if (IE 8)|(IE 9)]> <script src="http://cdn.rawgit.com/gfdev/javascript-jquery-transport-xdr/master/dist/jquery.transport.xdr.min.js"></script> <![endif]--> </head> <body> <script> //http://www.ruanyifeng.com/blog/2016/04/cors.html $.ajax({ url:'http://localhost:64542/api/user', type:'POST', data:{"Id":"1","Name":"张三"}, dataType:'json', //Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain //contentType: 'application/json; charset=utf-8', cache: false, crossDomain: true, success:function(data){ alert(data); } }); </script> </body> </html>
喜出往外,跑起来后遇到了,415错误,未识别的Content-Type,这是因为在IE8和IE9下,Content-Type为空造成的。
http://stackoverflow.com/questions/18964258/asp-web-api-post-request-with-cors-and-ie9-xdomainrequest-objec
新增DefaultContentTypeMessageHandler用来处理Request
using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Web.Cors; using System.Web.Http.Cors; namespace CrossDomain { public class DefaultContentTypeMessageHandler : DelegatingHandler { protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { CorsRequestContext corsRequestContext = request.GetCorsRequestContext(); if (corsRequestContext != null) //判断是否是跨域的请求 { if (request.Method == HttpMethod.Post && request.Content.Headers.ContentType == null) //ConentType为空,使用默认值 request.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded"); } var response = await base.SendAsync(request, cancellationToken); return response; } } }
再次更改WebApiConfig文件,在MessageHandlers管道中追加刚写的DefaultContentTypeMessageHandler,这样ContentType为空的跨域请求会使用默认的ContentType.
using System.Web.Http; using System.Web.Http.Cors; namespace CrossDomain { public static class WebApiConfig { public static void Register(HttpConfiguration config) { //新增CORS支持 var corsAttr = new EnableCorsAttribute("*", "*", "*"); config.EnableCors(corsAttr); //默认使用json格式,移除xml格式 config.Formatters.XmlFormatter.SupportedMediaTypes.Clear(); //处理Content-Type config.MessageHandlers.Add(new DefaultContentTypeMessageHandler()); // Web API configuration and services // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } }
在chrome,IE8,IE9,IE10+上测试没有问题。