路由其实也可以很简单-------Asp.net WebAPI学习笔记(一) ASP.NET WebApi技术从入门到实战演练 C#面向服务WebService从入门到精通 DataT...

路由其实也可以很简单-------Asp.net WebAPI学习笔记(一)

 

  MVC也好,WebAPI也好,据我所知,有部分人是因为复杂的路由,而不想去学的。曾经见过一位程序猿,在他MVC程序中,一切皆路由,url中是完全拒绝"?"和“&”。对此,我也不好说什么,搞不好是个人风格。路由虽然重要,但其实也只是实现MVC的一种手段,并非你用的路由越多,你的url完全不使用参数,你的MVC就越纯正。说实话,笔者一开始对路由也感到恐惧,但是阅读了官方文档后,发现路由其实也可以很简单,关键在于我们如何使用。由于笔者也是初学者,有什么错漏的地方,欢迎大家指正。

  本系列文章使用的是vs2017,WebAPI版本是2。本系列大多数内容并非原创,而是来自官网的教程(https://docs.microsoft.com/en-us/aspnet/web-api/overview/getting-started-with-aspnet-web-api/),如果你英文不好,可以将链接中的en-us改成zh-cn。中文版地址:https://docs.microsoft.com/zh-cn/aspnet/web-api/overview/getting-started-with-aspnet-web-api/。不过建议你可以的话,还是看英文版本,因为有些翻译是完全走样。

  废话不多说,马上来看看如何新建一个WebAPI项目。打开vs2017,文件-新建-项目

  

  选择空模板,勾选webapi

  

  添加模型类,在右侧资源管理器的Models文件夹上右键-添加-类

  类的代码如下:

复制代码
 1 namespace ProductsApp.Models
 2 {
 3     public class Product
 4     {
 5         public int Id { get; set; }
 6         public string Name { get; set; }
 7         public string Category { get; set; }
 8         public decimal Price { get; set; }
 9     }
10 }
复制代码

  添加空的控制器,在Controllers文件夹上右键-添加-控制器,选择Web API2 控制器 - 空

  控制器名称为:ProductsController,代码如下:

复制代码
using ProductsApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web.Http;

namespace ProductsApp.Controllers
{
    public class ProductsController : ApiController
    {
        Product[] products = new Product[] 
        { 
            new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 }, 
            new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M }, 
            new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M } 
        };

        public IEnumerable GetAllProducts()
        {
            return products;
        }

        public IHttpActionResult GetProduct(int id)
        {
            var product = products.FirstOrDefault((p) => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }
            return Ok(product);
        }
    }
}
复制代码

   这样,一个简单的WebAPI就完成了。完成后,文件结构如下:

  调用的话,我们直接使用IIS新建一个网站,端口为1111

  

 

  打开edge浏览器,输入地址:http://localhost:1111/api/Products,效果如下(注意,每次修改完代码后,需要重新生成一下), 

  一切都很简单,代码也都不复杂,不过明显有两个问题,一个是,为什么默认就调用了GetAllProducts()了,另一个是,我们明明返回一个列表的,怎么到了客户端就变成json了呢?

  第一个问题,就是本文所要研究的问题。说到路由,笔者想起一桩往事。笔者自从接触asp.net以来,一直都在使用webform,即使在MVC大行其道的时候。有一次,接到一个外包项目,利用某开源社区框架做业务的扩展,由于该开源框架用的是MVC,于是就问对方的技术负责人,业务扩展项目是否也必须用MVC,对方答道,用MVC干嘛,绕来绕去不是更麻烦吗?他这句话让我深以为然,大有惺惺相惜之感。我这样说,并非要贬低MVC,更无意挑起MVC与WebForm之争,而是在遍地MVCer的情况下,还能找到WebFormer而高兴。实际上,只要能满足客户要求,谁会在意你用MVC还是WebForm呢。

  废话不多说,回到我们的问题,为什么我们输入地址:http://localhost:1111/api/Products,就是在调用GetAllProducts()呢?首先,我们看看App_Start文件夹下的WebApiConfig.cs文件,这个文件是用来配置路由的,代码如下: 

