关于WebService、WebApi的跨域问题
在使用JavaScript进行数据交互时会遇到一个问题,那就是JavaScript的同源策略,简单的来讲同源策略就是指一段脚本只能读取来自同一来源的窗口和文档的属性,这里的同一来源指的是主机名、协议和端口号的组合。
以下是我对同源策略的理解,可能不完善:
请求URL | 请求结果 | 原因 |
http://wangyu.testurl.com/WebApi/test | YES | 当前域 |
http://wangyu.testurl.com/WebApi/test2 | YES | 当前域 |
http://wangyu.testurl.com/WebApi2/test | NO | 跨域/端口不同 |
http://wangyu2.testurl.com/WebApi2/test | NO | 跨域/主机不同 |
https://wangyu.testurl.com/WebApi/test | NO | 协议不同 |
一、WebApi的跨域问题解决:
1.从客户端来解决跨域问题:
从客户端来解决跨域问题就是从调用方的解决跨域访问,需要每个客户端自己来做处理,常见的JSONP回调方式。(本文略过,本博文主要讲解从服务端解决跨域的方法)
2.从服务端来解决跨域:
(1)ASP.NET Web API对CORS提供的原生支持的实现:
ASP.NET Web API对CORS提供的原生支持实现在一个名为“Microsoft ASP.NET Web API 2 Cross-Origin Support”的NuGet包中。当我们安装这个包之后,现有的packages目录下会添加两个名称分别为“Microsoft.AspNet.Cors”和“Microsoft.AspNet.WebApi.Cors”,针对保存其中的两个程序集(System.Web.Cors.dll和System.Web.Http.Cors.dll)的引用被自动添加到项目中。
具体操作步骤:①VS工具条“工具”->"NuGet工具管理器"->"程序包管理控制台"②在控制台输入Install-Package Microsoft.AspNet.WebApi.Cors(当然要在联网状态)此时编译器就会为我们安装对应的程序集。在安装程序集之后,跨域支持是默认关闭的,需要我们添加部分代码:
1 public static class WebApiConfig 2 { 3 public static void Register(HttpConfiguration config) 4 { 5 //打开跨域支持 6 config.EnableCors(); 7 // Web API 配置和服务 8 // 将 Web API 配置为仅使用不记名令牌身份验证。 9 config.SuppressDefaultHostAuthentication(); 10 config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType)); 11 12 // Web API 路由 13 config.MapHttpAttributeRoutes(); 14 15 config.Routes.MapHttpRoute( 16 name: "DefaultApi", 17 routeTemplate: "api/{controller}/{id}", 18 defaults: new { id = RouteParameter.Optional } 19 ); 20 } 21 }
然后在对应的控制器添加对应的属性,就可以让特定的控制器支持跨域访问。比如我自己的示例:
1 /// <summary> 2 /// Class UserController. 3 /// </summary> 4 [EnableCors(origins: "*", headers: "*", methods: "*")] 5 public class UserController : ApiController 6 { 7 /// <summary> 8 /// 注册用户 9 /// </summary> 10 /// <param name="model">The model.</param> 11 /// <returns>System.String.</returns> 12 [System.Web.Http.HttpPost] 13 14 public string NewUser([FromBody] MUserInfo model) 15 { 16 bool res = WcfCallHelper.Instance.Invoke<bool, IUserService, MUserInfo>(model, "RegisteUser"); 17 if(!res) 18 { 19 return "注册失败!"; 20 } 21 MOperateLog temp = new MOperateLog(); 22 temp.KeyID = model.KeyID; 23 temp.Operate = "注册"; 24 temp.OpUser = model.Name; 25 temp.TrackID = model.KeyID; 26 temp.Detail = string.Format("{0}注册了账号,初始密码是:{1}", model.Name, model.Password); 27 LogMannager.WriteLog(temp); 28 return "注册成功!"; 29 } 30 }
[EnableCors(origins: "*", headers: "*", methods: "*")]里面*号表示不加任何限定,具体的使用方法可以自己去学习。提示:当我们自定义参数类型时,要为参数加上[FromBody]属性,并且在Ajax提交的时候,不加DataType选项,要不然会出问题,亲测过!
3.从部署的服务器的配置上解决跨域限制问题:
当我们的WebApi需要跨域调用调试或者是完成了本地调试不要上服务器开始外网服务时,需要将我们写好的web服务部署在服务器上,这时候可以通过服务器上的一些配置选项来解决跨域限制问题。
具体操作方法:当我们把我们的服务部署到服务器之后打开IIS管理器,选中对应的服务,我们会看到如下一个菜单选项:
选则HTTP响应标头菜单,添加如下三个响应标头:
Access-Control-Allow-Origin 表示请求的来源,其格式:”地址:端口号”,”域名”,当其值为”*”表示不限定。标头设置好之后重新启动网站,跨域访问限制问题就解决了。
二、WebService的跨域问题解决:
1.从客户端调用来解决跨域限制:同上的,采用JSONP方式调用(掠过)。
2.从服务端解决跨域限制:
①采用WebApi的同样的方法,设置我们的IIS服务器HTTP响应标头
②采用ServiceStack服务框架
采用ServiceStack服务框架跨域的具体实现:
首先在编译器上安装好ServiceStack服务框架模板,建立新的工程之后,整个项目的目录如图(Yibaobao.BasicComponents是我自己定义的基础组件程序集,并不是框架提供):
按照ServiceStack服务框架建立好一个项目之后,我们就可以根据我们的实际需求来定义我们的服务接口以及相应的数据模型,ServiceStack的具体实现这里就不做赘述,在这里我们就讲解跨域的解决部分,在Service层目录里面有一个“AppHost.cs”文件,在这里面我们可以对我们的WebService做一些设置,比如我们的跨域支持也是在这里完成,改写“AppHost.cs”文件的代码如下:
1 using Funq; 2 using ServiceStack; 3 using Yibaobao.WebService.ServiceInterface; 4 using System.ServiceModel; 5 namespace Yibaobao.WebService 6 { 7 public class AppHost : AppHostBase 8 { 9 /// <summary> 10 /// Default constructor. 11 /// Base constructor requires a name and assembly to locate web service classes. 12 /// </summary> 13 public AppHost() 14 : base("Yibaobao.WebService", typeof(MyServices).Assembly) 15 { 16 17 } 18 19 /// <summary> 20 /// Application specific configuration 21 /// This method should initialize any IoC resources utilized by your web service classes. 22 /// </summary> 23 /// <param name="container"></param> 24 public override void Configure(Container container) 25 { 26 base.SetConfig(new HostConfig 27 { 28 GlobalResponseHeaders = { 29 { "Access-Control-Allow-Origin", "*" }, 30 { "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS" }, 31 { "Access-Control-Allow-Headers", "Content-Type" }, 32 }, 33 }); 34 35 } 36 } 37 }
在重写的Config函数里面也是加了一个响应标头,和配置IIS服务器感觉类似,只是这是在代码中直接解决。