ASP.NET web api汇总

Posted it-dennis

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ASP.NET web api汇总相关的知识,希望对你有一定的参考价值。

ASP.NET WEB API

与WEB API有关的类型 

HttpMessageHandler(System.Net.Http)(消息处理器)

表示Http请求的处理程序,处理程序类似于Http管道,它们是链式调用,所以可以自定义更多的处理程序。

HttpClient(System.Net.Http)(Http客户端)

表示客户端请求的类,可以配置请求的WEB API地址、Http报头、异步发送请求和读取服务端响应的Http报文等操作。HttpClient默认的构造函数就是利用HttpMessageHandler处理Http请求,而开发人员也可以在初始化HttpClient的时候向其构造函数传递一个自定义的消息处理器。

BaseAddress
//请求的地址主机名和端口号或域名

DefaultRequestHeaders
//请求报头的集合,提供了Add方法用于添加报头,报头以键值对的方式添加
//示例:
request.DefaultRequestHeaders.Add( "Accept", "application/json" );
//另一种方式是先创建报头对象,然后像下面这样添加:
request.DefaultRequestHeaders.Accept.Add( new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue( "application/json" ) ); //MediaTypeWithQualityHeaderValue表示Http报文的Accept请求头,更多报头可参考MSDN。

GetAsync( string uri )
//异步发起对WEB API的调用,此方法会自动开启一个新的线程Task,返回一个Task<HttpResponseMessage>

PostAsJsonAsync( string uri, T value )
//将value序列化为json字符串,异步提交Post请求到参数指定的地址,此方法会自动开启一个新的线程Task,返回一个Task<HttpResponseMessage>
//示例:
Product product new Product { Name "Hex", Category "音乐", Price 180 };
HttpResponseMessage response = request.PostAsJsonAsync( "/api/products", product ).Result; //调用Task的Result属性提取异步任务的返回值,这会阻塞所有线程直到异步任务成功返回数据

PutAsJsonAsync( )
//异步修改数据,此方法会自动开启一个新的线程Task,返回一个Task<HttpResponseMessage>
//示例:
Product product new Product { Name "万有引力之虹", Category "图书", Price 89 };
HttpResponseMessage response = request.PutAsJsonAsync( "/api/products/1", product ).Result; //服务端接收一个产品id和一个Product实体

DeleteAsync( )
//异步删除数据,此方法会自动开启一个新的线程Task,返回一个Task<HttpResponseMessage>

HttpResponseMessage(System.Net.Http)(Http响应流)

表示服务端返回的消息

IsSuccessStatusCode
//获取Http请求是否成功返回了状态码,状态码在200-299之间时返回true

StatusCode
//获取或设置服务端返回的Http状态码,(int)response.StatusCode

ReasonPhrase
//获取或设置服务端返回的Http状态码相关的信息

Content
//获取或设置报文主体,即服务端返回的数据,返回一个HttpContent,可以实例化一个StringContent来创建响应的内容,因为StringContent从HttpContent派生
 
Headers
//获取服务端返回的头部信息集合,可通过在其上调用GetValues(string headerKey)来获取请求头信息

HttpRequestMessage(System.Net.Http)(Http请求流)

表示客户端请求的消息

CreateResponse( HttpStatusCode code, T value )
//创建一个响应流对象,value表示报文主体内容

Content
//获取或设置报文主体,即客户端提交的数据,返回一个HttpContent

Headers
//获取客户端请求的头部信息集合,可通过在其上调用GetValues(string headerKey)来获取请求头信息

HttpContent(System.Net.Http)(Http报文主体)

表示客户端或服务端发送的报文主体内容

ReadAsAsync
//异步读取报文数据,返回一个Task,要取出数据需要Task.Result
//示例:
string json = response.Content.ReadAsAsync<string>( ).Result; //读取http报文

Headers
//报文主体内容的头信息集合,可通过在其上调用GetValues(string headerKey)来获取报文内容中的头信息,比如获取Content-Type
//示例:
IEnumerable<string> contentType = response.Content.Headers.GetValues( "Content-Type" );
foreach(var str in contentType)
{
    Console.WriteLine( str);
}

 

创建ASP.NET WEB API服务

选择ASP.NET WEB应用程序,勾选WEB API

技术分享图片

或选择空,这样就可以取消勾选MVC,值创建一个不包含MVC的API项目:

技术分享图片