复制代码
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API 配置和服务
            // Web API 路由
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
复制代码

  上面的代码,实质上就是定义了一个默认的路由规则。

  我们再看看Global.asax

        protected void Application_Start()
        {
            GlobalConfiguration.Configure(WebApiConfig.Register);
        }

  很明显,在程序第一次启动的时候,我们的路由规则就被配置加载了。这个默认的规则就是 api/{controller}/{id},其中{controller}匹配一个控制类,例如ProductsController,{id}是可选的,匹配的是public方法(也就是action)的参数。那么,Controller里的public方法,也就是action该如何匹配呢?

  从官方的文档可以查到这么几句话:If you are familiar with ASP.NET MVC, Web API routing is very similar to MVC routing. The main difference is that Web API uses the HTTP method, not the URI path, to select the action。意思就是,如果你熟悉MVC,那么API的路由是跟MVC的路由非常相似的。两者之间的不同,是Web API使用http方法,而非URI路径去选择action。这里的action,就是我们Controller里面的public 方法。

  也就是说,默认路由api/{controller}/{id},首先匹配一个Controller类,然后用http请求方法匹配Action方法名,最后,用{id},匹配Action中的参数。

  http请求方法是什么东西?如果你是传统的asp开发者,或是php开发者,相信都会非常熟悉。例如我们以前写表单html,通常都会这样写:

....

  里面的method就是我们所说的http请求方法,最常见的就是get和post,get的话,就是将参数放到url上去提交,post的话,参数不会显示在url中。更多的http方法,可以点击这里。

  既然知道WebAPI的默认路由,是用http请求方法去匹配控制类中的action,那么就好办了,我们在地址栏输入地址:http://localhost:1111/api/Products ,其实就是相当于在使用get方法与ProductsController中的Action进行匹配了。

  然而,上面代码中,两个Action方法都没明确表明是用什么http请求方法,那怎么确定调用哪一个方法呢?get跟GetAllProducts()到底有什么关系呢,以至于GetAllProducts()可以被默认调用?或许有的人已经看出来了,没错,调用的方法GetAllProducts()那么巧,也是以Get开头的。这就是我们匹配的其中一个条件。如果Controller中,public方法的名字(也就是action的名字),是以"Get", "Post", "Put", "Delete", "Head", "Options", 或 "Patch"开头,那么按照约定,该方法(action)匹配对应的http请求方法的调用。如果开头没有上述的关键字,默认表示该方法只支持Post。

  例如GetAllProducts()方法,就表示使用http的get方法调用。DeleteProduct(int id)就表示用http的deletel方法调用。由于我们调用的地址是:http://localhost:1111/api/Products,翻译成匹配规则就是,匹配ProductsController中,一个使用get,同时没有参数的Action(也就是public 方法),即GetAllProducts()。如果我们有另一个Get方法,同时也是没有参数的话,就会报错。例如,我们增加一个方法:

        public string GetTest()
        {
            return "GetTest is called";
        }

  该方法明显也是匹配Get方法,同时没有参数。重新生成下项目,然后用PostMan调用一下,会发现匹配多个的错误。(PostMan的安装就不说了,很简单,不断下一步。)

  

 

  我们在原来的基础上,修改一下ProductsController的代码,增加一个方法(红色字体部分),代码如下:

复制代码
using ProductsApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web.Http;

namespace ProductsApp.Controllers
{
    public class ProductsController : ApiController
    {
        Product[] products = new Product[]
        {
            new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
            new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
            new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
        };
        
        public IEnumerable GetAllProducts()
        {
            return products;
        } 

        public IHttpActionResult GetProduct(int id)
        {
            var product = products.FirstOrDefault((p) => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }
            return Ok(product);
        }

        public string SayHello()
        {
            return "Hello,World";
        }
    }
}
复制代码

  

  我们在PostMan中输入地址:http://localhost:1111/Products,http请求方法,选择Post,按照我们的规则,应该会调用SayHello方法,实际效果如下:

  

  如果我们将url改成:http://localhost:1111/api/Products/1,但方法依然是Post,那么按照上面说的,先找Post的方法,而三个方法中,只有SayHello符合,虽然后面加了id,并且值为1,由于它是可选的,所以,在post下,调用的依然是SayHello,如下图:

  

  假如,我们将Post方法改为Get,那么就会选择调用我们的GetProduct方法,效果如下:

  

  

  这个就是WebAPI默认的路由,主要使用Http请求方法来匹配Controller里的Action。而这个匹配的规则,就是使用前缀来决定哪一个最匹配,如果前缀都不是http方法,表示默认匹配Post。是不是感觉很简单呢,如果这样还觉得复杂,没关系,下面还有更简单的方法,就是属性路由。

   上面的这种路由匹配规则,其实是属于约定的路由。在调用的时候,你还多多少少需要想一下,究竟url是怎样,会调用哪个方法,会不会有多个方法同时匹配等等。但是使用属性路由,你就可以完全的“精准定位”。属性路由,就是利用特性,重新定义路由。例如:

