欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

WebApi简介

程序员文章站 2022-05-28 21:15:18
简单创建.NET Core WebApi:https://www.cnblogs.com/yanbigfeg/p/9197375.html 登陆验证四种方式:https://www.cnblogs.com/zuowj/p/5123943.html 解决跨域的8种方法:https://blog.csd ......

简单创建.net core webapi:

登陆验证四种方式:

解决跨域的8种方法:

webapi深入学习--特性路由:https://www.cnblogs.com/tiestoray/p/5755454.html
本文的示例代码是基于.net framework下的,.net webapi与.net core webapi的区别,个人认为主要是来自框架的不一样。可以参照官网后续介绍到.net core的时候再详细做下这两个框架的不同。

 

在webapi中,方法名以get开头,webapi会自动默认之歌请求是get请求,而如果你以其他名称开头而又不标注这个方法的请求方式,那么这个时候服务器虽然找到了这个方法,但是由于请求方式不确定,所以直接返回给你405---方法不被允许的错误
最后结论:所有的webapi方法最好是加上请求的方式[httpget]/[httppost]/[httpput]/[httpdelete],不要偷懒,这样既能防止类似的错误,也有利于方法的维护,被人一看就知道这个方法是什么请求

WebApi简介

 

 网站在启动时执行application_start(),给route增加地址规则,请求进来时,会经过路由匹配找到合适的控制器。

那怎么找action?

  1、根据httpmethod找方法---用的方法名字开头,get就是对应的get请求

  2、如果名字不是get开头,可以加上[httpget]

  3、按照参数找最吻合

其实资源是这样定义的,不是一个学生,而可能是一个学校。可能是一个订单----多件商品,一次查询,订单-商品,数据之间嵌套关系很复杂。还有个特性路由,可以单独定制(config.maphttpattributeroutes()、标机特性)

WebApi简介

 

 

 ioc容器+配置文件初始化

控制器也要注入--完成容器和webapi框架融合--实现idependencyresolver,将容器放进去--初始化

config.dependencyresolver 换成自定义的resolver

 public class ioccontroller : apicontroller
 {
     private iuserservice _userservice = null;
     public ioccontroller(iuserservice userservice)
     {
         this._userservice = userservice;
     }

     public string get(int id)
     {
         //iuserservice service = new userservice();
         //iuserservice service = containerfactory.buildcontainer().resolve<iuserservice>();
         return newtonsoft.json.jsonconvert.serializeobject(this._userservice.query(id));
     }
 }

在webapiconfig中加上:

 // web api 配置和服务
 config.dependencyresolver = new unitydependencyresolver(containerfactory.buildcontainer());

unitydependencyresolver:

 public class unitydependencyresolver : idependencyresolver
 {
     private iunitycontainer _unitycontainer = null;
     public unitydependencyresolver(iunitycontainer container)
     {
         _unitycontainer = container;
     }

     public idependencyscope beginscope()//scope
     {
         return new unitydependencyresolver(this._unitycontainer.createchildcontainer());
     }

     public void dispose()
     {
         this._unitycontainer.dispose();
     }

     public object getservice(type servicetype)
     {
         try
         {
             return this._unitycontainer.resolve(servicetype);
         }
         catch (exception ex)
         {
             console.writeline(ex.message);
             return null;
         }
     }

     public ienumerable<object> getservices(type servicetype)
     {
         try
         {
             return this._unitycontainer.resolveall(servicetype);
         }
         catch (exception ex)
         {
             console.writeline(ex.message);
             return null;
         }
     }
 }

containerfactory:

/// <summary>
/// 需要在nuget引用之后,单独引用unity.configuration
/// 如果有aop扩展,还需要引用unity.interception.configuration
/// 因为我们是用配置文件来做的配置
/// </summary>
public class containerfactory
{
    public static iunitycontainer buildcontainer()
    {
        //get
        //{
        return _container;
        //}
    }

    private static iunitycontainer _container = null;
    static containerfactory()
    {
        execonfigurationfilemap filemap = new execonfigurationfilemap();
        filemap.execonfigfilename = path.combine(appdomain.currentdomain.basedirectory + "cfgfiles\\unity.config");//找配置文件的路径
        configuration configuration = configurationmanager.openmappedexeconfiguration(filemap, configurationuserlevel.none);
        unityconfigurationsection section = (unityconfigurationsection)configuration.getsection(unityconfigurationsection.sectionname);
        _container = new unitycontainer();
        section.configure(_container, "webapicontainer");
    }
}

basic授权认证&权限filter

  在basic授权认证,(.net core下面用oauth2,后续的随笔会记载到oauth2),两者的区别可以参考博客

权限认证,是需要的,因为是http地址,如果不加权限认证,别人不就可以直接拿到这边的请求去进行操作了,然后再去猜测别的webapi。

session不可以吗?webapi默认是不支持的session的,因为restful风格,因为是状态的。

无状态:第二次请求和第一次请求不联系,没关系。

步骤:

  1、登录过程,是为了拿到令牌,tooken/ticket/许可证

  2、验证成功,把账号+密码+其他信息+时间,加密一下,得到ticket,返回给客户端

  3、请求时,ajax里面就带上这个tooken/ticket(在header里面验证)

  4、接口调用时,就去验证下ticket,解密一下,看看信息,看看时间

  5、每个方法都验证下ticket?不是这样的,可以基于filter来实现,formauthenticationticket

用户登录返回的结果中会是这个样子的。

