WebApi是微软在VS2012 MVC4版本中绑定发行的,WebApi是完全基于Restful标准的框架。RestFul: (英文:Representational State Transfer,简称REST)网上关于Restful定义的资料很多,我个人理解的Restful api 是指基于资源的定义的接口,它的所有接口都是对资源的操作。REST的核心原则是将你的API拆分为逻辑上的资源。这些资源通过http被操作(GET ,POST,PUT,DELETE).Restful Api接口标准的定义满足CURD,标准的接口定义如下:

  • Post:新增记录
  • Put:修改记录
  • Get:获取数据
  • Delete:删除数据

webapi标准的定义形式为:每个controller都对应一个Model,controller中的接口都应该是对controller所对应的那个model进行的操作,所以上诉的post,get,put,delete都是对model所对应的资源操作。但是在实际的操作中,可能没有办法完全满足这种定义的形式,力求尽量满足。

 

WebAPI项目和典型的MVC项目一样,包含主要的Models,Views,Controllers等文件夹和Global.asax文件。Views对于WebAPI来说没有太大的用途,Models中的Model主要用于保存Service和Client交互的对象,这些对象默认情况下会被转换为Json格式的数据进行传输,Controllers中的Controller用于提供服务。和普通的MVC一样,Global.asax用于配置路由规则。

在实际应用中,Controller是WebAPI的链接服务器和客户端的窗口。Controller的好坏影响整个系统的设计。Controller中是各种Action接口,对于Action接口,我们有必要对其接收的参数和返回值了解。

Action返回类型

WebAPI服务函数的返回值主要可以分为void、普通对象、HttpResponseMessag、IHttpActionResult 四种,这里简单的介绍一下。

Void类型

一般来说,Delete和Put类型的求情返回void类型的值,如:

C# WebAPI学习_序列化
public class DepartmentController : ApiController
{
        public void Delete(int id)
        {
            
        }
}
C# WebAPI学习_序列化

不过一个交互性好的接口,应该返回正确的Http status code,如返回200,对列子做修改:

C# WebAPI学习_序列化
public class DepartmentController : ApiController
{
    public HttpStatusCode Delete(int id)
  {
    return HttpStatusCode.OK;
  }
}    
C# WebAPI学习_序列化

 

普通对象

普通对象通常是由Get接口返回。例如:

C# WebAPI学习_序列化
public class CompanyController : ApiController
{
  public Company Get(int id)
   {
     return CompanyInfo;
   }
}
C# WebAPI学习_序列化

HttpResponseMessag

HttpResponseMessage是标准Http应答了,此时服务器并不做任何处理,可以直接将HttpResponseMessage发送给客户端。

C# WebAPI学习_序列化
  public HttpResponseMessage Get()
  {
         var response = Request.CreateResponse(HttpStatusCode.OK);
         response.Content = new StringContent("Success", Encoding.UTF8);
             
         return response;
   }
C# WebAPI学习_序列化

IHttpActionResult

IHttpActionResult是Web API 2中引入的一个接口,IHttpActionResult是HttpResponseMessage的一个工厂类。IHttpActionResult是WebAPI中推荐的标准返回值,ApiController类中也提供了不少标准的工厂函数方便我们快速构建它们,如BadRequest,Conflict,Ok,NotFound等,一个简单的示例如下:

C# WebAPI学习_序列化
 public IHttpActionResult Get(int id)
 {
      var dto = new ProductDataDTO();
      dto = AutoMapper.Mapper.Map<ProductDataDTO>(ProductDataService.QueryProductData (id));
      return Ok(dto);
}
C# WebAPI学习_序列化

 

Action参数类型

Get请求的参数,均在URI中给出,比较简单,不做过多说明。下面主要对POST请求的参数做一下说明。

POST请求

单一POST参数

单一的参数请求,需要使用[FromBody]标记参数,如下图所示:

[AcceptVerbs("POST")]
public string Post([FromBody]string data)
{
     return string.Format(@"Data:{1}",  data);
}

除了使用[FromBody]标记参数外,也可以使用dynamic关键字标记参数。

多个参数的POST请求

有了上面的经验,多个参数的例子,按照常理来说,应该如下所示

