在新出的MVC4中,增加了WebAPI,用于提供REST风格的WebService,新生成的WebAPI项目和典型的MVC项目一样,包含主要的Models、Views、Controllers等文件夹和Global.asax文件。Views对于WebAPI来说没有太大的用途,Models中的Model主要用于保存Service和Client交互的对象,这些对象默认情况下会被转换为Json格式的数据迚行传输,Controllers中的Controller对应于WebService来说是一个Resource,用于提供服务。和普通的MVC一样,Global.asax用于配置路由规则。  环境准备       建议使用VS2012以上版本创建WebAPI,如果是使用VS2010,需要安装VS2010 SP1升级包,MVC4升级包,打开VS2012创建如下:  第一步:新建ASP.NET Web应用程序

第二步:建议WebAPI

新生成的WebAPI项目和典型的MVC项目一样,包含主要的Models,Views,Controllers等文件夹和Global.asax文件

注意:再次强调Views对于WebAPI来说没有太大的用途,Models中的Model主要用于保存Service和Client交互的对象,这些对象默认情况下会被转换为Json格式的数据进行传输,Controllers中的Controller对应于WebService来说是一个Resource,用于提供服务。和普通的MVC一样,Global.asax用于配置路由规则

(二)Models  和WCF中的数据契约形成鲜明对比的是,MVC WebAPI中的Model就是简单的POCO,没有任何别的东西,如,你可以创建如下的Model

public class UserModel
{
public int Id { get; set; }
public string UserName { get; set; }
public string PassWord { get; set; }
}

注意:Model必须提供public的属性,用于json或xml反序列化时的赋值  (三)Controllers  MVC WebAPI中的Controllers和普通MVC的Controllers类似,不过不再继承于Controller,而改为继承API的ApiController,一个Controller可以包含多个Action,这些Action响应请求的方法与Global中配置的路由规则有关,在后面结束Global时统一说明    (四)Global  默认情况下,模板自带了两个路由规则,分别对应于WebAPI和普通MVC的Web请求,默认的WebAPI路由规则如下

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

可以看到,默认路由使用的固定的api作为Uri的先导,按照微软官方的说法,用于区分普通Web请求和WebService的请求路径:  可以看到,默认的路由规则只指向了Controller,没有指向具体的Action,因为默认情况下,对于Controller中的Action的匹配是和Action的方法名相关联的:具体来说,如果使用上面的路由规则,对应下面的Controller:

public class UserController : ApiController
{
public List<UserModel> allModeList = new List<UserModel>() {
new UserModel(){ Id=1,UserName="zhang", PassWord="123"},
new UserModel(){ Id=2,UserName="lishi", PassWord="123456"},
new UserModel(){ Id=3,UserName="wang", PassWord="1234567"}
};
//Get api/User/
public IEnumerable<UserModel> GetAll()
{
return allModeList;
}
//Get api/User/1
public IEnumerable<UserModel> GetOne(int id) {
return allModeList.FindAll((m) => { return m.Id == id; });
}
//POST api/User/
public bool PostNew(UserModel user)
{
try
{
allModeList.Add(user);
return true;
}
catch {
return false;
}
}
//Delete api/User/
public int DeleteAll()
{
return allModeList.RemoveAll((mode) => { return true; });
}
//Delete api/User/1
public int DeleteOne(int id) {
return allModeList.RemoveAll((m) => { return m.Id == id; });
}
//Put api/User
public int PutOne(int id, UserModel user)
{
List<UserModel> upDataList = allModeList.FindAll((mode) => { return mode.Id == id; });
foreach (var mode in upDataList)
{
mode.PassWord = user.PassWord;
mode.UserName = user.UserName;
}
return upDataList.Count;
}
}

则,会有下面的对应关系:

URL HttpMethod 对应的Action名
/api/User GET GetALL
/api/User/1 GET GetOne
/api/User POST PostNew
/api/User/1 DELETE DeleteOne
/api/User DELETE DeleteALL
/api/User PUT PutOne

客户端JS调用

