说到WebApi,我想你应该对RESTful也并不陌生,因为你或多或少都听说过RESTful Api。那么什么是RESTful呢?我们一起来看一下。
RESTful其实是一种架构风格,它体现了表现层的状态转化, 是一个接口的设计风格,它具有以下特点:
- 资源:万物看成资源,
- 提供统一接口:CRUD增删改查,将增删改查操作跟HTTP Method对应起来。
Create => Post,Read => Get,Update => Put/Patch,Delete => Delete
- URI:统一资源定位符,资源对应的唯一地址
- 无状态:基于Http协议, (登陆系统--查询工资--计算税收,有状态),无状态的直接一个地址,就能拿到工资,就能得到税收
我们知道,WebService采用的是http协议和soap协议,只能在IIS承载,入门简单,XML跨平台,而WCF是集大成者,可以采用多种协议,多种宿主,整合了RPC。而WebApi,则采用了RESTful的架构风格,采用http协议,无状态,标准化操作,其更轻量级,尤其是json,特别适合移动端调用。
那么,WebApi是如何找到我们定义的方法的呢?
网站在启动时执行Application_Start,然后给路由Routes增加地址规则,当请求进来时,会经过路由匹配找到合适的Controller,然后再去找Action。那怎么找的Action呢?
- 根据HttpMethod找方法,用的方法名字开头,Get就是对应Get请求
- 如果名字不是Get开头,可以加上[HttpGet]
- 按照参数找最吻合
除了在Application_start为路由添加地址规则意外,还有个特性路由!可以单独订制。需要我们在WebApiConfig中添加 config.MapHttpAttributeRoutes();然后在Controller或Action添加 Route 标记特性。
上面我们也提到WebApi通常为第三方如App提供接口,那么就应该关注它的权限认证如何实现,下面是基本的认证步骤
- 登陆过程,拿到令牌--token--ticket--许可证
- 验证成功--账号+密码+其他信息+时间--加密一下得到ticket---返回给客户端
- 请求时,request里面带上这个ticket(header)
- 接口调用时,就去验证ticket,解密一下,看看信息是否合法,看看时间是否过期
- 每个方法都验证下ticket?显然不合适,可以基于filter来实现,如AuthorizeAttribute
下面看一下具体的认证代码,登录我们使用表单认证
public string Login(string account, string password) { if ("Admin".Equals(account) && "123456".Equals(password))//应该数据库校验 { FormsAuthenticationTicket ticketObject = new FormsAuthenticationTicket(0, account, DateTime.Now, DateTime.Now.AddHours(1), true, string.Format("{0}&{1}", account, password), FormsAuthentication.FormsCookiePath); var result = new { Result = true, Ticket = FormsAuthentication.Encrypt(ticketObject) }; return JsonConvert.SerializeObject(result); } else { var result = new { Result = false }; return JsonConvert.SerializeObject(result); } }
public class CustomBasicAuthorizeAttribute : AuthorizeAttribute { /// <summary> /// action前会先来这里完成权限校验 /// </summary> /// <param name="actionContext"></param> public override void OnAuthorization(HttpActionContext actionContext) { //actionContext.Request.Headers["Authorization"] if (actionContext.ActionDescriptor.GetCustomAttributes<CustomAllowAnonymousAttribute>().FirstOrDefault() != null) { return;//继续 } else if (actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<CustomAllowAnonymousAttribute>().FirstOrDefault() != null) { return;//继续 } else { var authorization = actionContext.Request.Headers.Authorization; if (authorization == null) { this.HandlerUnAuthorization(); } else if (this.ValidateTicket(authorization.Parameter)) { return;//继续 } else { this.HandlerUnAuthorization(); } } } private void HandlerUnAuthorization() { throw new HttpResponseException(System.Net.HttpStatusCode.Unauthorized); } private bool ValidateTicket(string encryptTicket) { ////解密Ticket //if (string.IsNullOrWhiteSpace(encryptTicket)) // return false; try { var strTicket = FormsAuthentication.Decrypt(encryptTicket).UserData; //FormsAuthentication.Decrypt(encryptTicket). return string.Equals(strTicket, string.Format("{0}&{1}", "Admin", "123456"));//应该分拆后去数据库验证 } catch (Exception ex) { return false; } } }
然后在需要认证的Controller或Action中添加该特性即可。
WebApi除了为App提供接口,在前后端分离大行其道的现在,WebApi也往往用来为前端提供接口数据,而在前端调用时就会碰到 跨域请求的问题。
那么什么是跨域请求呢?其是指浏览器请求时,如果A网站(域名+端口)页面里面,通过XHR请求了B域名,就叫做跨域请求。这个请求是可以正常到达B服务器后端,正常的响应(200),但是,浏览器不允许这样操作,除非在响应里面有声明(Access-Control-Allow-Origin)。
为什么会存在跨域问题呢? 由于浏览器同源策略:处于安全考虑,浏览器限制脚本去发起跨站请求,但是,页面是js/css/图片/iframe 这些是浏览器自己发起的,是可以跨域的 。它的核心就在于它认为自任何站点装载的信赖内容是不安全的。当被浏览器半信半疑的脚本运行在沙箱时,它们应该只被允许访问来自同一站点的资源,而不是那些来自其它站点可能怀有恶意的资源。
解决跨域的问题可以有两种方式,一种在前端采用JSONP实现,一种在后端服务器在响应头里面指定Access-Control-Allow-Origin。
- JSONP---脚本标签自动请求--请求回来的内容执行个回调方法---解析数据
- CORS 跨域资源共享,添加nuget包Microsoft.AspNet.WebApi.Cors,通过在配置中添加或者在Controller或Action上添加特性 EnableCors 来实现
config.EnableCors(new EnableCorsAttribute("*", "*", "*"));//全部都允许
- 自定义actionfilter--response增加头文件Access-Control-Allow-Origin