项目创建完成后可以看到Controllers目录有两个控制器,一个用于ASP.NET MVC,另一个ValueController就是用于API服务的API控制器,每一个API控制器都从ApiController派生。Api控制器的方法返回类型按约定最好是使用HttpResponseMessage,每个方法按约定的前缀名称定义,Get前缀是获取数据,Post前缀是以Post提交方式提交数据,Put前缀是修改数据,Delete前缀是删除数据,按照这四种约定来定义你的Http处理函数即可。而客户端在调用API服务时,其请求的地址不能包含处理Http请求的函数名称,只包含Api控制器的名称即可,因为客户端会使用HttpClient的以Get、Post、Put、Delete作为前缀的函数名来发起Http请求,所以服务端会自动根据前缀约定找到Api控制器下对应的处理函数来处理请求。

配置Web.Config允许其它服务端返回的页面使用Ajax跨域请求

<system.webServer>
     <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>
</system.webServer>

首先需要在Models目录定义模型类、处理模型的接口、处理模型的类型,它们分别是Product、IProductRepository、ProductRepository。

技术分享图片 Product
技术分享图片 IProductRepository
技术分享图片 ProductRepository

新建一个Api控制器

using API.Models;
using Newtonsoft.Json;

namespace API.Controllers
{
    public class ProductsController : ApiController
    {
        static readonly IProductRepository productRepository new ProductRepository( );

        //查询所有产品
        public HttpResponseMessage GetAll( )
        {
            return Request.CreateResponse( HttpStatusCode.OK, JsonConvert.SerializeObject( productRepository.GetAll( ) ) );
        }

        //根据id查询产品
        public HttpResponseMessage GetProduct( int id )
        {
            Product item = productRepository.Get( id );
            return item == null throw new HttpResponseException( HttpStatusCode.NotFound ) : Request.CreateResponse( HttpStatusCode.OK, JsonConvert.SerializeObject( item ) );
        }

        //根据分类查询产品
        public HttpResponseMessage GetProductCategory( string category )
        {
            var list = productRepository.GetAll( ).Where( p => p.Category == category );
            return Request.CreateResponse( HttpStatusCode.OK, JsonConvert.SerializeObject( list ) );
        }

        //添加产品
        public HttpResponseMessage PostProduct( Product item )
        {
            item = productRepository.Add( item );
            var response = Request.CreateResponse( HttpStatusCode.Created, item );
            string uri = Url.Link( "DefaultApi", new { id = item.Id } );
            response.Headers.Location new Uri( uri );
            return response;
        }

        //修改产品
        public HttpResponseMessage PutProduct( int id, Product product )
        {
            product.Id = id;
            bool update = productRepository.Update( product );
            return Request.CreateResponse( HttpStatusCode.OK );
        }

        //删除产品
        public HttpResponseMessage DeleteProduct( int id )
        {
            productRepository.Remove( id );
            return new HttpResponseMessage( HttpStatusCode.NoContent ); //删除后可以返回http204以表示再无此条目
        }
    }
}

WEB API默认返回xml的数据,为了能返回json格式,可通过修改App_Start目录的WebApiConfig.cs文件,在Register方法中作如下配置:

技术分享图片 View Code

新建一个CUI程序来表示客户端

技术分享图片 View Code

例子中调用HttpClient的异步操作方法向服务端发起请求,而在WEB应用程序中,除了可以使用Result阻塞所有线程等待异步任务完成以外,还可以使用await操作符达到同样的效果,如:

public HttpResponseMessage Index()
{    
    HttpResponseMessage response = request.GetAsync( "/api/products?category=图书" ).Result;      
}

或:

public async Task<HttpResponseMessage > Index()
{  
    HttpResponseMessage response =await request.GetAsync( "/api/products?category=图书" );
}

API控制器的Action方法也可以直接返回string,如:

//FromBody特性应用在参数上,表示将客户端发送的Http报文主体的数据转换为C#实体模型
public string Add( [FromBody] Product pro )
{
    return $"{pro.Name},{pro.Category},{pro.Price}";
}

 

添加API路由模板

默认情况下Http请求调用API的Url都是以api开头,带api控制器名,但不带Action名称,这是在WebApiConfig.cs中默认的路由模板所定义的,你可以更改这个模板,以便可以像下面那样调用API

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}", //没有action
    defaults: new { id RouteParameter.Optional }
);

//调用时的url请求格式:api/products

config.Routes.MapHttpRoute( 
    name:"myApi",
    routeTemplate: "api/{controller}/{action}/{id}", //定义了api的action占位符
    defaults: new { id RouteParameter.Optional }
);

//调用时的url请求格式:api/products/insertProduct

 

自定义客户端的消息处理器