function getAll() {
$.ajax({
url: "api/User/",
type: 'GET',
success: function (data) {
document.getElementById("modes").innerHTML = "";
$.each(data, function (key, val) {
var str = val.UserName + ': ' + val.PassWord;
$('<li/>', { html: str }).appendTo($('#modes'));
});
}
}).fail(
function (xhr, textStatus, err) {
alert('Error: ' + err);
});
   }
function find() {
$.ajax({
url: "api/User/1" ,
type: 'GET',
success: function (data) {
document.getElementById("modes").innerHTML = "";
$.each(data, function (key, val) {
var str = val.UserName + ': ' + val.PassWord;
$('<li/>', { html: str }).appendTo($('#modes'));
});
}
}).fail(
function (xhr, textStatus, err) {
alert('Error: ' + err);
});
} function add() { $.ajax({
url: "api/User/",
type: "POST",
dataType: "json",
data: { "Id":4,"UserName": "admin", "PassWord": "666666"},
success: function (data) {
getAll();
}
}).fail(
function (xhr, textStatus, err) {
alert('Error: ' + err);
}); } function removeUser() {
$.ajax({
url: "api/User/3",
type: 'DELETE',
success: function (data) {
document.getElementById("modes").innerHTML = "";
getAll();
}
}).fail(
function (xhr, textStatus, err) {
alert('Error: ' + err);
});
} function removeAll() {
$.ajax({
url: "api/User/",
type: 'DELETE',
success: function (data) {
document.getElementById("modes").innerHTML = "";
getAll();
}
}).fail(
function (xhr, textStatus, err) {
alert('Error: ' + err);
});
}
function udpate() {
$.ajax({
url: "api/User/1",
type: 'PUT',
dataType: "json",
data: { Id: 1, "UserName": "admin", "PassWord": "666666" },
success: function (data) {
document.getElementById("modes").innerHTML = "";
getAll();
}
}).fail(
function (xhr, textStatus, err) {
alert('Error: ' + err);
});
}

这样就实现了最基本的CRUD操作。  扩展需求  问题1:我想按照用户名称(UserName)进行查询,怎么办?  办法:第一步:在UserController类中加一个方法名称叫:GetUserByName,如下所示:

public UserModel GetUserByName(string userName) {
return allModeList.Find((m) => { return m.UserName.Equals(userName); });
}

第二步:在客户端index.cshtml中调用

function getUserByName() {
$.ajax({
url: "api/User/zhang",
type: 'GET',
success: function (data) {
document.getElementById("modes").innerHTML = "";
var str = data.UserName + ': ' + data.PassWord;
$('<li/>', { html: str }).appendTo($('#modes'));
}
}).fail(
function (xhr, textStatus, err) {
alert('Error: ' + err);
});
   }

如果URL是: url: "api/User/zhang",将会报错:Bad Request  原因是他会自动调用我们的GetOne(int id) 这个方法,类型转换出错  解决办法:  改变URL为: url: "api/User/?userName=zhang",  问题2:我想按用户名称(UserName) 和用户密码(PassWord)一起来进行查询,怎么办?  解决办法  第一步:UserController类中,可以重载一个GetUserByName的方法,如下所示:

public UserModel GetUserByName(string userName) {
return allModeList.Find((m) => { return m.UserName.Equals(userName); });
}

第二步:客户端调用:

function getUserByName() {
$.ajax({
url: "api/User/?userName=zhang&passWord=123", //这里尤其需要注意
type: 'GET',
success: function (data) {
document.getElementById("modes").innerHTML = "";
var str = data.UserName + ': ' + data.PassWord;
$('<li/>', { html: str }).appendTo($('#modes'));
}
}).fail(
function (xhr, textStatus, err) {
alert('Error: ' + err);
});
   }

路由规则扩展  和普通的MVC一样,MVC WebAPI支持自定义的路由规则,如:在上面的操作中,路由规则使用  "api/{controller}/{id}"  则限定了使用GET方式利用URL来传值时,controller后面的接收参数名为id,但是在Controller中,如果GetOne方法的接收参数名为key,是不会被匹配的,这是只需要新增一个新的路由规则,或修改原先的路由规则为:  "api/{controller}/{key}",如下所示:

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

