让ASP.NET Web API支持JSONP和W3C的CORS规范是解决“跨域资源共享”的两种途径,在《通过扩展让ASP.NET Web API支持JSONP》中我们实现了前者,并且在《W3C的CORS Specification》一文中我们对W3C的CORS规范进行了详细介绍,现在我们通过一个具体的实例来演示如何利用ASP.NET Web API具有的扩展点来实现针对CORS的支持。

目录
一、ActionFilter OR HttpMessageHandler
二、用于定义CORS资源授权策略的特性——CorsAttribute
三、实施CORS授权检验的HttpMessageHandler——CorsMessageHandler
四、CorsMessageHandler针对简单跨域资源请求的授权检验
五、CorsMessageHandler针对Preflight Request的授权检验

一、ActionFilter OR HttpMessageHandler

通过上面针对W3C的CORS规范的介绍,我们知道跨域资源共享实现的途径就是资源的提供者利用预定义的响应报头表明自己是否将提供的资源授权给了客户端JavaScript程序,而支持CORS的浏览器利用这些响应报头决定是否允许JavaScript程序操作返回的资源。对于ASP .NET Web API来说,如果我们具有一种机制能够根据预定义的资源授权规则自动生成和添加针对CORS的响应报头,那么资源的跨域共享就迎刃而解了。

那么如何利用ASP.NET Web API的扩展实现针对CORS响应报头的自动添加呢?可能有人首先想到的是利用HttpActionFilter在目标Action方法执行之后自动添加CORS响应报头。这种解决方案对于简单跨域资源请求是没有问题的,但是不要忘了:对于非简单跨域资源请求,浏览器会采用“预检(Preflight)”机制。目标Action方法只会在处理真正跨域资源请求的过程中才会执行,但是对于采用“OPTIONS”作为HTTP方法的预检请求,根本找不到匹配的目标Action方法。

为了能够有效地应付浏览器采用的预检机制,我们只能在ASP.NET Web API的消息处理管道级别实现对提供资源的授权检验和对CORS响应报头的添加。我们只需要为此创建一个自定义的HttpMessageHandler即可,不过在此之前我们先来介绍用于定义资源授权策略的CorsAttribute特性。

二、用于定义CORS资源授权策略的特性——CorsAttribute

我们将具有如下定义的CorsAttribute特性直接应用到某个HttpController或者定义其中的某个Action方法上来定义相关的资源授权策略。简单起见,我们的授权策略只考虑请求站点,而忽略请求提供的自定义报头和携带的用户凭证。如下面的代码片断所示,CorsAttribute具有一个只读属性AllowOrigins表示一组被授权站点对应的Uri数组,具体站点列表在构造函数中指定。另一个只读属性ErrorMessage表示在请求没有通过授权检验情况下返回的错误消息。

   1: [AttributeUsage( AttributeTargets.Class| AttributeTargets.Method)]

   2: public class CorsAttribute: Attribute

   3: {

   4:     public Uri[]      AllowOrigins { get; private set; }

   5:     public string     ErrorMessage { get; private set; }

   6:  

   7:     public CorsAttribute(params string[] allowOrigins)

   8:     {

   9:         this.AllowOrigins = (allowOrigins ?? new string[0]).Select(origin => new Uri(origin)).ToArray();

  10:     }

  11:  

  12:     public bool TryEvaluate(HttpRequestMessage request, out IDictionary<string, string> headers)

  13:     {

  14:         headers = null;

  15:         string origin = request.Headers.GetValues("Origin").First();

  16:         Uri originUri = new Uri(origin);

  17:         if (this.AllowOrigins.Contains(originUri))

  18:         {

  19:             headers = this.GenerateResponseHeaders(request);

  20:             return true;

  21:         }

  22:         this.ErrorMessage = "Cross-origin request denied";

  23:         return false;

  24:     }

  25:  

  26:     private IDictionary<string, string> GenerateResponseHeaders(HttpRequestMessage request)

  27:     {

  28:         //设置响应报头"Access-Control-Allow-Methods"

  29:         string origin = request.Headers.GetValues("Origin").First();

  30:         Dictionary<string, string> headers = new Dictionary<string, string>();

  31:         headers.Add("Access-Control-Allow-Origin", origin);

  32:         if (request.IsPreflightRequest())

  33:         {

  34:             //设置响应报头"Access-Control-Request-Headers"

  35:             //和"Access-Control-Allow-Headers"

  36:             headers.Add("Access-Control-Allow-Methods", "*");

  37:             string requestHeaders = request.Headers.GetValues("Access-Control-Request-Headers").FirstOrDefault();

  38:             if (!string.IsNullOrEmpty(requestHeaders))

  39:             {

  40:                 headers.Add("Access-Control-Allow-Headers", requestHeaders);

  41:             }

  42:         }

  43:         return headers;

  44:     }

  45: }

