zoukankan      html  css  js  c++  java
  • c# webapi2 实用详解

    本文介绍webapi的使用知识

    1. 发布webapi的问题

      配置问题
          webapi的项目要前端访问,需要在web.config配置文件中添加如下配置
          在system.webServer节点下面添加
              <modules runAllManagedModulesForAllRequests="true" />
              <httpProtocol>
              <customHeaders>
                  <add name="Access-Control-Allow-Origin" value="*" />
                  <add name="Access-Control-Allow-Headers" value="*" />
                  <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE" />
              </customHeaders>
              </httpProtocol>
          在Global.asax.cs文件中添加
              GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
              GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
      
    2. 最基础的demo

      public class ProductsController : ApiController
      {
          MyTest[] tests = new MyTest[]
          {
              new MyTest {Id = 1, Name = "苹果", Category = "水果", Price = 10},
              new MyTest {Id = 2, Name = "番茄", Category = "蔬菜", Price = 5},
              new MyTest {Id = 3, Name = "猪肉", Category = "肉类", Price = 20}
          };
          public IEnumerable<MyTest> GetAllProducts()
          {
              return tests;
          }
          public IHttpActionResult GetProduct(int id)
          {
              var product = tests.FirstOrDefault(p => p.Id == id);
              if(product == null)
              {
                  return NotFound();
              }
              return Ok(product);
          }
      }
      将程序编译好,然后发布出去就可以通过如下方式请求数据
      var axiosInstance = window.axios.create({
          baseURL: "http://192.168.31.198:8888/api",
          timeout: 5000
      })
      // 或者是products/2 获取具体某一条数据
      axiosInstance.get("products").then(function (res) {
          debugger
      }).catch(function (err) {
          console.log(err);
      })
      
    3. 控制器中方法的返回值

      返回void 
          如果一个get方法写成
              public void GetAllProducts(){}
              那么HTTP响应状态码是204,表示没有返回内容
      返回HttpResponseMessage
          如果返回的是HttpResponseMessage对象,那么请求的HTTP响应信息就是HttpResponseMessage对象
          public HttpResponseMessage GetAllProducts()
          {
              // 创建HttpResponseMessage对象,并且将返回值设置为 叶家伟
              HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "叶家伟"); 
              // 设置响应的返回内容,StringContent用来返回字符串的Json对象
              response.Content = new StringContent(@"{""a"": ""1""}", Encoding.UTF8); 
              // 设置响应头的缓存控制
              response.Headers.CacheControl = new CacheControlHeaderValue
              {
                  NoCache = false
              };
              return response;
          }
          可以直接设置  HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, tests);
          将tests可枚举对象作为返回结果
      返回IHttpActionResult
          IHttpActionResult是HttpResponseMessage的生产工厂
          如果一个控制器的方法返回一个IHttpActionResult,那么会自动调用ExecuteAsync方法创建HttpResponseMessage
          ApiController提供了很多的内置生产IHttpActionResult类型的函数,方便调用,比如OK,NotFound等
      其他类型的返回值    
          用户可以返回值类型和引用类型,web api使用媒体序列化器解析返回值类型,默认支持的数据格式有
          xml,json,bson,form-urlencoded,如果有其他类型需要自己手动的创建
          public MyTest[] GetAllProducts()
          {
              return tests;
          }
      
    4. 创建webapi文档页

      首先,安装 Install-Package Microsoft.AspNet.WebApi.HelpPage
      然后,在Global.asax文件中的Application_Start方法下添加
          AreaRegistration.RegisterAllAreas();
      然后,在Areas/HelpPage/App_Start/HelpPageConfig.cs,开启
          config.SetDocumentationProvider(new XmlDocumentationProvider(
          HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));
      在项目属性的生成中开启XML documentation file 添加 App_Data/XMLDocument.xml
      最后发布,将相关的资源包含到项目中来,然后生成,完事儿了发布即可
      剩下的就是写注释了
      
    5. 路由

      控制器中的方法可以使用 GET,POST,PUT和DELETE 开头的命名规则,来指定具体请求对应的方法
      当然,你也可以选择修饰符的方式来指定方法,支持的修饰符 HttpGet, HttpPut, HttpPost, or HttpDelete
          [HttpPost]
          public MyTest[] AllProducts()
          {
              return tests;
          }
          使用 [AcceptVerbs("GET", "HEAD")] 这种写法,可以支持多种http请求,并且还可以使用GET,POST,PUT和DELETE之外的方式
      另外如果你想把具体的方法的名称也加到请求的url中,那么可以配合上面的修饰符形式,结合下面的方法实现
          首先,将WebApiConfig.cs文件中的配置改成
              config.Routes.MapHttpRoute(
                  name: "DefaultApi",
                  routeTemplate: "api/{controller}/{action}/{id}",
                  defaults: new { id = RouteParameter.Optional }
              );
              还可以这样配置
              config.Routes.MapHttpRoute(
                  name: "DefaultApi",
                  routeTemplate: "api/{action}/{id}",
                  defaults: new { controller = "Department", id = RouteParameter.Optional }
              ); // 表示如果api满足routeTemplate那么使用Department控制器匹配,如果不满足则执行其他的路由匹配规则
          控制器中的方法的写法
              [HttpPost]
              public MyTest[] AllProducts()
              {
                  return tests;
              }
          url的请求方式
              post("products/AllProducts")
      如果,想无视某个方法,添加 [NonAction] 即可
      
    6. 路由配置

      路由模板,也就是路由配置中的routeTemplate选项
          "api/{controller}/yejiawei/{action}/{id}"
          其中使用花括号包裹的是占位符,可以有多个占位符
      路由参数默认值,是路由配置中的defaults参数,用来给方法的参数指定默认值
          new { id = RouteParameter.Optional } 表示id这个字段在匹配的时候可以有,也可以没有
          new { id = 2 } 表示id这个字段可以有也可以没有,但是如果没有会自动设置为2
          new { id = @"d+" } 可以使用正则限定id可以的值
      创建路由匹配规则的方式
          除了使用config.Routes.MapHttpRoute方法创建路由规则外,还可以使用如下的方式
              IHttpRoute myRoute = config.Routes.CreateRoute(
                  "api/{controller}/yejiawei/{id}",
                  new { id = RouteParameter.Optional },
                  null
              );
              config.Routes.Add("MyRoute", myRoute);
          MapHttpRoute方法的参数除了上面将到的name,routeTemplate,defaults还有如下的参数
              constraints 使用正则表达式,限定传参的要求
                  constraints: new { id = @"/d+"}
              handler 每个请求的分发函数
      路由字典
          var routeData = Request.GetRouteData();
          IDictionary<string, object> routes = routeData.Values;
          foreach(KeyValuePair<string, object> item in routes)
          {
              Console.WriteLine("Key: {0}, Value{1}", item.Key, item.Value);
          }
          路由匹配到的所有占位符都保存在上面的routes里面,
          如果一个占位符被设置成 RouteParameter.Optional 那么如果这个占位符没有提供的话,就不会添加到路由占位符对象里面
          defaults选项中也可以包含routeTemplate中不存在的占位符,这样可以保存一些额外的信息
      路由参数的获取方式
          默认情况下简单的数据类型从URL中获取,复杂的数据类型从请求体中获取
      
    7. 基于路由属性的控制器方法

      上面我们讲的都是基于约定的路由,因为路由规则都是在配置文件中预先定义好的,可以使用路由属性更加灵活的配置请求
      路由配置文件中
          config.MapHttpAttributeRoutes(); 这段代码就是属性路由的配置,当方法没有配置属性路由,默认使用基于约定的路由
      使用例子
          [Route("yejiawei/{id}/haha")]
          [HttpPost]
          public int AllProducts(int id)
          {
              return id;
          }
          匹配的路由 yejiawei/3/haha 注意api已经不需要了
      可以给控制器添加路由前缀
          [RoutePrefix("yejiawei")]
          那么控制器的方法只需要简写成 
              [Route("{id}/haha")] 即可
          如果有的控制器方法的前缀不一致可以使用~重写
              [Route("~/api/{id}/haha")]
          还可以添加路由约束
              [Route("{id:int}/haha")]
              支持的路由约束还有很多,自行查阅
          添加可选的路由参数
              [Route("{id:int?}/haha")] 控制器的方法要设置默认值
          添加路由参数默认值
              [Route("{id:int=2}/haha")]   
      
    8. 路由参数

      参数名称必须相同,不区分大小写
      get请求
          值类型参数从url中获取
              public string GetDepartments(string str)
              {
                  return str;
              }
              符合的api
              Departments?str=bbb
      post请求
          值类型参数默认从url中获取
              public string PostDepartments(string str)
              {
                  return str;
              }
              符合的api
              Departments?str=ccc
          引用类型的参数默认从请求体中获取
              public class Demo
              {
                  public string Name { get; set; }
                  public string Sex { get; set; }
              }
              [Route("yejiawei/Departments")]
              public Demo PostDepartments(Demo obj)
              {
                  return obj;
              }
              符合的api
                  axios.post("yejiawei/Departments", {
                      Name: "yejiawei",
                      Sex: "女"
                  }).then( (res) => { debugger }).catch( (err) => {console.log(err);})
              注意:应用类型的参数,只能存在一个,put方法的处理方式和post一致
      如果想修改默认的参数获取规则,可以使用 
          [FromUri] 可以让引用类型的属性从uri中的参数获取
              public class Demo
              {
                  public string Name { get; set; }
                  public string Sex { get; set; }
              }
              public Demo GetDepartments([FromUri] Demo obj)
              {
                  return obj;
              }
              匹配的api如下
              axiosInstance.get("api/Departments", {
                  params: {
                      Name: "yejiawei",
                      Sex: "女"
                  }
              })
          [FromBody] 可以让值类型从请求体中获取
              [Route("yejiawei/BodyTest")]
              public string Post ([FromBody] string name)
              {
                  return name;
              }
              匹配的api如下
              axiosInstance.post("yejiawei/BodyTest", "=yejiawei")
              如果你不想上面这种奇怪的写法,可以参考如下
                  首先定义一个类
                      public class Test
                      {
                          public string Name { get; set; }
                      }
                  控制器方法改成如下
                      [Route("yejiawei/BodyTest")]
                      public string Post ([FromBody] Test name)
                      {
                          return name.Name;
                      }
                  符合的api写法
                      axiosInstance.post("yejiawei/BodyTest", { name: "yejiawei" })
              注意:
                  [FromBody] 只能使用于一个参数
      
    9. webapi跨域解决

      使用开局介绍的配置只能简单的解决get,post不带请求体这类的跨域请求,如果想彻底的跨域,请看下面
      第一步,安装 Install-Package Microsoft.AspNet.WebApi.Cors
      第二步,在WebApiConfig.cs中添加 
          config.EnableCors();
      第三步,在会被跨域的控制器上添加 
          [EnableCors(origins: "http://mywebclient.azurewebsites.net", headers: "*", methods: "*")]
          origins对应前端发请求的地址
      第四步,注释掉本文开头所说配置中的
          <add name="Access-Control-Allow-Origin" value="*" />
          <add name="Access-Control-Allow-Headers" value="*" />
          <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE" />
          干掉上面这三句句话,此时你的跨域配置已经ok了
      注:
          [EnableCors] 可以单独给一个控制器方法使用
          [DisableCors] 可以给一个单独的控制器方法禁用跨域
          开启全局跨域
              让任何一个控制器都支持跨域,只需要将上面的第二步改成
              var cors = new EnableCorsAttribute("http://localhost:3000", "*", "*");
              config.EnableCors(cors);
          设置规则的顺序
              Action > Controller > Global
          origins参数详解
              origins支持一逗号分隔,写多个跨域地址,也可以支持通配符 * 
          methods参数
              methods参数用来指定具体的http方法,可以以逗号分隔写多个,也支持通配符
          headers参数不需要手动配置,写成通配符即可
      
    10. 请求和响应的数据格式化

      MIME类型指定了数据的基本格式
      在HTTP请求中 
          Accept头指定了客户端期望得到的数据格式
          Content-Type指定了请求发送的数据格式
      Web Api根据Content-Type头指定的格式类型,将请求的数据转化成对应的CLR对象,同时根据Accept头将响应的结果序列化成对应的数据格式,Web Api 内置支持JSON, XML, BSON, form-urlencoded 序列化器,这就是说这几种格式的数据Web Api会自动处理
      所以仅仅改变Content-Type和Accept就可以方便的序列化请求或者响应的数据
      媒体类型格式序列化器
          webapi能够自动识别并且处理JSON和XML数据格式,是因为Web API包含以下的内置媒体格式化器
          媒体格式化器类 
              JsonMediaTypeFormatter 处理的MIME类型 application/json, text/json 用来处理JSON格式化
              XmlMediaTypeFormatter  处理的MIME类型 application/xml, text/json 用来处理XML格式化
              FormUrlEncodedMediaTypeFormatter 处理的MIME类型 application/x-www-form-urlencoded 处理HTML表达URL编码的数据
              JQueryMvcFormUrlEncodedFormatter 处理的MIME类型 application/x-www-form-urlencoded 处理模型绑定的HTML表单URL编码的数据
          访问所有的媒体格式化器
              [Route("yejiawei/MIMEType")]
              public IEnumerable<string> Get()
              {
                  IList<string> formatters = new List<string>();
                  foreach(var item in GlobalConfiguration.Configuration.Formatters)
                  {
                      formatters.Add(item.ToString());
                  }
                  return formatters.AsEnumerable<string>();
              }  
          单独访问指定的媒体格式化器
              GlobalConfiguration.Configuration.Formatters.JsonFormatter.GetType().FullName
              GlobalConfiguration.Configuration.Formatters.FormUrlEncodedFormatter.GetType().FullName
      webapi 处理Json格式的数据默认使用 PascalCase, 可以设置成支持camelCase的
          在WebApiConfig.cs中添加如下代码
          config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();  
      
    11. WebApi过滤器

      过滤器可以在控制器方法调用之前和之后执行
      简单的一个用法
          先创建一个实现ActionFilterAttribute的类
              public class LogAttribute : ActionFilterAttribute
              {
                  public LogAttribute()
                  {
                  }
                  public override void OnActionExecuting(HttpActionContext actionContext)
                  {
                      Trace.WriteLine(string.Format("Action Method {0} executing at {1}", actionContext.ActionDescriptor.ActionName, DateTime.Now.ToShortDateString()), "Web API Logs");
                  }
                  public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
                  {
                      Trace.WriteLine(string.Format("Action Method {0} executed at {1}", actionExecutedContext.ActionContext.ActionDescriptor.ActionName, DateTime.Now.ToShortDateString()), "Web API Logs");
                  }
              }
          然后在控制器类的头部添加属性 [Log] 即可,当控制器调用方法时,OnActionExecuting会执行,当方法调用完毕以后OnActionExecuted会执行
      
    12. WebApi使用注意点

      在写post,put请求之前,要先使用 ModelState.IsValid 判断一下,传进来的引用类型参数是否包含有效值
      
  • 相关阅读:
    bootstrap 幻灯片(轮播)
    图片轮播
    JS 标签页切换(复杂)
    JS 标签页切换
    js获取日期时间
    HTML标签marquee实现滚动效果
    webstorm 快捷键,很多人想知道的三个常用快捷键
    js红包算法随机分配
    node.js express,ejs后端模板
    递归思路简单例子
  • 原文地址:https://www.cnblogs.com/ye-hcj/p/8076202.html
Copyright © 2011-2022 走看看