首页 > 技术文章 > .Net中关于SOA的三大组件之WebApi

jesen1315 2020-11-20 10:44 原文

  说到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 

推荐阅读