当然,可以对路由进行更深的扩展,如:扩展成和普通MVC一样的路由:  "api/{controller}/{action}/{id}"  这样,就要求同时使用Action和HTTP方法进行匹配当然,根据微软的说法,这种使用是不被推荐的,因为这不符合大家对WebService的一般认知:  使用Attribute声明HTTP方法

       [HttpGet]
public IEnumerable<TestUseMode> FindAll()
[HttpGet]
public IEnumerable<TestUseMode> FindByKey(string key)
[HttpPost]
public bool Add(TestUseMode mode)
[HttpDelete]
public int RemoveByKey(string key)
[HttpDelete]
public int RemoveAll()
[HttpPut]
public int UpdateByKey(string key, string value)
[NonAction]
public string GetPrivateData()

当然,我只列出了方法名,而不是这些方法真的没有方法体...方法体是不变的,NoAction表示这个方法是不接收请求的,即使以GET开头。如果感觉常规的GET,POST,DELETE,PUT不够用,还可以使用AcceptVerbs的方式来声明HTTP方法,如:

[AcceptVerbs("MKCOL", "HEAD")]
public int UpdateByKey(string key, string value)
{
List<TestUseMode> upDataList = allModeList.FindAll((mode) => { if (mode.ModeKey == key) return true; return false; });
foreach(var mode in upDataList)
{
mode.ModeValue = value;
}
return upDataList.Count;
}

附:什么是REST风格? 参考:什么是REST风格  http://hi.baidu.com/yankaiwei/item/1f0b37dd922d53ef3cc2cb69  第二部分:综合示例:应用ASP.NET MVC4+WebAPI+FluentData开发Web应用  第一步:创建数据库  NorthWind数据库的Customers表