我们将针对请求的资源授权检查定义在TryEvaluate方法中,其返回至表示请求是否通过了授权检查,输出参数headers通过返回的字典对象表示最终添加的CORS响应报头。在该方法中,我们从指定的HttpRequestMessage对象中提取表示请求站点的“Origin”报头值。如果请求站点没有在通过AllowOrigins属性表示的授权站点内,则意味着请求没有通过授权检查,在此情况下我们会将ErrorMessage属性设置为“Cross-origin request denied”。

在请求成功通过授权检查的情况下,我们调用另一个方法GenerateResponseHeaders根据请求生成我们需要的CORS响应报头。如果当前为简单跨域资源请求,只会返回针对“Access-Control-Allow-Origin”的响应报头,其值为请求站点。对于预检请求来说,我们还需要额外添加针对“Access-Control-Request-Headers”和“Access-Control-Allow-Methods”的响应报头。对于前者,我们直接采用请求的“Access-Control-Request-Headers”报头值,而后者被直接设置为“*”。

在上面的程序中,我们通过调用HttpRequestMessage的扩展方法IsPreflightRequest来判断是否是一个预检请求,该方法定义如下。从给出的代码片断可以看出,我们判断预检请求的条件是:包含报头“Origin”和“Access-Control-Request-Method”的HTTP-OPTIONS请求。

   1: public static class HttpRequestMessageExtensions

   2: {

   3:     public static bool IsPreflightRequest(this HttpRequestMessage request)

   4:     {

   5:         return request.Method == HttpMethod.Options &&

   6:             request.Headers.GetValues("Origin").Any() &&

   7:             request.Headers.GetValues("Access-Control-Request-Method").Any();

   8:     }

   9: }

 

三、实施CORS授权检验的HttpMessageHandler——CorsMessageHandler