WebApi简介

 

 

这边用到了authorizeattribute,现在我们自定义个类custombasicauthorizeattribute继承自authorizeattribute。

1、方法注册,标机在action上

2、控制器生效,方法全部生效,标机在controller上

3、全局注册:

在webapiconfig里面加上WebApi简介

 

 问题来了,现在不能每一个action都标机吧,就要用到控制器或者全局的。那么登录的时候也要验证token,每次都登录,就是登录不上去,然后循环下去。。。

有一个特性allowanonymous,我们可以自己写一个customallowanonymousattribute,其实就是

下面是示例代码:

 #region 用户登陆
 [customallowanonymousattribute]
 [httpget]
 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);
     }
 }
 #endregion



public class customallowanonymousattribute : attribute
{
}



 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;
         }

     }
 }

异常处理filter,exceptionfilterattribute

1、自己定义一个类,继承自exceptionfilterattribute

2、实现onexception方法

3、标机到需要的控制钱action

4、注册到全局

5、filter的范围仅仅局限在action里面

 public class customexceptionfilterattribute : exceptionfilterattribute
 {
     /// <summary>
     /// 异常发生后(没有被catch),会进到这里
     /// </summary>
     /// <param name="actionexecutedcontext"></param>
     public override void onexception(httpactionexecutedcontext actionexecutedcontext)
     {
         //actionexecutedcontext.response. 可以获取很多信息,日志一下
         console.writeline(actionexecutedcontext.exception.message);//日志一下
         actionexecutedcontext.response = actionexecutedcontext.request.createresponse(
             system.net.httpstatuscode.ok, new
             {
                 result = false,
                 msg = "出现异常,请联系管理员",
                 //value=
             });//创造一个返回
         //base.onexception(actionexecutedcontext);
         //exceptionhandler
     }
 }

自定义的异常类,要在全局中进行注册:config.filters.add(new customexceptionfilterattribute());

webapi全局异常处理

自定义一个类,继承自exceptionhandler,重写handle,初始化项目时,服务替换上。

 /// <summary>
 /// webapi的全局异常处理
 /// </summary>
 public class customexceptionhandler : exceptionhandler
 {
     /// <summary>
     /// 判断是否要进行异常处理,规则自己定
     /// </summary>
     /// <param name="context"></param>
     /// <returns></returns>
     public override bool shouldhandle(exceptionhandlercontext context)
     {
         string url = context.request.requesturi.absoluteuri;
         return url.contains("/api/");

         //return base.shouldhandle(context);
     }
     /// <summary>
     /// 完成异常处理
     /// </summary>
     /// <param name="context"></param>
     public override void handle(exceptionhandlercontext context)
     {
         //console.writeline(context);//log
         context.result = new responsemessageresult(context.request.createresponse(
             system.net.httpstatuscode.ok, new
             {
                 result = false,
                 msg = "出现异常,请联系管理员",
                 debug = context.exception.message
             }));

         //if(context.exception is httpexception)
     }
 }



 config.services.replace(typeof(iexceptionhandler), new customexceptionhandler());//替换全局异常处理类

actionfilter,可以在action前/后增加逻辑

 public class customactionfilterattribute : actionfilterattribute
 {
     public override void onactionexecuting(httpactioncontext actioncontext)
     {
         console.writeline("1234567");
     }

     public override void onactionexecuted(httpactionexecutedcontext actionexecutedcontext)
     {
         console.writeline("2345678");
         actionexecutedcontext.response.headers.add("access-control-allow-origin",
"*");
     }
 }

webapi跨域请求

  这里对webapi跨域请求做下简单介绍。什么是跨域请求呢?就是浏览器请求时,如果a网站(域名+端口)页面里面,通过xmlhttprequest去请求b域名,这个就是跨域。这个请求时是跨域正常到达b服务器后端的,正常的响应(200)。但是浏览器是不允许这样操作的,除非在相应里面有声明(access-control-allow-origin)。

  这个是因为浏览器同源策略。出于安全考虑,浏览器限制脚本去发起蛞蝓请求。比如你想攻击别人的网站,得自己服务器发起请求,如果你搞个js,等于在客户端的电脑上去攻击别人。但是页面是script-src、img-href、jss/css/图片,这些是浏览器自己发起的,是可以跨域的。还有a标签,iframe标签也是可以的。浏览器自己的可以,但是用xhr去请求就是不行。

解决跨域的方法

  1、jsonp:脚本标签自动请求,请求回来的内容执行回调方法,解析数据

  2、cors,跨域资源共享。允许服务器在响应时,指定access-control-allow-origin,浏览器按照响应来操作

  nuget下面添加cors

 WebApi简介

 

 全局的都允许:

WebApi简介

 

action级别的:

WebApi简介

 

自定义一个一个标签,继承actionfilter attribute,执行完城后,加上actionexecutedcontext.response.headers.add("access-control-allow-origin", "*");

public class customactionfilterattribute : actionfilterattribute
{
    public override void onactionexecuting(httpactioncontext actioncontext)
    {
        console.writeline("1234567");
    }

    public override void onactionexecuted(httpactionexecutedcontext actionexecutedcontext)
    {
        console.writeline("2345678");
        actionexecutedcontext.response.headers.add("access-control-allow-origin", "*");
    }
}

因为跨域是浏览器控制的,在后端并没有跨域。

自动生成webapi文档(.net core下面基于swagger生成文档)

WebApi简介

 

 可以在浏览器中直接访问。

WebApi简介