Create DataBase NorthWind
Go
Use NorthWind
Go
CREATE TABLE [dbo].[Customers](
[CustomerID] [nchar](5) NOT NULL,
[CompanyName] [nvarchar](40) NOT NULL,
[ContactName] [nvarchar](30) NULL,
[ContactTitle] [nvarchar](30) NULL,
[Address] [nvarchar](60) NULL,
[City] [nvarchar](15) NULL,
[Region] [nvarchar](15) NULL,
[PostalCode] [nvarchar](10) NULL,
[Country] [nvarchar](15) NULL,
[Phone] [nvarchar](24) NULL,
[Fax] [nvarchar](24) NULL,
CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED
(
[CustomerID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] GO

第二步:创建 FluentData.Entity层,创建Customer实体类

namespace FluentData.Entity
{
public class Customer
{
public string CustomerID { get; set; }
public string CompanyName { get; set; }
public string ContactName { get; set; }
public string ContactTitle { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string Region { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
public string Phone { get; set; }
public string Fax { get; set; }
}
}

第三步:利用FluentData做数据的持久化  首先引入FluentData.cs (见附件)  其次:创建DBHelper类,代码如下:

public class DBHelper
{
public static IDbContext Context() {
//return new DbContext().ConnectionString("server=127.0.0.1;uid=sa;pwd=sa;database=TestDB", new SqlServerProvider());
return new DbContext().ConnectionStringName("connString", new SqlServerProvider());
}
}

然后不要忘记修改ASP.NET MVC层所在的Web.config,加入数据库连结字符串:

  <connectionStrings>
<add name="connString" connectionString="server=127.0.0.1;database=Northwind;uid=sa;pwd=sa;"/>
</connectionStrings>

第三步:创建 CustomerService数据持久化类,代码如下:

public class CustomerService
{
private IDbContext context = DBHelper.Context();
public Customer Select(string customerId){ return context.Select<Customer>("*").From("Customers").Where("CustomerID=@0").Parameters(customerId)
.QuerySingle(); }
public List<Customer> SelectAll() {
return context.Select<Customer>("*").From("Customers").QueryMany();
} public List<Customer> SelectAll(string sortExpression) {
if (String.IsNullOrEmpty(sortExpression)) return null;
return context.Select<Customer>("*").From("Customers").OrderBy(sortExpression).QueryMany();
} public List<Customer> SelectAll(int currentPageIndex,int maxRows, string sortExpression)
{
var select = context.Select<Customer>("*").From("Customers");
if (maxRows > 0) {
if (currentPageIndex == 0) currentPageIndex = 1;
select.Paging(currentPageIndex, maxRows);
}
if (!string.IsNullOrEmpty(sortExpression)) {
select.OrderBy(sortExpression);
} return select.QueryMany();
} public int CountAll() {
return context.Sql("select count(*) from Customers").QuerySingle<int>();
} public int Insert(Customer customer) {
return context.Insert<Customer>("Customers", customer).Execute();
} public int Update(Customer customer) {
return context.Update<Customer>("Customers", customer).Where("CustomerID", customer.CustomerID).Execute();
} public int Delete(string customerId) {
return context.Delete("Customers").Where("CustomerID", customerId).Execute();
} public int Delete(Customer customer)
{
return this.Delete(customer.CustomerID);
}
}

第四步:Web API,创建CustomerController  注意要引用:FluentData.Entity及FluentData.DAL 程序集

public class CustomerController : ApiController
{
private CustomerService customerService = new CustomerService();
//Select All
public IEnumerable<Customer> Get()
{
return customerService.SelectAll();
} //Select By Id
public Customer Get(string id)
{
return customerService.Select(id);
} //Insert
public void Post(Customer customer)
{
customerService.Insert(customer);
} //Update
public void Put(string id, Customer obj)
{
customerService.Update(obj);
} //Delete
public void Delete(string id)
{
customerService.Delete(id);
}
}

第五步:View层代码

namespace MyWebApI.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
} public ActionResult Test() {
return View();
} public ActionResult CustomerManager() {
return View();
}
}
}

然后创建View

    <table id="customerTable" border="1" cellpadding="3"  style="width:700px">
<tr>
<th>Customer ID</th>
<th>Company Name</th>
<th>Contact Name</th>
<th>Country</th>
<th>Actions</th>
</tr>
<tr>
<td><input type="text" id="txtCustomerId" style="width:100px" size="5"/></td>
<td><input type="text" id="txtCompanyName" style="width:150px" /></td>
<td><input type="text" id="txtContactName" style="width:150px"/></td>
<td><input type="text" id="txtCountry" style="width:150px"/></td>
<td><input type="button" name="btnInsert" value="Insert" style="width:150px"/></td>
</tr>
</table> <script type="text/javascript">
$(function () {
$.getJSON("api/Customer", LoadCustomers);
}); function LoadCustomers(data) {
$("#customerTable").find("tr:gt(1)").remove();
$.each(data, function (key, val) {
var tableRow = '<tr>' +
'<td>' + val.CustomerID + '</td>' +
'<td><input type="text" value="' + val.CompanyName + '" /></td>' +
'<td><input type="text" value="' + val.ContactName + '" /></td>' +
'<td><input type="text" value="' + val.Country + '" /></td>' +
'<td><input type="button" name="btnUpdate" value="修改" /> <input type="button" name="btnDelete" value="删除" /></td>' +
'</tr>';
$('#customerTable').append(tableRow);
}); $("input[name='btnInsert']").click(OnInsert);
$("input[name='btnUpdate']").click(OnUpdate);
$("input[name='btnDelete']").click(OnDelete);
} function OnInsert(evt) {
var customerId = $("#txtCustomerId").val();
var companyName = $("#txtCompanyName").val();
var contactName = $("#txtContactName").val();
var country = $("#txtCountry").val();
var data = '{"CustomerID":"' + customerId + '","CompanyName":"' + companyName + '","ContactName":"' + contactName + '","Country":"' + country + '"}'; $.ajax({
type: 'POST',
url: '/api/Customer/',
data: data,
contentType: "application/json; charset=utf-8",
dataType: 'json',
success: function (results) {
$("#txtCustomerId").val('');
$("#txtCompanyName").val('');
$("#txtContactName").val('');
$("#txtCountry").val('');
$.getJSON("api/customers" + new Date().getTime(), LoadCustomers);
alert('添加成功!');
}
}).fail(
function (xhr, textStatus, err) {
alert('添加失败,原因如下: ' + err);
});
}
function OnUpdate(evt) { var input;
var customerId = $(this).parent().parent().children().get(0).innerHTML; input = $($(this).parent().parent().children().get(1)).find("input");
//input.removeAttr("disabled");
var companyName = input.val(); input = $($(this).parent().parent().children().get(2)).find("input");
//input.removeAttr("disabled");
var contactName = input.val(); input = $($(this).parent().parent().children().get(3)).find("input");
//input.removeAttr("disabled");
var country = input.val(); var data = '{"CustomerID":"' + customerId + '","CompanyName":"' + companyName + '","ContactName":"' + contactName + '","Country":"' + country + '"}'; $.ajax({
type: 'PUT',
url: '/api/Customer/' + customerId,
data: data,
contentType: "application/json; charset=utf-8",
dataType: 'json',
success: function (results) {
$.getJSON("api/Customer" + new Date().getTime(), LoadCustomers);
alert('修改成功 !');
}
}).fail(
function (xhr, textStatus, err) {
alert('修改失败,原因如下: ' + err);
});
}
function OnDelete(evt) {
var customerId = $(this).parent().parent().children().get(0).innerHTML;
//var data = '{"id":"' + customerId + '"}';
//var row = $(this).parent().parent(); $.ajax({
type: 'DELETE',
url: '/api/Customer/' + customerId,
contentType: "application/json; charset=utf-8",
dataType: 'json',
success: function (results) {
$.getJSON("api/Customer?"+new Date().getTime(), LoadCustomers);
alert('成功删除!');
}
}).fail(
function (xhr, textStatus, err) {
alert('删除失败,原因如下: ' + err);
}); }
</script>