针对跨域资源共享的实现最终体现在具有如下定义的CorsMessageHandler类型上,它直接继承自DelegatingHandler。在实现的SendAsync方法中,CorsMessageHandler利用应用在目标Action方法或者HttpController类型上CorsAttribute来对请求实施授权检验,最终将生成的CORS报头添加到响应报头列表中。

   1: public class CorsMessageHandler: DelegatingHandler

   2: {

   3:     protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

   4:     {

   5:         //得到描述目标Action的HttpActionDescriptor

   6:         HttpMethod originalMethod = request.Method;

   7:         bool isPreflightRequest = request.IsPreflightRequest();

   8:         if (isPreflightRequest)

   9:         {

  10:             string method = request.Headers.GetValues("Access-Control-Request-Method").First();

  11:             request.Method = new HttpMethod(method);

  12:         }

  13:         HttpConfiguration configuration = request.GetConfiguration();

  14:         HttpControllerDescriptor controllerDescriptor = configuration.Services.GetHttpControllerSelector().SelectController(request);

  15:         HttpControllerContext controllerContext = new HttpControllerContext(request.GetConfiguration(), request.GetRouteData(), request)

  16:         {

  17:             ControllerDescriptor = controllerDescriptor

  18:         };

  19:         HttpActionDescriptor actionDescriptor = configuration.Services.GetActionSelector().SelectAction(controllerContext);

  20:  

  21:         //根据HttpActionDescriptor得到应用的CorsAttribute特性

  22:         CorsAttribute corsAttribute = actionDescriptor.GetCustomAttributes<CorsAttribute>().FirstOrDefault()??

  23:             controllerDescriptor.GetCustomAttributes<CorsAttribute>().FirstOrDefault();

  24:         if(null == corsAttribute)

  25:         {

  26:             return base.SendAsync(request, cancellationToken);

  27:         }

  28:  

  29:         //利用CorsAttribute实施授权并生成响应报头

  30:         IDictionary<string,string> headers;

  31:         request.Method = originalMethod;

  32:         bool authorized = corsAttribute.TryEvaluate(request, out headers);

  33:         HttpResponseMessage response;

  34:         if (isPreflightRequest)

  35:         {

  36:             if (authorized)

  37:             {

  38:                 response = new HttpResponseMessage(HttpStatusCode.OK);                   

  39:             }

  40:             else

  41:             {

  42:                 response = request.CreateErrorResponse(HttpStatusCode.BadRequest, corsAttribute.ErrorMessage);

  43:             }

  44:         }

  45:         else

  46:         {

  47:             response = base.SendAsync(request, cancellationToken).Result;                

  48:         }

  49:  

  50:         //添加响应报头

  51:         foreach (var item in headers)

  52:         {

  53:             response.Headers.Add(item.Key, item.Value);

  54:         }

  55:         return Task.FromResult<HttpResponseMessage>(response);

  56:     }

  57: }

具体来说,我们通过注册到当前ServicesContainer上的HttpActionSelector根据请求得到描述目标Action的HttpActionDescriptor对象,为此我们需要根据请求手工生成作为HttpActionSelector的SelectAction方法参数的HttpControllerContext对象。对此有一点需要注意:由于预检请求采用的HTTP方法为“OPTIONS”,我们需要将其替换成代表真正跨域资源请求的HTTP方法,也就是预检请求的“Access-Control-Request-Method”报头值。

在得到描述目标Action的HttpActionDescriptor对象后,我们调用其GetCustomAttributes<T>方法得到应用在Action方法上的CorsAttribute特性。如果这样的特性不存在,在调用同名方法得到应用在HttpController类型上的CorsAttribute特性。

接下来我们调用CorsAttribute的TryEvaluate方法对请求实施资源授权检查并得到一组CORS响应报头,作为参数的HttpRequestMessage对象的HTTP方法应该恢复其原有的值。对于预检请求,在请求通过授权检查之后我们会创建一个状态为“200, OK”的响应,否则会根据错误消息创建创建一个状态为“400, Bad Request”的响应。

对于非预检请求来说(可能是简单跨域资源请求,也可能是继预检请求之后发送的真正的跨域资源请求),我们调用基类的SendAsync方法将请求交付给后续的HttpMessageHandler进行处理并最终得到最终的响应。我们最终将调用CorsAttribute的TryEvaluate方法得到的响应报头逐一添加到响应报头列表中。

四、CorsMessageHandler针对简单跨域资源请求的授权检验

接下来我们通过于一个简单的实例来演示同源策略针对跨域Ajax请求的限制。如图右图所示,我们利用Visual Studio在同一个解决方案中创建了两个Web应用。从项目名称可以看出,WebApi和MvcApp分别为ASP.NET Web API和MVC应用,后者是Web API的调用者。我们直接采用默认的IIS Express作为两个应用的宿主,并且固定了端口号:WebApi和MvcApp的端口号分别为“3721”和“9527”,所以指向两个应用的URI肯定不可能是同源的。我们在WebApi应用中定义了如下一个继承自ApiController的ContactsController类型,它具有的唯一Action方法GetAllContacts返回一组联系人列表。