复制代码
        [HttpPost]
        [Route("aaa/bbb")]
        public IEnumerable GetAllProducts()
        {
            return products;
        }
复制代码

  HttpPost强制了这个方法是需要使用Post来调用,Route强制定义了这个方法的调用路径。虽然这个方法是以Get开头,但是[HttpPost]优先级大于这个约定,我们用PostMan来测试下,我们依然先输入之前的地址:http://localhost:1111/Products,方法为Get,可以看到抛出Not Found这个错误

  

  直到我们将地址改为:http://localhost:1111/aaa/bbb,http方法改为Post的时候,调用才成功。是不是太厉害了,我们可以随便定义访问这个方法的路由,什么约定的规则完全可以置之不理,我们可以完全实现“精准定位”,路由变得不再复杂了,一切都在我们的掌握之中。

  那么,我们怎样才能使用这种属性路由呢,首先,我们要打开App_Start文件夹中的WebApiConfig.cs文件,确保一下这句代码存在:

复制代码
        public static void Register(HttpConfiguration config)
        {
            // 确保开启了属性路由
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
复制代码

  其次,要引入命名空间:using System.Web.Http;就是这么简单,我们就可以使用属性路由。看到WebApiConfig.cs的代码,有人担心,会不会是因为config.MapHttpAttributeRoutes()代码在前,所以优先级才大于约定的路由呢,我们可以换一下次序,代码改成这样:

复制代码
       public static void Register(HttpConfiguration config)
        {
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        //次序在后
            config.MapHttpAttributeRoutes();
        }
复制代码

  结果发现,属性路由的优先级依然大于约定的路由,如果你还担心,大可直接删除约定的路由,将上述的代码改成这样:

        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();
        }

  这样,你就可以完全不用考虑约定的路由对属性路由的影响,一切的映射都在你的牢牢掌握中。不过坏处就是,你的每个方法,都要显式指定http方法和路由。但我觉得这个代价是值得的,因为我们再不用花时间去绕来绕去,再不用担心增加了新方法会不会造成路由冲突等问题,我们只需定好命名规则,保证我们每个方法定义的路由不重复即可。说着说着,我也觉得自己跟文章开头说的那位程序猿一样,在走向一个极端,他是在玩命的用路由,而我是在拼命的阻止路由的多匹配性,追求唯一确定,尽量不让路由造成我的负担,也许,这也是一种风格?

  剩下的都是很简单,例如,路由前缀,还是用官方的例子:

复制代码
public class BooksController : ApiController
{
    [Route("api/books")]
    public IEnumerable GetBooks() { ... }

    [Route("api/books/{id:int}")]
    public Book GetBook(int id) { ... }

    [Route("api/books")]
    [HttpPost]
    public HttpResponseMessage CreateBook(Book book) { ... }
}
复制代码

  每个方法的路由前缀都是“api/books",是不是显得很重复,我们可以将前缀抽取,为整个控制器增加公共的前缀,代码如下:

复制代码
[RoutePrefix("api/books")]
public class BooksController : ApiController
{
    // GET api/books
    [Route("")]
    public IEnumerable Get() { ... }

    // GET api/books/5
    [Route("{id:int}")]
    public Book Get(int id) { ... }

    // POST api/books
    [Route("")]
    public HttpResponseMessage Post(Book book) { ... }
}
复制代码

  路由前缀的重写,我们可以使用波浪符对前缀进行重写,例如:

复制代码
[RoutePrefix("api/books")]
public class BooksController : ApiController
{
    // GET /api/authors/1/books
    [Route("~/api/authors/{authorId:int}/books")]
    public IEnumerable GetByAuthor(int authorId) { ... }

    // ...
}
复制代码

   除此之外,还有路由约束,例如Route("api/books/{id:int}"),表示id是一个32位整数,如果是可选的,可以在后面加"?",例如Route("api/books/{id:int?}")

  更详细的使用可以参考官网文档:https://docs.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

    有了属性路由,我们甚至可以极端的抛弃约定的路由,从而实现”精准定位“,一切定位都可以牢牢的掌握在自己手中。相信这样,大家应该不会再害怕面对路由了吧。

 

 

  

  

 

 

  