调用API的客户端可以自定义消息处理器,消息处理器从System.Net.Http.DelegatingHandler派生,然后重写基类的SendAsync和Dispose方法,因为请求发出时会进入SendAsync方法,而服务端的响应同样也会进入SendAsync方法,所以该方法可以处理即将发送到远程API的请求,也可以处理远程API返回的数据。

技术分享图片

using System.Net.Http;
using Newtonsoft.Json;
using System.Diagnostics;

namespace ClientCallWebAPI
{
    public class MyMessageHandler : DelegatingHandler
    {
        protected override async Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, System.Threading.CancellationToken cancellationToken )
        {
            //发送Http请求,如果没有得到服务端API的响应,则输出一个日志记录,最后将HttpResponseMessage(响应消息)返回以便传递给其它的Http消息处理器
            HttpResponseMessage response await base.SendAsync( request, cancellationToken );
            if (!response.IsSuccessStatusCode)
            {
                Trace.Listeners.Add( new TextWriterTraceListener( "f:/log.txt" ) );
                Trace.AutoFlush true;
                Trace.WriteLine( $"响应错误:请求时间 { DateTime.Now.ToString( ) }   错误码:{ response.StatusCode }   错误详细信息:{ response.ReasonPhrase }" );
            }
            return response;
        }

        protected override void Dispose( bool disposing )
        {
            base.Dispose( disposing );
        }
    }

    class Program
    {
        static void Main( string[] args )
        {
            //创建HttpClient实例时需要使用HttpClientFactory的工厂方法,以便将自定义的消息处理器注册到处理管线中,每个处理器以逗号隔开即可
            //自定义的处理器总是先进后出,Create方法参数的最后一个处理器会是第一个执行处理器
            HttpClient request HttpClientFactory.Create( new MyMessageHandler( ) ); 
            //……
        }
    }
}

自定义服务端的消息处理器

与自定义客户端的消息处理器是一样的,以下实现当请求进入服务端后,MyMessageHandler 将处理请求,将客户端IP写入日志,最后调用基类的消息处理器正常处理请求。

技术分享图片 View Code

将消息处理器插入处理管道,需要在App_Start的WebApiConfig.cs中注册

public static class WebApiConfig
{
    public static void Register( HttpConfiguration config )
    {
        config.MessageHandlers.Add( new MyMessageHandler( ) );
    }
}

注册单路由消息处理器

namespace API
{
    public static class WebApiConfig
    {
        public static void Register( HttpConfiguration config )
        {
            // Web API 路由
            config.MapHttpAttributeRoutes( );

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

            config.MessageHandlers.Add( new MyMessageHandler1( ) ); //全局消息处理器

            config.Routes.MapHttpRoute(
                name: "SpecialApi",
                routeTemplate: "specialApi/{controller}/{id}",
                defaults: new { id RouteParameter.Optional },
                constraints: null, //必须提供约束,哪怕是null,否则会提示参数个数不完整,没有采用4个参数重载
                handler:new MyMessageHandler2( ) //注册特定路由的消息处理器,只针对此路由使用此消息处理器
            );
        }
    }
}

创建API帮助文档

右击API项目属性 - 生成 - 勾选生成xml文档,为xml文档命名,这会为项目生成一个xml格式的说明性文档

技术分享图片

打开项目目录,Areas - HelpPage - App_Start - HelpPageConfig.cs,在Register方法中注册xml说明文档

public static void Register( HttpConfiguration config )
{
    config.SetDocumentationProvider( new XmlDocumentationProvider( HttpContext.Current.Server.MapPath( "~/App_Data/APIHelp.xml" ) ) );
}

在你的Api控制器中为每一个Action操作添加注释,帮助文档页面会自动显示这些注释信息。

/// <summary>
/// 根据id查询产品
/// </summary>
/// <param name="id">提供产品ID</param>
/// <returns></returns>
public HttpResponseMessage GetProduct( int id )
{
    Product item = productRepository.Get( id );
    return item == null throw new HttpResponseException( HttpStatusCode.NotFound ) : Request.CreateResponse( HttpStatusCode.OK, JsonConvert.SerializeObject( item ) );
}

打开以下项目目录的文件可以修改帮助文档页面及其详细页面的部分英文说明为中文:

Areas - HelpPage - Views - Help - Index.cshtml,可修改帮助文档页面顶部的文字描述。

Areas - HelpPage - Views - Help -  Api.cshtml,可修改帮助文档详细页面顶部的回到帮助主页的超链接。