[AcceptVerbs("POST")]
 public string Post([FromBody]string data, [FromBody]string name)
 {
     return string.Format(@"Name:{0},Data:{1}", name, data);
 }

但在实际调用中却报错,报错如下:

C# WebAPI学习_json_11

查了资料,原因是在一个接口中,被[FromBody]修饰的参数只能有一个。对此,我们可以将上面的两个参数封装为一个对象传递。如下:

C# WebAPI学习_序列化
public class Person
{
  public string name { set; get; }
  public string message { set; get; }
}

......


[AcceptVerbs("POST")]
public string TEST4([FromBody]Person data)
{
  return string.Format(@"Name:{0},Message:{1}", data.name, data.message);
}
C# WebAPI学习_序列化

除此之外,还有另外一种方式实现,使用[FromURi]关键字,如下所示:

public string TEST2([FromUri]string data,[FromBody]string name)
{
  return string.Format(@"Name:{0},Data:{1}",name,data);
}

需要说明的是,在WebApi的接口参数中,在没有标记的情况下,参数默认是[FromUri]形式,Get请求的接口的参数都是FromUri,顾名思议是从Uri获取数据。一个接口可以有多个FromUri参数(这些参数一般都是简单参数),但是只能有一个FromBody的参数。

就刚才的例子而言,data数据在Uri中获取,而name数据则从body中获取。

传递对象

有了上面的例子,其实传递对象在上面已经讲过了,就是使用[FromBody]或dynamic标记参数。

传递不同的多个对象

这种情况,最近打算写这篇文章的时候,查找资料的时候,在其他人的博文中看到的,里面提到了传递多个不同对象的情况,个人也尝试去试着调试,但是一直没有成功,也就不好做过多的说明。后来想了下,其实可以另辟蹊径解决这个方法,新建一个对象,将要传递的对象做了属性,这样来传递给后台接口。关于最后这种方式,大家感兴趣可以去看看原文。

百闻不如一见,百见不如一做,只有做了,才知道问题出现在哪儿,才能去解决问题。
 
 
 

数据序列化


Web API 框架目前支持两种数据格式的序列化:Json 及 Xml。在不做任何配置的情况下,如果 Http 请求中,HttpHeader 中 Accept 被指定为 accept: application/xml,则 Web API 会自动把数据使用 xml 进行序列化,否则使用 json 序列化。 
如果期望不使用 xml 序列化数据,我们可以通过 GlobalConfiguration.Configuration.Formatters 来进行配置:config.Formatters.Remove(config.Formatters.XmlFormatter)。

一般情况下,我们会使用 Json 序列化。跟 ASP.NET MVC 的 Json 序列化不同的是,Web API 使用了 Newtonsoft.Json 框架来进行序列化。(例如,JsonMediaTypeFormatter.SerializerSettings 属性就是 Newtonsoft.Json.JsonSerializerSettings 类型,可以直接对序列化进行配置。) 
Json 序列化支持对匿名类型进行进行序列化,这大大方便了开发人员,例如,我们可以随意组装数据并直接返回:

   1:  [HttpGet]
   2:  public IEnumerable AllGet()
   3:  {
   4:      return new string[] { "Item1", "Item2" }.Select(s => new
   5:      {
   6:          Name = s,
   7:          Code = s,
   8:          Items = new ArrayList
   9:          {
  10:              new { Name = "Item1" },
  11:              new { Name = "Item2" },
  12:              new { Name = "Item3" },
  13:              new { Name = "Item4" },
  14:          }
  15:      });
  16:  }

另外,Web API 提供了 HttpResponseMessage 类型可作为返回值,使得开发人员可以对 HttpResponse 做一些更详细的设置。而且,如果不期望修改返回值类型而直接返回 HttpResponse 时,可以使用 HttpResponseException 间接返回一个 HttpResponseMessage。

 

Action 匹配


Web API 框架默认是基于 Restful 架构模式的,与 ASP.NET MVC 有区别的是,它会根据 Http 请求的 HttpMethod(Get、Post、Put、Delete) 来在 Controller 中查找 Action,规则是:Action 名中是否以 Get、Post 开头?Action 上标记 HttpGet、HttpPost 等标记?并会完全忽视 Action 的方法名。

