路由其实也可以很简单-------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<Product> 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,通常都会这样写:

<form action="form_action.asp" method="get">
 ....
</form>

  里面的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<Product> 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<Product> 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<Book> 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<Book> 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<Book> 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

官方QQ二群(加入):574187616

个人讲师课堂主页: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的基本使用。

 
观看地址:
 

DataTable与List<T>相互转换

 
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<T> TableToEntity<T>(DataTable dt) where T : new()
        {
            List<T> lists = new List<T>();
            if (dt.Rows.Count > 0)
            {
                foreach (DataRow row in dt.Rows)
                {
                    lists.Add(SetVal(new T(), row));
                }
            }
            return lists;
        }
 
        public static T SetVal<T>(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<T>(List<T> 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;
        }
    }
}
 
 

  

路由其实也可以很简单-------Asp.net WebAPI学习笔记(一) ASP.NET WebApi技术从入门到实战演练 C#面向服务WebService从入门到精通 DataTable与List<T>相互转换的更多相关文章

  1. ASP.NET MVC 学习笔记-2.Razor语法 ASP.NET MVC 学习笔记-1.ASP.NET MVC 基础 反射的具体应用 策略模式的具体应用 责任链模式的具体应用 ServiceStack.Redis订阅发布服务的调用 C#读取XML文件的基类实现

    ASP.NET MVC 学习笔记-2.Razor语法   1.         表达式 表达式必须跟在“@”符号之后, 2.         代码块 代码块必须位于“@{}”中,并且每行代码必须以“: ...

  2. ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则

    ASP.NET MVC 学习笔记-7.自定义配置信息   ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...

  3. ASP.NET MVC 学习笔记-1.ASP.NET MVC 基础

    ASP.NET MVC在原来ASP.NET的基础上抛弃了基于页面的架构风格,使用了全新的MVC(模型-视图-控制器)架构的一种技术. 目前,它和ASP.NET都共存在.NET Framework之上. ...

  4. ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用

    Ajax的全名为:Asynchronous Javascript And XML(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术.Ajax技术首先向Web服务器发送 ...

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

    MVC也好,WebAPI也好,据我所知,有部分人是因为复杂的路由,而不想去学的.曾经见过一位程序猿,在他MVC程序中,一切皆路由,url中是完全拒绝"?"和“&”.对此,我 ...

  6. Asp.Net Core WebApi学习笔记(四)-- Middleware

    Asp.Net Core WebApi学习笔记(四)-- Middleware 本文记录了Asp.Net管道模型和Asp.Net Core的Middleware模型的对比,并在上一篇的基础上增加Mid ...

  7. 输出的数据格式是如何决定的-------Asp.net WebAPI学习笔记(二)

    在上一篇文章<路由其实也可以很简单>,我们解决了路由问题,这篇文章,我们来研究剩下的另一个问题,为何我们的方法返回的是一个列表,输出到客户端的时候,变成json呢,大家应该还记得我们上一篇 ...

  8. ASP.NET MVC学习笔记-----Filter

    ASP.NET MVC学习笔记-----Filter(1) Filter类型 接口 MVC的默认实现 Description Authorization IAuthorizationFilter Au ...

  9. [ASP.NET MVC] ASP.NET Identity学习笔记 - 原始码下载、ID型别差异

    [ASP.NET MVC] ASP.NET Identity学习笔记 - 原始码下载.ID型别差异 原始码下载 ASP.NET Identity是微软所贡献的开源项目,用来提供ASP.NET的验证.授 ...

随机推荐

  1. Mybatis 在 insert 之后想获取自增的主键 id,但却总是返回1

    记录一次傻逼的问题, 自己把自己蠢哭:Mybatis 在 insert 之后想获取自增的主键 id,但却总是返回1 错误说明: 返回的1是影响的行数,并不是自增的主键id: 想要获取自增主键id,需要 ...

  2. UVM基础之-------uvm report机制的使用

    后面的例子我会继续补充: 1. 因为uvm默认定义的message格式比较长,非常不利于debug过程中的分析使用,一般情况下,开始使用uvm,都要利用uvm_report_server重新定义mes ...

  3. Android 读取asset文件

    * * 从Assets中读取图片 */ private Bitmap getImageFromAssetsFile(String fileName) { Bitmap image = null; As ...

  4. grunt 全局使用

    grunt 不同地方使用时需要将插件下载的当前文件夹,这是因为查找模块时是当前路径,这会造成多个工程使用时会需要下载多次,而这些东西又不应该存在于工程之中,所以应该将所有模块全局安装,然后在工程下面只 ...

  5. Windows提高_2.3第三部分:内核区同步

    第三部分:内核区同步 等待函数(WaitForObject) 等待函数的形式 单个:WaitForSingleObject 多个:WaitForMultipleObjects 一个可以被等待的对象通常 ...

  6. 安卓app测试之Monkey日志分析

    转:原文:https://blog.csdn.net/a136332462/article/details/76066909  一.一般测试结果分析-搜索关键字: 1.无响应问题可以在日志中搜索 “A ...

  7. java项目其他基础配置

    创建完maven项目之后. 1.pom.xml文件配置项目相关的架包. 2.src.main.resources下边 创建文件夹:spring以及mapper. 3.src.main.resource ...

  8. centos7服务器安装fail2ban配合Firewalld防护墙防止SSH爆破与防护网站CC攻击

    centos7服务器安装fail2ban配合Firewalld防护墙防止SSH爆破与防护网站CC攻击 1.检查firewalld是否启用 #如果您已经安装iptables建议先关闭 service i ...

  9. 01Struts 2

    Struts 2 Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交 ...

  10. elk大纲

    一.ELK功能概览 1.检索 2.数据可视化--实时监控(实时刷新) nginx 访问量 ip地区分布图(大数据) 3.zabbix 微信联动报警 4.大数据日志分析平台(基于hadoop) 二.ka ...