ASP.NET WebApi技术从入门到实战演练

 

一、课程介绍   

曾经有一位不知名的讲师说过这么一句名言: 一门RPC技术不会,那么千万万门RPC技术将都不会!在今天移动互联网的时代,作为攻城师的我们,谁不想着只写一套API就可以让我们的Web, Android APP, IOS APP, iPad APP, Hybired APP, H5 Web共用共同的逻辑呢? 希望大家学完本次分享课程能够达到使用ASP.NET WebAPI技术,并且掌握如何优雅提供对外API接口。

本次分享课程适合人群范围如下:

1)、对ASP.NET WebApi技术零基础的童鞋们。

2)、对ASP.NET MVC技术零基础但是想快速上手的童鞋们。

3)、本次分享课程属于ASP.NET WebApi基础课程,课程侧重点是零基础快速上手和实战演练。

本次分享课程包含以下干货知识点:

1)、如何快速上手创建一个ASP.NET WebApi应用程序。

2)、API & HTTP接口调试模拟请求工具/插件基本介绍和使用。

3)、如何通过VS快速部署发布我们的WebApi服务应用程序。

4)、ASP.NET WebAPI返回数据同时支持XML和JSON两种格式。

5)、ASP.NET WebAPI自定义HTTP参数绑定支持多参数POST请求。(强烈推荐)

6)、ASP.NET WebAPI如何优雅的通过Action过滤器的方式来实现HTTP压缩。

7)、ASP.NET WebAPI实战演练之客户端应用程序如何调用服务商提供的远程WebApi接口服务。

8)、 Q&A环节:大家一起聊一聊ASP.NET WebAPI

           8.1、ASP.NET WebAPI如何优雅的提供对外接口服务。

           8.2、ASP.NET WebAPI接口方法如何实现多版本控制。

          8.3、ASP.NET WebAPI如何保证客户端以安全的方式进行访问。

如果您同样对本次分享《ASP.NET WebApi技术从入门到实战演练》课程感兴趣的话,那么请跟着阿笨一起学习吧。

废话不多说,直接上干货,我们不生产干货,我们只是干货的搬运工。

二、什么是WebAPI ?  

三、为什么选择WebAPI ?

四、WebApi技术从入门到实战演练实例分享

1)、如何快速上手创建一个ASP.NET WebApi应用程序。

2)、API & HTTP接口调试模拟请求工具/插件基本介绍和使用。

3)、ASP.NET WebAPI返回数据同时支持XML和JSON两种格式。

4)、ASP.NET WebAPI自定义HTTP参数绑定支持多参数POST请求。

5)、ASP.NET WebAPI 如何优雅的通过Action过滤器的方式来实现HTTP压缩。

6)、ASP.NET WebAPI实战演练之WinForm 桌面应用程序如何调用我们的WebApi服务。

 五、Q&A环节:大家一起聊一聊ASP.NET WebAPI技术

5.1、ASP.NET WebAPI如何优雅的提供对外接口服务。

5.2、ASP.NET WebAPI接口方法如何实现多版本控制。

5.3、ASP.NET WebAPI如何保证客户端以安全的方式进行访问。

六、总结

本次分享课程需要思考的问题:在移动互联网的时代, Web服务已经成为了异构系统之间的互联与集成的主要手段,各种 Web服务几乎都采用REST风格的Web Api来构建。 通过Http协议的形式来. 以Get/Post方式发送请求, 返回json格式(数据更小巧且自描述能力强)的数据。目前,各大互联网公司, 对自身的REST Api设计有各自的标准,他们的Api 的设计也非常成熟。 那么,我们应该如何更好的设计我们的接口, 来提高我们 API 的可用性,易用性,可维护性与可扩展性呢?由于ASP.NET WEBAPI知识点涉及范围比较广,阿笨本次分享课程侧重点不一样,所以带个大家的知识不能做到面面俱到,大家也可以根据自己的实际工作经验总结一套关于对Restful风格的WebApi架构的知识。

最后还是送大家一句话:师父领进门修行在个人,希望大家在学习的道路上一直坚持下去!

作者:阿笨 

      官方QQ一群(已满):422315558跟着阿笨一起玩NET 

      官方QQ二群(加入):574187616跟着阿笨一起玩NET(二) 

      个人讲师课堂主页:http://study.163.com/instructor/2544628.htm 

      个人微信公众号课程主页:http://dwz.cn/ABenNET

 

 
 
 