第三部分:Web API高级部分  在第一部分和大家一起学习了建立基本的WebAPI应用,第二部分写了一个综合示例,立刻就有人想到了一些问题:1.客户端和WebService/WebAPI之间文件传输2.客户端或者服务端的安全控制要解决这些问题,要了解一下WebAPI的基本工作方式。  (一)WebAPI中工作的Class  在MVC中大家都知道,获取Request和Response使用HttpRequest和HttpResponse两个类,在WebAPI中使用两外两个类:HttpRequestMessage 和HttpResponseMessage,分别用于封装Requset和Response。除了这两个类之外,还有一个常见的抽象 类:HttpMessageHandler,用于过滤和加工HttpRequestMessage和HttpResponseMessage    (二)解决第一个问题:客户端和WebService之间文件传输其 实第一个问题之所以被提出来应该是和客户端有关,如果客户端的请求是我们手写提交的,比如使用HttpClient封装的请求,则要传递文件之前,我们一 般会进行一次序列化,转化为二进制数组之类的,在网络上传输。这样的话,在Controller中的Action参数里,我们只需要接收这个二进制数组类 型的对象就可以了。但是如果客户端是Web Form呢,比如我们提交一个Form到指定的Controller的Action中,这个Action要接收什么类型的参数呢?或者我们问另外一个问题,如果我将Web Form提交到一个WebAPI的Action中 ,我要怎么去取出这个表单中的数据呢?其 实我们应该想到:我们的Action设置的参数之所以能够被赋值,是因为WebAPI的架构中在调用Action时将HTTP请求中的数据解析出来分别赋 值给Action中的参数,如果真是这样的话,我们只需要在Action中获取到HTTP请求,然后直接获取请求里面的数据,就能解决上面的问题。这 种想法是正确的,只不过,此时的HTTP请求已经不是最原始的HTTP Request,而是已经被转化成了HttpRequestMessage,在Action中,我们可以直接调用base.Requet来得到这个 HttpRequestMessage实例,通过这个实例我们就可以随心所欲的取出HTTP请求中想要的数据    2.1从RequestMessage中获取普通表单数据  这里的普通表单是指不包含File的表单,也就是说表单的enctype值不是multipart/form-data,这时,表单的数据默认情况下是以Json来传递的如下页面

<form name="form" action="~/api/File/Register" method="post">
<input type="text" name="userName" />
<br />
<input type="text" name="passWord" />
<br />
<input type="submit" value="Submit" />
</form>

ApiContoller:

  public class FileController : ApiController
{
[HttpPost]
public async Task Register()
{
HttpContent content = Request.Content;
var jsonValue = await content.ReadAsStringAsync();
Console.WriteLine(jsonValue);
}
}

执行:userName字段输入admin,passWord字段输入123456  传递到服务器端输出的是:userName=admin&passWord=12345  2.2从RequestMessage中获取multipart表单数据将view页面改写为:

<form name="form" action="~/api/File/SubmitFile" method="post" enctype="multipart/form-data" >
<input type="text" name="userName2" />
<br />
<input type="text" name="passWord2" />
<br />
<input type="file" name="file" id="upFile" />
<br />
<input type="submit" value="Submit" />
</form>

ApiController代码:

        public async Task<string> SubmitFile()
{
// 检查是否是 multipart/form-data
if (!Request.Content.IsMimeMultipartContent("form-data"))
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
var path = HttpContext.Current.Server.MapPath("~/File");
// 设置上传目录
var provider = new MultipartFormDataStreamProvider(path);
// 接收数据,并保存文件
var bodyparts = await Request.Content.ReadAsMultipartAsync(provider);
var file = provider.FileData[0];//provider.FormData
string orfilename = file.Headers.ContentDisposition.FileName.TrimStart('"').TrimEnd('"');
FileInfo fileinfo = new FileInfo(file.LocalFileName);
String ymd = DateTime.Now.ToString("yyyyMMdd", System.Globalization.DateTimeFormatInfo.InvariantInfo);
String newFileName = DateTime.Now.ToString("yyyyMMddHHmmss_ffff", System.Globalization.DateTimeFormatInfo.InvariantInfo);
string fileExt = orfilename.Substring(orfilename.LastIndexOf('.'));
fileinfo.CopyTo(Path.Combine(path, newFileName + fileExt), true);
fileinfo.Delete();
string result = "";
// 获取表单数据
result += "formData userName: " + bodyparts.FormData["userName1"];
result += "<br />";
// 获取文件数据
result += "fileData headers: " + bodyparts.FileData[0].Headers; // 上传文件相关的头信息
result += "<br />";
result += "fileData localFileName: " + bodyparts.FileData[0].LocalFileName; // 文件在服务端的保存地址,需要的话自行 rename 或 move
return result;
}

还有一种简单的方式

public string Post()
         {
HttpPostedFile file = HttpContext.Current.Request.Files[0];
string strPath = "D:\\MyProjects\\StudySolution\\RestDemo\\Upload\\test2.rar" ;
file.SaveAs(strPath);
string result = "0";
return result;
}

注:上述的文件上传代码涉及到async、Task、await 这些关键字是 asp.net mvc 中异步编程的知识点,在这里暂不做过多解释,不了解的同学可以去先了解一下这块的内容,后期在我的系列主题文章中也还会有这块知识点的讲解,敬请关注!  解决第二个问题:客户端或者服务端的安全控制  WebAPI的工作方式:HTTP的请求最先是被传递到HOST中的,如果WebAPI是被寄宿在IIS上的,这个HOST就是IIS上,HOST是没有能力也没有必要进行请求的处理的,请求通过HOST被转发给了HttPServer此时已经进入WebAPI的处理加工范围,HttpServer是 System.Net.HTTP中的一个类,通过HttpServer,请求被封装成了WebAPI中的请求承载 类:HttpRequestMessage,这个封装后的请求可以经过一系列自定义的Handler来处理,这些handler串联成一个 pipeline,最后请求会被传递给HttpControlDispather,这个类通过对路由表的检索来确定请求将被转发到的具体的 Controller中的Action。     由此我们早就可以看出,想要解决第二个问题,可以直接在Handler PipeLine中进行,这种AOP风格的过滤器(拦截器)在REST的Webservice的安全验证中应用很广,一般大家比较乐于在HTTP头或者在 HTTP请求的URL中加上身份验证字段进行身份验证,下面举一个在Http头中添加身份验证信息的小例子:  3.1客户端客户端的customhandler用于将身份验证信息添加入报头

class RequestCheckHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
request.Headers.Add("keyword", "ibeifeng");
return base.SendAsync(request, cancellationToken);
}
}