例如,Web API 对于资源的 CRUD 操作,采用如下格式: 
get /API/models/ 查询所有实体 
get /API/models/1000 查询id为1000的实体 
post /API/models/ {id:-1, name:'name'} 添加一个实体 
put /API/models/ {id:1000, name:'name'} 更新指定实体 
delete /API/models/1 删除指定实体

由于忽视了方法名,所以的几个方法,在调用时会抛出异常: 
C# WebAPI学习_数据_14

C# WebAPI学习_webAPI_15

 

在面向服务的架构中,往往不会直接把底层的实体公布出来,让客户端直接进行 CRU 操作;而是公布一些粗粒度的 RPC 形式的服务操作。要使用 Web API 框架,我们需要修改默认的配置。例如,让客户端在调用时显式指定 action 名称:

   1:  config.Routes.MapHttpRoute(
   2:      name: "DefaultApi",
   3:      routeTemplate: "api/{controller}/{action}/{id}",
   4:      defaults: new { id = RouteParameter.Optional }
   5:  );

这样,由于显式指定了 Action 名称,Web API 会使用该名称来查找对应的 Action 方法,而不再按照 HttpMethod 约定来查找对应的 Action。例如,对于以下 API 的调用如下:

   1:  [HttpGet]
   2:  public HttpResponseMessage Login(string userName, string password)
   3:  {
   4:      return Request.CreateResponse(HttpStatusCode.NotFound);
   5:  }

调用方法: 
get /api/account/login/?username=hqf@qq.com&password=dsd

 

关于 POST 参数绑定


Web API 相对于 ASP.NET MVC,使用了新的参数绑定类。要注意的是,Action 参数列表中只能有一个参数可以从 Http Post Body 中反序列化出来。如果参数列表中只有一个参数,而且它的类型是一个复杂类型,那么 Web API 会直接把 Body 尝试反序列化为该类的对象。如果有多个参数,那么要从 Body 反序列化的那个参数,需要标记上 [FromBodyAttribute]。 
相关内容,比较复杂,可以参考以下文章: 
javascript:void(0)
javascript:void(0)
http://www.tuicool.com/articles/eQzyEv 
http://weblogs.asp.net/cibrax/archive/2012/08/10/binding-form-data-in-asp-net-web-api.aspx

 

不错的示例代码


MSDN 上有一个比较全面的示例代码: 
http://code.msdn.microsoft.com/ASPNET-Web-API-JavaScript-d0d64dd7

 

WebApi是微软在VS2012 MVC4版本中绑定发行的,WebApi是完全基于Restful标准的框架。RestFul: (英文:Representational State Transfer,简称REST)网上关于Restful定义的资料很多,我个人理解的Restful api 是指基于资源的定义的接口,它的所有接口都是对资源的操作。REST的核心原则是将你的API拆分为逻辑上的资源。这些资源通过http被操作(GET ,POST,PUT,DELETE).Restful Api接口标准的定义满足CURD,标准的接口定义如下:

  • Post:新增记录
  • Put:修改记录
  • Get:获取数据
  • Delete:删除数据

webapi标准的定义形式为:每个controller都对应一个Model,controller中的接口都应该是对controller所对应的那个model进行的操作,所以上诉的post,get,put,delete都是对model所对应的资源操作。但是在实际的操作中,可能没有办法完全满足这种定义的形式,力求尽量满足。

 

WebAPI项目和典型的MVC项目一样,包含主要的Models,Views,Controllers等文件夹和Global.asax文件。Views对于WebAPI来说没有太大的用途,Models中的Model主要用于保存Service和Client交互的对象,这些对象默认情况下会被转换为Json格式的数据进行传输,Controllers中的Controller用于提供服务。和普通的MVC一样,Global.asax用于配置路由规则。

在实际应用中,Controller是WebAPI的链接服务器和客户端的窗口。Controller的好坏影响整个系统的设计。Controller中是各种Action接口,对于Action接口,我们有必要对其接收的参数和返回值了解。

Action返回类型

WebAPI服务函数的返回值主要可以分为void、普通对象、HttpResponseMessag、IHttpActionResult 四种,这里简单的介绍一下。

Void类型

一般来说,Delete和Put类型的求情返回void类型的值,如:

C# WebAPI学习_序列化
public class DepartmentController : ApiController
{
        public void Delete(int id)
        {
            
        }
}
C# WebAPI学习_序列化