如下面的代码片断所示,用于获取所有联系人列表的Action方法GetAllContacts返回一个JsonResult<IEnumerable<Contact>>对象,但是该方法上面应用了我们定义的CorsAttribute特性,并将“http://localhost:9527”(客户端ASP.NET MVC应用的站点)设置为允许授权的站点。

   1: public class ContactsController : ApiController

   2: {

   3:     [Cors("http://localhost:9527")] 
   4:     public IHttpActionResult GetAllContacts()

   5:     {

   6:         Contact[] contacts = new Contact[]

   7:         {

   8:             new Contact{ Name="张三", PhoneNo="123", EmailAddress="zhangsan@gmail.com"},

   9:             new Contact{ Name="李四", PhoneNo="456", EmailAddress="lisi@gmail.com"},

  10:             new Contact{ Name="王五", PhoneNo="789", EmailAddress="wangwu@gmail.com"},

  11:         };

  12:         return Json<IEnumerable<Contact>>(contacts);

  13:     }

  14: }

在Global.asax中,我们采用如下的方式将一个CorsMessageHandler对象添加到ASP.NET Web API的消息处理管道中。

   1: public class WebApiApplication : System.Web.HttpApplication

   2: {

   3:     protected void Application_Start()

   4:     {

   5:         GlobalConfiguration.Configuration.MessageHandlers.Add(new CorsMessageHandler ());

   6:         //其他操作

   7:     }

   8: }

接下来们在MvcApp应用中定义如下一个HomeController,默认的Action方法Index会将对应的View呈现出来。

   1: public class HomeController : Controller

   2: {

   3:     public ActionResult Index()

   4:     {

   5:         return View();

   6:     }

   7: }

如下所示的是Action方法Index对应View的定义。我们的目的在于:当页面成功加载之后以Ajax请求的形式调用上面定义的Web API获取联系人列表,并将自呈现在页面上。如下面的代码片断所示,Ajax调用和返回数据的呈现是通过调用jQuery的getJSON方法完成的。在此基础上直接调用我们的ASP.NET MVC程序照样会得到如右图所示的结果.

   1: <html>

   2: <head>

   3:     <title>联系人列表</title>

   4: <script type="text/javascript" src="@Url.Content("~/scripts/jquery-1.10.2.js")"></script>
   1:  

   2: </head>

   3: <body>

   4:     <ul id="contacts"></ul>

   5:     <script type="text/javascript">

   6:         $(function ()

   7:         {

   8:             var url = "http://localhost:3721/api/contacts";

   9:             $.getJSON(url, null, function (contacts) {

  10:                 $.each(contacts, function (index, contact)

  11:                 {

  12:                     var html = "<li><ul>";

  13:                     html += "<li>Name: " + contact.Name + "</li>";

  14:                     html += "<li>Phone No:" + contact.PhoneNo + "</li>";

  15:                     html += "<li>Email Address: " + contact.EmailAddress + "</li>";

  16:                     html += "</ul>";

  17:                     $("#contacts").append($(html));

  18:                 });

  19:             });

  20:         });

  21:     

</script>

   5: </body>

   6: </html>