注:1.customhandler继承自DelegatingHandler类,上面已经说过,WebAPI的客户端和服务端被设计为相互对应的两套结构,所以不论是在客户端还是服务端,customhandler都是继承自DelegatingHandler类2.DelegatingHandler的sendAsync方法便是处理请求和接受请求时会被调用的方法,该方法返回值是HttPResponseMessage,接收的值为HttpRequestMessage,符合我们的一般认知3.方法的最后,调用base.SendAsync是将Request继续向该pipeline的其他customHandler传递,并获取其返回值。由于该方法不包含Response的处理逻辑,只需直接将上一个CustomHandler的返回值直接返回    客户端主程序:

static void Main(string[] args)
{
HttpClient client = new HttpClient(new RequestCheckHandler() { InnerHandler = new HttpClientHandler() });
HttpResponseMessage response = client.GetAsync("http://localhost:47673/api/File/GetUserInfo?userName=admin").Result;
response.Content.ReadAsStringAsync().ContinueWith((str) => { Console.WriteLine(str.Result); });
}

客户端的主程序创建了一个HttpClient,HttpClient可以接受一个参数,该参数就是CustomHandler,此处我们嵌入了我们定义的 RequestUpHandler,用于对Request报头进行嵌入身份验证码的处理,CustomHandler通过InnerHandler属性嵌 入其内置的下一个CustomHandler,此处,由于没有下一个CustomerHandler,我们直接嵌入HttpClientHandler用 于将HttpRequestMessage转化为HTTP 请求、将HTTP响应转化为HttpResponseMessage    3.2服务端服务端的customHandler用于解析HTTP报头中的身份认证码

public class SecurityHandler : DelegatingHandler
{
   protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken
    cancellationToken)
{
int matchHeaderCount = request.Headers.Count((item) =>
{
if ("keyword".Equals(item.Key))
{
foreach (var str in item.Value)
{
if ("ibeifeng".Equals(str))
{
return true;
}
}
}
return false;
});
if (matchHeaderCount>0)
{
return base.SendAsync(request, cancellationToken);
}
return Task.Factory.StartNew<HttpResponseMessage>(() => { return new HttpResponseMessage(HttpStatusCode.Forbidden); });
}
}

另:  FileController中加一个测试方法:

        [HttpGet]
public string GetUserInfo(string userName)
{
if (userName == "admin")
{
return "success";
}
else {
return "failed";
}
}

注:代码的处理逻辑很简单:如果身份验证码匹配成功,则通过base.SendAsync继续将请求向下传递,否则返回直接中断请求的传递,直接返回一个响应码为403的响应,指示没有权限。注意由于SendAsync的返回值需要封装在Task之中,所以需要使用Task.Factory.StartNew将返回值包含在Task中将customHandler注入到HOST中本例中WebAPI HOST在IIS上,所以我们只需将我们定义的CustomHandler在Application_Start中定义即可

protected void Application_Start()
{
//省略其他逻辑代码
GlobalConfiguration.Configuration.MessageHandlers.Add(new SecurityHandler ());
}

由于WebAPI Host在IIS上,所以HttpServer和HttpControllerDispatcher不用我们手工处理  在加上上面的处理后,如果没有身份验证码的请求,会得到如下的响应      总结  1.使用WebAPI的目的    当你遇到以下这些情况的时候,就可以考虑使用Web API了。    a. 需要Web Service但是不需要SOAP    b. 需要在已有的WCF服务基础上建立non-soap-based http服务    c. 只想发布一些简单的Http服务,不想使用相对复杂的WCF配置    d. 发布的服务可能会被带宽受限的设备访问    e. 希望使用开源框架,关键时候可以自己调试或者自定义一下框架  2.使用WebAPI的几种方式 与注意事项  3.使用WebAPI实现文件上传  4.如何加强WebAPI的安全性