Areas - HelpPage - Views - Help - DisplayTemplates - ApiGroup.cshtml,可修改帮助文档页面的列头为中文。

Areas - HelpPage - Views - Help - DisplayTemplates - HelpPageApiModel.cshtml,可修改帮助文档API页面的列头为中文。

Areas - HelpPage - Views - Help - DisplayTemplates - Parameters.cshtml,可修改帮助文档API详细页面的列头为中文。

技术分享图片

技术分享图片

 

异常处理

为了便于开发人员定位http错误,也为了向客户端显示更加友好的http错误信息,你可以手动定义http异常信息,这样可以把友好的异常信息响应给客户端,也可以定义一个http异常过滤器,异常过滤器应从ExceptionFilterAttribute派生。

手动定义http异常

手动定义http异常,使用这种方式必须注意,WEB API的Http异常机制是最早开始执行的,像下面这种由开发人员编写的异常抛出逻辑是后来才执行的,也即,如果一个明显的Http异常被WEB API的异常机制捕获,比如发起请求的客户端并未提供category参数,那么以下代码的测试逻辑根本不会执行,因为未提供category参数的异常已经在手动测试的代码执行前被执行,如果没有明显的异常被WEB API捕获,则以下测试category参数的值是否在两个值的范围之内的代码逻辑才会得到执行。

public HttpResponseMessage GetProductCategory( string category )
{
    HttpResponseMessage httpResponseMessage null;
    if (!category.Contains( "图书" ) || !category.Contains( "音乐" ))
    {
        httpResponseMessage = Request.CreateResponse( HttpStatusCode.NotFound, JsonConvert.SerializeObject( new { msg "提供的参数值不在可查询范围之内" } ) );
        //CreateResponse方法会自动将参数2提供的value序列化为json格式:message:value
        //httpResponseMessage = Request.CreateResponse( HttpStatusCode.NotFound, "提供的参数值不在可查询范围之内" );
    }
    else
    {
        var list = productRepository.GetAll( ).Where( p => p.Category == category );
        httpResponseMessage = Request.CreateResponse( HttpStatusCode.OK, JsonConvert.SerializeObject( list ) );
    }
    return httpResponseMessage;
}

定义全局异常过滤器

你可以直接将异常过滤器应用在api控制器或api控制器的action方法上,但是,如果你创建API项目时勾选了MVC,那么过滤器先必须注册在WebApiConfig.cs中,否则无效。单经过我的测试,自定义的Http异常过滤器无效,下断点不会进入,原因不明。

技术分享图片 View Code
namespace API
{
    public static class WebApiConfig
    {
        public static void Register( HttpConfiguration config )
        {
            config.Filters.Add( new API.App_Start.HttpErrorFilterAttribute( ) );
        }
    }
}

  

javascript调用API

<script type="text/javascript">
    $(document).ready(function () {
    //查询数据
    $.ajax({
        url: "http://localhost:58594/api/employeemsg/Get",
        type: "get",
        success: (data) => {
            var json = $.parseJSON(data);
            $(json).each((index, item) =>alert(item.ID + item.Name));         }
  });

  //添加新数据
    $.ajax({
        url: "http://localhost:58594/api/employeemsg/Add",
        data:{ ID:15,Name:"lily",Gender:"男",Birthday:"2016-11-12",Age:32 },
        type: ‘post‘,
        success: (data) => {
            //console.log(data);
            alert(data);
        }
    });
});
</script>

服务端调用API得到数据存入ViewBag后,可以通过如下方式将数据取出来放进Js代码中:

$(document).ready(function () {
    var jsonstr=@Html.Raw(ViewBag.msg);
    var json = $.parseJSON(jsonstr);
    $(json).each(function(index,item){
        alert(item.ID+item.Name+item.Gender+item.Birthday);
    });
});

 

附:下载远程数据

string url "http://www.weather.com.cn/weathern/101040100.shtml";
WebClient wc new WebClient();
wc.Encoding Encoding.GetEncoding("utf-8");
string content = wc.DownloadString(url);


















































































































































































































































































以上是关于ASP.NET web api汇总的主要内容,如果未能解决你的问题,请参考以下文章

Asp.net MVC 与 Asp.net Web API 区别

Asp.Net Web API 2第五课——Web API路由

Asp.Net Web API 2第三课——.NET客户端调用Web API

Asp.Net Web API 2第十三课——ASP.NET Web API中的JSON和XML序列化

AngularJS + ASP.NET Web API + ASP.NET MVC 身份验证

Asp.Net Web API 2第六课——Web API路由和动作选择