如果我们利用Fiddler来检测针对Web API调用的Ajax请求,如下所示的请求和响应内容会被捕捉到,我们可以清楚地看到利用CorsMessageHandler添加的“Access-Control-Allow-Origin”报头出现在响应的报头集合中。

   1: GET http://localhost:3721/api/contacts HTTP/1.1

   2: Host: localhost:3721

   3: Connection: keep-alive

   4: Accept: application/json, text/javascript, */*; q=0.01

   5: Origin: http://localhost:9527

   6: User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36

   7: Referer: http://localhost:9527/

   8: Accept-Encoding: gzip,deflate,sdch

   9: Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh-TW;q=0.4

  10:  

  11: HTTP/1.1 200 OK

  12: Cache-Control: no-cache

  13: Pragma: no-cache

  14: Content-Length: 205

  15: Content-Type: application/json; charset=utf-8

  16: Expires: -1

  17: Server: Microsoft-IIS/8.0

  18: Access-Control-Allow-Origin: http://localhost:9527 
  19: X-AspNet-Version: 4.0.30319

  20: X-SourceFiles: =?UTF-8?B?RTpc5oiR55qE6JGX5L2cXEFTUC5ORVQgV2ViIEFQSeahhuaetuaPnmFxOZXcgU2FtcGxlc1xDaGFwdGVyIDE0XFMxNDAzXFdlYkFwaVxhcGlcY29udGFjdHM=?=

  21: X-Powered-By: ASP.NET

  22: Date: Wed, 04 Dec 2013 01:50:01 GMT

  23:  

  24: [{"Name":"张三","PhoneNo":"123","EmailAddress":"zhangsan@gmail.com"},{"Name":"李四","PhoneNo":"456","EmailAddress":"lisi@gmail.com"},{"Name":"王五","PhoneNo":"789","EmailAddress":wangwu@gmail.com}]

 

五、CorsMessageHandler针对Preflight Request的授权检验

从上面给出的请求和响应内容可以确定Web API的调用采用的是“简单跨域资源请求”,所以并没有采用“预检”机制。如何需要迫使浏览器采用预检机制,就需要了解我们在《W3C的CORS Specification》上面提到的简单跨域资源请求具有的两个条件

  • 采用简单HTTP方法(GET、HEAD和POST);
  • 不具有非简单请求报头的自定义报头。

只要打破其中任何一个条件就会迫使浏览器采用预检机制,我们选择为请求添加额外的自定义报头。在ASP.NET MVC应用用户调用Web API的View中,针对Ajax请求调用Web API的JavaScript程序被改写成如下的形式:我们在发送Ajax请求之前利用setRequestHeader函数添加了两个名称分别为“'X-Custom-Header1”和“'X-Custom-Header2”的自定义报头。

   1: <html>

   2: <head>

   3:     <title>联系人列表</title>

   4: <script type="text/javascript" src="@Url.Content("~/scripts/jquery-1.10.2.js")"></script>
   1:  
   2: </head>
   3: <body>
   4:     <ul id="contacts"></ul>
   5:     <script type="text/javascript">
   6:         $(function ()
   7:         {
   8:             $.ajax({
   9:                 url         : 'http://localhost:3721/api/contacts',
  10:                 type        : 'GET',
  11:                 success     : listContacts,
  12:                 beforeSend  : setRequestHeader
  13:             });
  14:         });
  15:  
  16:         function listContacts(contacts)
  17:         {
  18:             $.each(contacts, function (index, contact) {
  19:                 var html = "<li><ul>";
  20:                 html += "<li>Name: " + contact.Name + "</li>";
  21:                 html += "<li>Phone No:" + contact.PhoneNo + "</li>";
  22:                 html += "<li>Email Address: " + contact.EmailAddress + "</li>";
  23:                 html += "</ul>";
  24:                 $("#contacts").append($(html));
  25:             });
  26:         }
  27:  
  28:         function setRequestHeader(xmlHttpRequest)
  29:         {
  30:             xmlHttpRequest.setRequestHeader('X-Custom-Header1', 'Foo');
  31:             xmlHttpRequest.setRequestHeader('X-Custom-Header2', 'Bar');
  32:         }
  33:     
 </script>

   5: </body>

   6: </html>

再次运行我们的ASP.NET MVC程序,依然会得正确的输出结果,但是针对Web API的调用则会涉及到两次消息交换,分别针对预检请求和真正的跨域资源请求。从下面给出的两次消息交换涉及到的请求和响应内容可以看出:自定义的两个报头名称会出现在采用“OPTIONS”作为HTTP方法的预检请求的“Access-Control-Request-Headers”报头中,利用CorsMessageHandler添加的3个报头(“Access-Control-Allow-Origin”、“Access-Control-Allow-Methods”和“Access-Control-Allow-Headers”)均出现在针对预检请求的响应中。

   1: OPTIONS http://localhost:3721/api/contacts HTTP/1.1

   2: Host: localhost:3721

   3: Connection: keep-alive

   4: Cache-Control: max-age=0

   5: Access-Control-Request-Method: GET 
   6: Origin: http://localhost:9527

   7: User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36

   8: Access-Control-Request-Headers: accept, x-custom-header1, x-custom-header2 
   9: Accept: */*

  10: Referer: http://localhost:9527/

  11: Accept-Encoding: gzip,deflate,sdch

  12: Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh-TW;q=0.4

  13:  

  14: HTTP/1.1 200 OK

  15: Cache-Control: no-cache

  16: Pragma: no-cache

  17: Expires: -1

  18: Server: Microsoft-IIS/8.0

  19: Access-Control-Allow-Origin: http://localhost:9527 
  20: Access-Control-Allow-Methods: * 
  21: Access-Control-Allow-Headers: accept, x-custom-header1, x-custom-header2 
  22: X-AspNet-Version: 4.0.30319

  23: X-SourceFiles: =?UTF-8?B??=

  24: X-Powered-By: ASP.NET

  25: Date: Wed, 04 Dec 2013 02:11:16 GMT

  26: Content-Length: 0

  27:  

  28: --------------------------------------------------------------------------------

  29: GET http://localhost:3721/api/contacts HTTP/1.1

  30: Host: localhost:3721

  31: Connection: keep-alive

  32: Accept: */*

  33: X-Custom-Header1: Foo

  34: Origin: http://localhost:9527

  35: X-Custom-Header2: Bar

  36: User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36

  37: Referer: http://localhost:9527/

  38: Accept-Encoding: gzip,deflate,sdch

  39: Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh-TW;q=0.4

  40:  

  41: HTTP/1.1 200 OK

  42: Cache-Control: no-cache

  43: Pragma: no-cache

  44: Content-Length: 205

  45: Content-Type: application/json; charset=utf-8

  46: Expires: -1

  47: Server: Microsoft-IIS/8.0

  48: Access-Control-Allow-Origin: http://localhost:9527

  49: X-AspNet-Version: 4.0.30319

  50: X-SourceFiles: =?UTF-8?B?RTpc5oiR55qE6JGX5L2cXEFTUC5ORVQgV2ViIEFQSeahhuaetuaPreenmFxOZXcgU2FtcGxlc1xDaGFwdGVyIDE0XF9udGFjdHM=?=

  51: X-Powered-By: ASP.NET

  52: Date: Wed, 04 Dec 2013 02:11:16 GMT

  53:  

  54: [{"Name":"张三","PhoneNo":"123","EmailAddress":"zhangsan@gmail.com"},{"Name":"李四","PhoneNo":"456","EmailAddress":"lisi@gmail.com"},{"Name":"王五","PhoneNo":"789","EmailAddress":wangwu@gmail.com}]

 

CORS系列文章

[1] 同源策略与JSONP

[2] 利用扩展让ASP.NET Web API支持JSONP

[3] W3C的CORS规范

[4] 利用扩展让ASP.NET Web API支持CORS

[5] ASP.NET Web API自身对CORS的支持: 从实例开始

[6] ASP.NET Web API自身对CORS的支持: CORS授权策略的定义和提供

[7] ASP.NET Web API自身对CORS的支持: CORS授权检验的实施

[8] ASP.NET Web API自身对CORS的支持: CorsMessageHandler

通过扩展让ASP.NET Web API支持W3C的CORS规范的更多相关文章

  1. 通过扩展让ASP.NET Web API支持W3C的CORS规范(转载)

    转载地址:http://www.cnblogs.com/artech/p/cors-4-asp-net-web-api-04.html CORS(Cross-Origin Resource Shari ...

  2. 通过扩展让ASP.NET Web API支持JSONP

    同源策略(Same Origin Policy)的存在导致了"源"自A的脚本只能操作"同源"页面的DOM,"跨源"操作来源于B的页面将会被拒 ...

  3. (转)通过扩展让ASP.NET Web API支持JSONP

    原文地址:http://www.cnblogs.com/artech/p/3460544.html 同源策略(Same Origin Policy)的存在导致了“源”自A的脚本只能操作“同源”页面的D ...

  4. 通过扩展让ASP.NET Web API支持JSONP -摘自网络

    同源策略(Same Origin Policy)的存在导致了“源”自A的脚本只能操作“同源”页面的DOM,“跨源”操作来源于B的页面将会被拒绝.同源策略以及跨域资源共享在大部分情况下针对的是Ajax请 ...

  5. 通过微软的cors类库,让ASP.NET Web API 支持 CORS

    前言:因为公司项目需要搭建一个Web API 的后端,用来传输一些数据以及文件,之前有听过Web API的相关说明,但是真正实现的时候,感觉还是需要挺多知识的,正好今天有空,整理一下这周关于解决COR ...

  6. 让ASP.NET Web API支持POST纯文本格式(text/plain)的数据

    今天在web api中遇到了这样一个问题,虽然api的参数类型是string,但只能接收post body中json格式的string,不能接收原始string. web api是这样定义的: pub ...

  7. 让ASP.NET Web API支持$format参数的方法

    在不使用OData的情况下,也可以让ASP.NET Web API支持$format参数,只要在WebApiConfig里添加如下三行红色粗体代码即可: using System; using Sys ...

  8. [转]让ASP.NET Web API支持$format参数的方法

    本文转自:http://www.cnblogs.com/liuzhendong/p/4228592.html 在不使用OData的情况下,也可以让ASP.NET Web API支持$format参数, ...

  9. 让ASP.NET Web API支持text/plain内容协商

    ASP.NET Web API的内容协商(Content Negotiation)机制的理想情况是这样的:客户端在请求头的Accept字段中指定什么样的MIME类型,Web API服务端就返回对应的M ...

随机推荐

  1. Linux SHELL 命令入门题目答案(一)

    1.如何使用shell 打印 “Hello World!” (1)如果你希望打印 !,那就不要将其放入双引号中,或者你可以通过转义字符转义(2)echo 'hello world!' 使用单引号ech ...

  2. iOS Start developing ios apps (OC) pdf

    这是苹果官方最后一次更新的基于OC的iOS开发基础教程, 如果英文的看不懂,还有中文的版本哦. 点击下面的链接 好东西,分享给大家! 如果确实有帮到你,麻烦star一下我的github吧!

  3. 再记录一下如何配置oracle instantclient

    这问题遇到很多次,每次重装系统就遇到一次,却总是搞半天才搞定. 今天再次花费几个小时解决,终于有一个清晰的认识必须记录一下. 一.下载解压,不建任何目录,直接复制tnsname.ora过来.(当然也可 ...

  4. JavaScript-Object基础知识

    1.   定义:对象是JS的核心概念,也是最重要的数据类型.js的所有数据都可以被视为对象.                 对象是一种无序的数据集合,由若干个键值对(key:value)构成,由{ ...

  5. MVC框架

    MVC (Modal View Controler)本来是存在于Desktop程序中的,M是指数据模型,V是指用户界面,C则是控制器.使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用 ...

  6. CSS预处理框架:less,scss

    CSS预处理器:less和sass:CSS 预处理器是一种语言用来为 CSS 增加一些编程的的特性,无需考虑浏览器的兼容性问题,例如你可以在 CSS 中使用 变量.简单的程序逻辑.函数等等在编程语言中 ...

  7. thrift笔记

    Thrift tutorial 演示 python服务端与客户端本文的开发环境是windows 7 + python2.7.3Thrift官方主页:http://thrift.apache.org/先 ...

  8. vue-cli 组件的使用

    开始项目之前,先了解如何创建项目: http://www.cnblogs.com/pearl07/p/6247389.html 1,项目目录结构(路由是后来建的,将在下一篇使用路由,此处可忽略). 2 ...

  9. [LintCode] Trapping Rain Water 收集雨水

    Given n non-negative integers representing an elevation map where the width of each bar is 1, comput ...

  10. [CSS]textarea设置下划线格式

    功能要求:1:如何实现在多行文本框textarea里面每一行下面都有一条横线  2:textarea文本框里面有一段不能删掉 实现方法:横线用背景图片来做,不动的文字用浮动层+给textarea增加t ...