转载地址:http://www.cnblogs.com/artech/p/cors-4-asp-net-web-api-04.html

CORS(Cross-Origin Resource Sharing )跨域资源共享,让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表示在请求没有通过授权检验情况下返回的错误消息。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CorsAttribute : Attribute
{
public Uri[] AllowOrigins { get; private set; }
public string ErrorMessage { get; private set; } public CorsAttribute(params string[] allowOrigins)
{
this.AllowOrigins = (allowOrigins ?? new string[]).Select(origin => new Uri(origin)).ToArray();
} public bool TryEvaluate(HttpRequestMessage request, out IDictionary<string, string> headers)
{
headers = null;
string origin = request.Headers.GetValues("Origin").First();
Uri originUri = new Uri(origin);
if (this.AllowOrigins.Contains(originUri))
{
headers = this.GenerateResponseHeaders(request);
return true;
}
this.ErrorMessage = "Cross-origin request denied";
return false;
} private IDictionary<string, string> GenerateResponseHeaders(HttpRequestMessage request)
{
//设置响应报头"Access-Control-Allow-Methods"
string origin = request.Headers.GetValues("Origin").First();
Dictionary<string, string> headers = new Dictionary<string, string>();
headers.Add("Access-Control-Allow-Origin", origin);
if (request.IsPreflightRequest())
{
//设置响应报头"Access-Control-Request-Headers"
//和"Access-Control-Allow-Headers"
headers.Add("Access-Control-Allow-Methods", "*");
string requestHeaders = request.Headers.GetValues("Access-Control-Request-Headers").FirstOrDefault();
if (!string.IsNullOrEmpty(requestHeaders))
{
headers.Add("Access-Control-Allow-Headers", requestHeaders);
}
}
return headers;
}
}

我们将针对请求的资源授权检查定义在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请求。

public static class HttpRequestMessageExtensions
{
public static bool IsPreflightRequest(this HttpRequestMessage request)
{
return request.Method == HttpMethod.Options &&
request.Headers.GetValues("Origin").Any() &&
request.Headers.GetValues("Access-Control-Request-Method").Any();
}
}

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

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

public class CorsMessageHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
//得到描述目标Action的HttpActionDescriptor
HttpMethod originalMethod = request.Method;
bool isPreflightRequest = request.IsPreflightRequest();
if (isPreflightRequest)
{
string method = request.Headers.GetValues("Access-Control-Request-Method").First();
request.Method = new HttpMethod(method);
}
HttpConfiguration configuration = request.GetConfiguration();
HttpControllerDescriptor controllerDescriptor =
configuration.Services.GetHttpControllerSelector().SelectController(request);
HttpControllerContext controllerContext = new HttpControllerContext(request.GetConfiguration(),
request.GetRouteData(), request)
{
ControllerDescriptor = controllerDescriptor
};
HttpActionDescriptor actionDescriptor =
configuration.Services.GetActionSelector().SelectAction(controllerContext); //根据HttpActionDescriptor得到应用的CorsAttribute特性
CorsAttribute corsAttribute = actionDescriptor.GetCustomAttributes<CorsAttribute>().FirstOrDefault() ??
controllerDescriptor.GetCustomAttributes<CorsAttribute>().FirstOrDefault();
if (null == corsAttribute)
{
return base.SendAsync(request, cancellationToken);
} //利用CorsAttribute实施授权并生成响应报头
IDictionary<string, string> headers;
request.Method = originalMethod;
bool authorized = corsAttribute.TryEvaluate(request, out headers);
HttpResponseMessage response;
if (isPreflightRequest)
{
if (authorized)
{
response = new HttpResponseMessage(HttpStatusCode.OK);
}
else
{
response = request.CreateErrorResponse(HttpStatusCode.BadRequest, corsAttribute.ErrorMessage);
}
}
else
{
response = base.SendAsync(request, cancellationToken).Result;
} //添加响应报头
foreach (var item in headers)
{
response.Headers.Add(item.Key, item.Value);
}
return Task.FromResult<HttpResponseMessage>(response);
}
}

具体来说,我们通过注册到当前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}]

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

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

    让ASP.NET Web API支持JSONP和W3C的CORS规范是解决"跨域资源共享"的两种途径,在<通过扩展让ASP.NET Web API支持JSONP>中我们 ...

  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. 上传读取Excel文件数据

    /// <summary> /// 上传读取Excel文件数据 /// 来自http://www.cnblogs.com/cielwater /// </summary> // ...

  2. youtube不显示其他人头像

    我用的是ss , 右键ss选择pac->编辑本地pac 把ggpht.com加入pac列表即可

  3. Android 布局 中实现适应屏幕大小及组件滚动

    要实现如图的布局: 这是在eclipse可视化窗口中的截图,但实际运行在Android设备上可能出现的问题有: (1):当编辑图1中的最后一个EditText时,输入法的编辑界面会把底部的Button ...

  4. Request 分别获取具有相同 name 属性表单元素值

    html 中是允许多个具有相同name属性的元素的,例如 <div> <input name="txtName" id="txtFirstName&qu ...

  5. 构建高性能WEB站点笔记二

    构建高性能WEB站点笔记 因为是跳着看的,后面看到有提到啥epoll模型,那就补充下前面的知识. 第三章 服务器并发处理能力 3.2 CPU并发计算 进程 好处:cpu 时间的轮流使用.对CPU计算和 ...

  6. SQL Server 恢复过程

    在恢复过程中.只会分析那些自最后一个检查点之后发生的更改,以确定是否需要重做还是撤销. 在最后一个检查点之前完成的操作都会精确的反应到数据文件中,恢复过程不需要做其它的事. 第一阶段: 分析. 这个阶 ...

  7. AndroidUI组件之TabHost

    package com.gc.tabhost; /** * @author Android将军 * * * * 1.TabHost是一种非常有用的组件,TabHost能够非常方便地在窗体上放置 * 多 ...

  8. C++学习的心路历程之心理障碍

    断断续续的C++学习已经过了1年多了,可是,我还是没有迈出可以自如输出写点什么的那一步.甚至我因为这个老是怀疑自己的智商,我是真心想学懂,因为这个关系到我的就业,直接关系到我的饭碗.我是十分的着急,可 ...

  9. Scrapy详解

    一.爬虫生态框架 在管道传数据只能传字典和items类型. 将 上一return语句注释则会报错  如: 如上图,爬虫文件中有一个name属性,如果多个爬虫可以通过这个属性在管道控制分析的是哪个爬虫的 ...

  10. shiro + jwt 实现 请求头中的 rememberMe 时间限制功能

    前言: 上一篇提出, 通过修改 rememberMe 的编码来实现 rememberMe的功能的设想, 事后我去尝试实现了一番, 发现太麻烦, 还是不要那么做吧. 程序还是要越简单越好. 那功能总是要 ...