C#面向服务WebService从入门到精通

 

《C#面向服务WebService从入门到精通》包含以下两个部分:

一、《C#远程调用技术WebService修炼手册【基础篇】》
本次分享课您将学习到以下干货知识点:
1)、WebService技术调用原理图。
2)、C# WebService常用的几种调用方式。
3)、C# WebService调试小技巧和开发必备工具分享。
4)、实战演练之如何通过优雅的方式进行WebService调用。  优不优雅,你看了就知道了,我们尽可能的Write Less Do More !(强烈推荐)。

二、《C#远程调用技术WebService葵花宝典【高级篇】》
本次阿笨的分享课包含以下方面:
1、C# WebService常用的几种调用方式。
2、WebService如何使用异步调用。(基于基于异步委托BeginInvoke/EndInvoke、基于委托事件机制、基于Async Task异步编程模式)
3、C# WebService如何保证客户端以安全的方式进行访问。
4、C#通过反射(Reflection)动态创建WebService实例。
5、微软IOC框架Unity的基本使用。
 
观看地址:
http://163.lu/8eLv53
 

 

 

DataTable与List相互转换

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
using  System;
using  System.Collections.Generic;
using  System.ComponentModel;
using  System.Data;
using  System.Linq;
using  System.Reflection;
using  System.Text;
using  System.Threading.Tasks;
 
namespace  Common
{
     public  class  ModelHelper
     {
         public  static  List TableToEntity(DataTable dt)  where  T :  new ()
         {
             List lists =  new  List();
             if  (dt.Rows.Count > 0)
             {
                 foreach  (DataRow row  in  dt.Rows)
                 {
                     lists.Add(SetVal( new  T(), row));
                 }
             }
             return  lists;
         }
 
         public  static  T SetVal(T entity, DataRow row)  where  T :  new ()
         {
             Type type =  typeof (T);
             PropertyInfo[] pi = type.GetProperties();
             foreach  (PropertyInfo item  in  pi)
             {
                 if  (row[item.Name] !=  null  && row[item.Name] != DBNull.Value)
                 {
                     if  (item.PropertyType.IsGenericType && item.PropertyType.GetGenericTypeDefinition().Equals( typeof (Nullable<>)))
                     {
                         Type conversionType = item.PropertyType;
                         NullableConverter nullableConverter =  new  NullableConverter(conversionType);
                         conversionType = nullableConverter.UnderlyingType;
                         item.SetValue(entity, Convert.ChangeType(row[item.Name], conversionType),  null );
                     }
                     else
                     {
                         item.SetValue(entity, Convert.ChangeType(row[item.Name], item.PropertyType),  null );
                     }
                 }
             }
             return  entity;
         }
 
         public  static  DataTable EntityToDataTable(List list)  where  T :  new ()
         {
             if  (list ==  null  || list.Count == 0)
             {
                 return  null ;
             }
 
             DataTable dataTable =  new  DataTable( typeof (T).Name);
             foreach  (PropertyInfo propertyInfo  in  typeof (T).GetProperties())
             {
                 if  (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition().Equals( typeof (Nullable<>)))
                 {
                     Type conversionType = propertyInfo.PropertyType;
                     NullableConverter nullableConverter =  new  NullableConverter(conversionType);
                     conversionType = nullableConverter.UnderlyingType;
                     dataTable.Columns.Add( new  DataColumn(propertyInfo.Name, conversionType));
                 }
                 else
                 {
                     dataTable.Columns.Add( new  DataColumn(propertyInfo.Name, propertyInfo.PropertyType));
                 }
             }
 
             foreach  (T model  in  list)
             {
                 DataRow dataRow = dataTable.NewRow();
                 foreach  (PropertyInfo propertyInfo  in  typeof (T).GetProperties())
                 {
                     object  value = propertyInfo.GetValue(model,  null );
                     if  (value !=  null )
                     {
                         dataRow[propertyInfo.Name] = propertyInfo.GetValue(model,  null );
                     }
                     else
                     {
                         dataRow[propertyInfo.Name] = DBNull.Value;
                     }
                 }
                 dataTable.Rows.Add(dataRow);
             }
             return  dataTable;
         }
     }
}
 
 

 

 

  

你可能感兴趣的:(测试,c#,postman)