ASP.NET WebAPI从入门的更多相关文章

  1. 推荐Asp.net WebApi入门教程

    Web API 强势入门指南; Web API 入门指南 - 闲话安全; 实例快速上手 -ASP.NET 4.5新特性WebAPI从入门到精通; Asp.net WebApi 项目示例(增删改查).

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

    一.课程介绍 曾经有一位不知名的讲师说过这么一句名言: 一门RPC技术不会,那么千万万门RPC技术将都不会!在今天移动互联网的时代,作为攻城师的我们,谁不想着只写一套API就可以让我们的Web, An ...

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

    路由其实也可以很简单-------Asp.net WebAPI学习笔记(一)   MVC也好,WebAPI也好,据我所知,有部分人是因为复杂的路由,而不想去学的.曾经见过一位程序猿,在他MVC程序中, ...

  4. 【ASP.NET Web API教程】1 ASP.NET Web API入门

    原文 [ASP.NET Web API教程]1 ASP.NET Web API入门 Getting Started with ASP.NET Web API第1章 ASP.NET Web API入门 ...

  5. ASP.NET WebApi 增删改查

    本篇是接着上一篇<ASP.NET WebApi 入门>来介绍的. 前言 习惯说 CRUD操作,它的意思是"创建. 读取. 更新和删除"四个基本的数据库操作.许多 HTT ...

  6. 连表查询都用Left Join吧 以Windows服务方式运行.NET Core程序 HTTP和HTTPS的区别 ASP.NET SignalR介绍 asp.net—WebApi跨域 asp.net—自定义轻量级ORM C#之23中设计模式

    连表查询都用Left Join吧   最近看同事的代码,SQL连表查询的时候很多时候用的是Inner Join,而我觉得对我们的业务而言,99.9%都应该使用Left Join(还有0.1%我不知道在 ...

  7. 重温ASP.NET WebAPI(一)初阶

    重温ASP.NET WebAPI(一)初阶   前言 本文为个人对WebApi的回顾无参考价值.主要简单介绍WEB api和webapi项目的基本结构,并创建简单地webaapi项目实现CRUD操作. ...

  8. Asp.Net WebApi核心对象解析(下篇)

    在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑, ...

  9. ASP.NET WebApi OWIN 实现 OAuth 2.0

    OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...

随机推荐

  1. 最近做RTSP流媒体的实时广播节目

    //h264视频流打包代码 // NALDecoder.cpp : Defines the entry point for the console application. #include < ...

  2. git 常用命令及问题解决(转)

    git init 产生的目录解释error: src refspec master does not match any.引起该错误的原因是,目录中没有文件,空目录是不能提交上去的error: ins ...

  3. OWIN编写中间件

    OWIN系列之自己动手编写中间件 一.前言 1.基于OWIN的项目摆脱System.Web束缚脱颖而出,轻量级+跨平台,使得ASP.NET应用程序只需依赖这个抽象接口,不用关心所运行的Web服务器. ...

  4. ServiceStack.Hello——跨平台.net REST api服务搭建

    ServiceStack.Hello--跨平台.net REST api服务搭建 自己创建: https://github.com/ServiceStack/ServiceStack/wiki/Cre ...

  5. NET5 Web应用程序

    ASP.NET5 Web应用程序结构 本文参考ASP.NET5 官方文档 Understanding ASP.NET 5 Web Apps,加入了一些个人理解,理解不对的地方希望大家能指出,互相学习. ...

  6. Uva 10131 Is Bigger Smarter? (LIS,打印路径)

    option=com_onlinejudge&Itemid=8&page=show_problem&problem=1072">链接:UVa 10131 题意: ...

  7. PHP关联数组和哈希表(hash table) 未指定

    PHP有数据的一个非常重要的一类,就是关联数组.又称为哈希表(hash table),是一种很好用的数据结构. 在程序中.我们可能会遇到须要消重的问题,举一个最简单的模型: 有一份username列表 ...

  8. uploadify控制 上传图片到百度云存储

    最近使用uploadify 控制图片上传到百度网盘....总的想法是 招待会uploadify获取文件传入后台,调用百度云存储api上传到百度网盘,返回url 联系.送存储在数据库中的链接.因此,我们 ...

  9. UVALive 6469 Deranged Exams (排列:力绝对是无辜的高中知识啊)

    标题手段 : 给你个n([1,17])表达n无论从数据结构.然后n个对这些术语的定义,让你对这些术语和定义对号入座(相当于进行连线,A术语连A术语的定义).然后一个 k([0,n]).问你至少前k个术 ...

  10. IIS安装asp组件:JMail 邮件收发组件

    JMail简介 jmail是一种服务器端的邮件发送组件,和个人用的客户端邮件软件不一样的.jmail是在服务器上给程序用来发邮件用的,除了软件编程人员,其他人一般平常用不上. jmail是一个第三方邮 ...