不过一个交互性好的接口,应该返回正确的Http status code,如返回200,对列子做修改:

C# WebAPI学习_序列化
public class DepartmentController : ApiController
{
    public HttpStatusCode Delete(int id)
  {
    return HttpStatusCode.OK;
  }
}    
C# WebAPI学习_序列化

 

普通对象

普通对象通常是由Get接口返回。例如:

C# WebAPI学习_序列化
public class CompanyController : ApiController
{
  public Company Get(int id)
   {
     return CompanyInfo;
   }
}
C# WebAPI学习_序列化

HttpResponseMessag

HttpResponseMessage是标准Http应答了,此时服务器并不做任何处理,可以直接将HttpResponseMessage发送给客户端。

C# WebAPI学习_序列化
  public HttpResponseMessage Get()
  {
         var response = Request.CreateResponse(HttpStatusCode.OK);
         response.Content = new StringContent("Success", Encoding.UTF8);
             
         return response;
   }
C# WebAPI学习_序列化

IHttpActionResult

IHttpActionResult是Web API 2中引入的一个接口,IHttpActionResult是HttpResponseMessage的一个工厂类。IHttpActionResult是WebAPI中推荐的标准返回值,ApiController类中也提供了不少标准的工厂函数方便我们快速构建它们,如BadRequest,Conflict,Ok,NotFound等,一个简单的示例如下:

C# WebAPI学习_序列化
 public IHttpActionResult Get(int id)
 {
      var dto = new ProductDataDTO();
      dto = AutoMapper.Mapper.Map<ProductDataDTO>(ProductDataService.QueryProductData (id));
      return Ok(dto);
}
C# WebAPI学习_序列化

 

Action参数类型

Get请求的参数,均在URI中给出,比较简单,不做过多说明。下面主要对POST请求的参数做一下说明。

POST请求

单一POST参数

单一的参数请求,需要使用[FromBody]标记参数,如下图所示:

[AcceptVerbs("POST")]
public string Post([FromBody]string data)
{
     return string.Format(@"Data:{1}",  data);
}

除了使用[FromBody]标记参数外,也可以使用dynamic关键字标记参数。

多个参数的POST请求

有了上面的经验,多个参数的例子,按照常理来说,应该如下所示

[AcceptVerbs("POST")]
 public string Post([FromBody]string data, [FromBody]string name)
 {
     return string.Format(@"Name:{0},Data:{1}", name, data);
 }

但在实际调用中却报错,报错如下:

C# WebAPI学习_json_11

查了资料,原因是在一个接口中,被[FromBody]修饰的参数只能有一个。对此,我们可以将上面的两个参数封装为一个对象传递。如下:

C# WebAPI学习_序列化
public class Person
{
  public string name { set; get; }
  public string message { set; get; }
}

......


[AcceptVerbs("POST")]
public string TEST4([FromBody]Person data)
{
  return string.Format(@"Name:{0},Message:{1}", data.name, data.message);
}
C# WebAPI学习_序列化

除此之外,还有另外一种方式实现,使用[FromURi]关键字,如下所示:

public string TEST2([FromUri]string data,[FromBody]string name)
{
  return string.Format(@"Name:{0},Data:{1}",name,data);
}

需要说明的是,在WebApi的接口参数中,在没有标记的情况下,参数默认是[FromUri]形式,Get请求的接口的参数都是FromUri,顾名思议是从Uri获取数据。一个接口可以有多个FromUri参数(这些参数一般都是简单参数),但是只能有一个FromBody的参数。

就刚才的例子而言,data数据在Uri中获取,而name数据则从body中获取。

传递对象

有了上面的例子,其实传递对象在上面已经讲过了,就是使用[FromBody]或dynamic标记参数。

传递不同的多个对象

这种情况,最近打算写这篇文章的时候,查找资料的时候,在其他人的博文中看到的,里面提到了传递多个不同对象的情况,个人也尝试去试着调试,但是一直没有成功,也就不好做过多的说明。后来想了下,其实可以另辟蹊径解决这个方法,新建一个对象,将要传递的对象做了属性,这样来传递给后台接口。关于最后这种方式,大家感兴趣可以去看看原文。