[MVC]ASP.NET MVC Custom Error Handling Application

mikel阅读(721)

通过Application_Error对ASP.NET MVC的所有异常进行默认指向,也可以通过webConfig的        <customErrors mode="RemoteOnly" defaultRedirect="\Error\Index">
            <error statusCode="403" redirect="\Error\Http404" />
            <error statusCode="404" redirect="\Error\Http404" />
        </customErrors>

来指定,不过这样的记录日志异常就需要在ErrorController中进行了

转载:http://stackoverflow.com/questions/1171035/asp-net-mvc-custom-error-handling-applicationerror-global-asax

Instead of creating a new route for that, you could just redirect to your controller/action and pass the information via querystring. For instance:

protected void Application_Error(object sender, EventArgs e) {
  Exception exception = Server.GetLastError();
  Response.Clear();
  HttpException httpException = exception as HttpException;
  if (httpException != null) {
    string action;
    switch (httpException.GetHttpCode()) {
      case 404:
        // page not found
        action = "HttpError404";
        break;
      case 500:
        // server error
        action = "HttpError500";
        break;
      default:
        action = "General";
        break;
      }
      // clear error on server
      Server.ClearError();
      Response.Redirect(String.Format("~/Error/{0}/?message={1}", action, exception.Message));
    }

Then your controller will receive whatever you want:

// GET: /Error/HttpError404
public ActionResult HttpError404(string message) {
   return View("SomeView", message);
}

There are some tradeoffs with your approach. Be very very careful with looping in this kind of error handling. Other thing is that since you are going through the ASP.NET pipeline to handle a 404, you will create a session object for all those hits. This can be an issue (performance) for heavly used systems.

Cheers,

André Carlucci

[MVC]Creating Unit Tests for ASP.NET MVC Applicati

mikel阅读(870)

转载:http://www.ASP.NET/learn/mvc/tutorial-07-cs.aspx

Creating Unit Tests for ASP.NET MVC Applications

The goal of this tutorial is to demonstrate how you can write unit tests for the controllers in your ASP.NET MVC applications. We discuss how to build three different types of unit tests. You learn how to test the view returned by a controller action, how to test the View Data returned by a controller action, and how to test whether or not one controller action redirects you to a second controller action.

Creating the Controller under Test

Let’s start by creating the controller that we intend to test. The controller, named the ProductController, is contained in Listing 1.

Listing 1 – ProductController.cs

using System;
using System.Web.Mvc;
namespace Store.Controllers
{
     public class ProductController : Controller
     {
          public ActionResult Index()
          {
               // Add action logic here
               throw new NotImplementedException();
          }
          public ActionResult Details(int Id)
          {
               return View("Details");
          }
     }
}

The ProductController contains two action methods named Index() and Details(). Both action methods return a view. Notice that the Details() action accepts a parameter named Id.

Testing the View returned by a Controller

Imagine that we want to test whether or not the ProductController returns the right view. We want to make sure that when the ProductController.Details() action is invoked, the Details view is returned. The test class in Listing 2 contains a unit test for testing the view returned by the ProductController.Details() action.

Listing 2 – ProductControllerTest.cs

using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Store.Controllers;
namespace StoreTests.Controllers
{
     [TestClass]
     public class ProductControllerTest
     {
          [TestMethod]
          public void TestDetailsView()
          {
               var controller = new ProductController();
               var result = controller.Details(2) as ViewResult;
               Assert.AreEqual("Details", result.ViewName);
          }
     }
}

The class in Listing 2 includes a test method named TestDetailsView(). This method contains three lines of code. The first line of code creates a new instance of the ProductController class. The second line of code invokes the controller’s Details() action method. Finally, the last line of code checks whether or not the view returned by the Details() action is the Details view.

The ViewResult.ViewName property represents the name of the view returned by a controller. One big warning about testing this property. There are two ways that a controller can return a view. A controller can explicitly return a view like this:

public ActionResult Details(int Id)
{
     return View("Details");
}

Alternatively, the name of the view can be inferred from the name of the controller action like this:

public ActionResult Details(int Id)
{
     return View();
}

This controller action also returns a view named Details. However, the name of the view is inferred from the action name. If you want to test the view name, then you must explicitly return the view name from the controller action.

You can run the unit test in Listing 2 by either entering the keyboard combination Ctrl-R, A or by clicking the Run All Tests in Solution button (see Figure 1). If the test passes, you’ll see the Test Results window in Figure 2.

Figure 01: Run All Tests in Solution (Click to view full-size image)

Figure 02: Success! (Click to view full-size image)

Testing the View Data returned by a Controller

An MVC controller passes data to a view by using something called View Data. For example, imagine that you want to display the details for a particular product when you invoke the ProductController Details() action. In that case, you can create an instance of a Product class (defined in your model) and pass the instance to the Details view by taking advantage of View Data.

The modified ProductController in Listing 3 includes an updated Details() action that returns a Product.

Listing 3 – ProductController.cs

using System;
using System.Web.Mvc;
namespace Store.Controllers
{
     public class ProductController : Controller
     {
          public ActionResult Index()
          {
               // Add action logic here
               throw new NotImplementedException();
          }
          public ActionResult Details(int Id)
          {
               var product = new Product(Id, "Laptop");
               return View("Details", product);
          }
     }
}

First, the Details() action creates a new instance of the Product class that represents a laptop computer. Next, the instance of the Product class is passed as the second parameter to the View() method.

You can write unit tests to test whether the expected data is contained in view data. The unit test in Listing 4 tests whether or not a Product representing a laptop computer is returned when you call the ProductController Details() action method.

Listing 4 – ProductControllerTest.cs

using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Store.Controllers;
namespace StoreTests.Controllers
{
     [TestClass]
     public class ProductControllerTest
     {
          [TestMethod]
          public void TestDetailsViewData()
          {
               var controller = new ProductController();
               var result = controller.Details(2) as ViewResult;
               var product = (Product) result.ViewData.Model;
               Assert.AreEqual("Laptop", product.Name);
          }
     }
}

In Listing 4, the TestDetailsView() method tests the View Data returned by invoking the Details() method. The ViewData is exposed as a property on the ViewResult returned by invoking the Details() method. The ViewData.Model property contains the product passed to the view. The test simply verifies that the product contained in the View Data has the name Laptop.

Testing the Action Result returned by a Controller

A more complex controller action might return different types of action results depending on the values of the parameters passed to the controller action. A controller action can return a variety of types of action results including a ViewResult, RedirectToRouteResult, or JsonResult.

For example, the modified Details() action in Listing 5 returns the Details view when you pass a valid product Id to the action. If you pass an invalid product Id — an Id with a value less than 1 — then you are redirected to the Index() action.

Listing 5 – ProductController.cs

using System;
using System.Web.Mvc;
namespace Store.Controllers
{
     public class ProductController : Controller
     {
          public ActionResult Index()
          {
               // Add action logic here
               throw new NotImplementedException();
          }
          public ActionResult Details(int Id)
          {
               if (Id < 1)
                    return RedirectToAction("Index");
               var product = new Product(Id, "Laptop");
               return View("Details", product);
          }
     }
}

You can test the behavior of the Details() action with the unit test in Listing 6. The unit test in Listing 6 verifies that you are redirected to the Index view when an Id with the value -1 is passed to the Details() method.

Listing 6 – ProductControllerTest.cs

using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Store.Controllers;
namespace StoreTests.Controllers
{
     [TestClass]
     public class ProductControllerTest
     {
          [TestMethod]
          public void TestDetailsRedirect()
          {
               var controller = new ProductController();
               var result = (RedirectToRouteResult) controller.Details(-1);
               Assert.AreEqual("Index", result.Values["action"]);
          }
     }
}

When you call the RedirectToAction() method in a controller action, the controller action returns a RedirectToRouteResult. The test checks whether the RedirectToRouteResult will redirect the user to a controller action named Index.

Summary

In this tutorial, you learned how to build unit tests for MVC controller actions. First, you learned how to verify whether the right view is returned by a controller action. You learned how to use the ViewResult.ViewName property to verify the name of a view.

Next, we examined how you can test the contents of View Data. You learned how to check whether the right product was returned in View Data after calling a controller action.

Finally, we discussed how you can test whether different types of action results are returned from a controller action. You learned how to test whether a controller returns a ViewResult or a RedirectToRouteResult.

[ASP.NET]HttpHandler的认识与加深理解

mikel阅读(607)

HttpHandler 是HTTP请求的处理中心,真正地对客户端请求的服务器页面做出编译和执行,并将处理过后的信息附加在HTTP请求信息流中再次返回到 HttpModule中。HttpHandler与HttpModule不同,一旦定义了自己的HttpHandler类,那么它对系统的 HttpHandler的关系将是“覆盖”关系. HttpHandler功能的实现通过实现IHttpHandler接口来达到.

当一个HTTP请求经同HttpModule容器传递到HttpHandler容器中时,ASP.NET Framework会调用HttpHandler的ProcessRequest成员方法来对这个HTTP请求进行真正的处理。以一个ASPX页面为例, 正是在这里一个ASPX页面才被系统处理解析,并将处理完成的结果继续经由HttpModule传递下去,直至到达客户端。

对于ASPX页面,ASP.NET Framework在默认情况下是交给System.Web.UI.PageHandlerFactory这个HttpHandlerFactory来处 理的。所谓一个HttpHandlerFactory,所谓一个HttpHandlerFactory,是指当一个HTTP请求到达这个 HttpHandler Factory时,HttpHandlerFactory会提供出一个HttpHandler容器,交由这个HttpHandler容器来处理这个 HTTP请求。

一个HTTP请求都是最终交给一个HttpHandler容器中的ProcessRequest方法来处理的。 

IHttpHandler接口声明:

public interface IHttpHandler
{
   
bool IsReusable { get; }
   
public void ProcessRequest(HttpContext context); //请求处理函数
}

v2.0.50727 下的machine.config中httpHandlers结点是这样的:<httpHandlers />,并没有给出详细的处理程序,在Web.config中才能看到。

Code

可以看到,在<httpHandlers>结点中将不同的文件类型映射给不同的Handler去处理. 上面的type部分为了好显示我去掉了,其实你打开web.config,可以看到里面的内容的.

Verb属性:指定了处理程序支持的HTTP动作。*-支持所有的HTTP动作;“GET”-支持Get操作;“POST”-支持Post操作;“GET, POST”-支持两种操作

Path属性:指定了需要调用处理程序的路径和文件名(可以包含通配符).“*”、“*.aspx”

Type属性:用名字空间、类名称和程序集名称的组合形式指定处理程序或处理程序工厂的实际类型. ASP.NET运行时首先搜索bin目录中的DLL,接着在GAC中搜索 

IHttpHandler工厂

IHttpHandlerFactory的作用是对IHttpHandler进行管理。接口如下:

public interface IHttpHandlerFactory
{
    IHttpHandler GetHandler (HttpContext context,
string requestType,string url,string pathTranslated);
    
void ReleaseHandler (IHttpHandler handler);
}

GetHandler返回实现IHttpHandler接口的类的实例
ReleaseHandler使工厂可以重用现有的处理程序实例

HttpHandler的示例:

把硬盘上的图片以流的方式写在页面上 

class TestHandler : IHttpHandler
    {
        
public void ProcessRequest(HttpContext context)
        {
            FileStream fs 
= new FileStream(context.Server.MapPath("test.jpg"), FileMode.Open);
            
byte[] b = new byte[fs.Length];
            fs.Read(b, 
0, (int)fs.Length);
            fs.Close();
            context.Response.OutputStream.Write(b, 
0, b.Length);
        }
        
public bool IsReusable
        {
            
get
            {
                
return true;
            }
        }
    }

Web.Config配置文件

      <httpHandlers>

          <add verb="*" path="*" type="ClassLibrary831.TestHandler,ClassLibrary831"></add>

      </httpHandlers>

使用HttpHandler实现图片防盗链  

namespace CustomHandler
{
    
public class JpgHandler : IHttpHandler
    {
        
public void ProcessRequest(HttpContext context)
        {
            
// 获取文件服务器端物理路径
            string FileName = context.Server.MapPath(context.Request.FilePath);
            
// 如果UrlReferrer为空,则显示一张默认的禁止盗链的图片
            if (context.Request.UrlReferrer.Host == null)
            {
                context.Response.ContentType 
= "image/JPEG";
                context.Response.WriteFile(
"/error.jpg");
            }
            
else
            {
                
// 如果 UrlReferrer中不包含自己站点主机域名,则显示一张默认的禁止盗链的图片
                if (context.Request.UrlReferrer.Host.IndexOf("yourdomain.com"> 0)
                {
                    context.Response.ContentType 
= "image/JPEG";
                    context.Response.WriteFile(FileName);
                }
                
else
                {
                    context.Response.ContentType 
= "image/JPEG";
                    context.Response.WriteFile(
"/error.jpg");
                }
            }
        }
       
public bool IsReusable{
           
getreturn true; }
       }
    }
}

 编译后复制DLL文件到Bin文件夹下,然后在web.config注册
    <httpHandlers>
      <add path="*.jpg" verb="*" type="CustomHandler.JpgHandler, CustomHandler" />
    </httpHandlers>

通过IhttpHandler实现图片验证码

ashx是什么文件呢? 其实该文件用于写web handler. 在web里面添加一个这样的文件,可以看到下列的代码:

<%@ WebHandler Language="C#" Class="Handler" %>
using System;
using System.Web;
public class Handler : IHttpHandler 
{
    
    
public void ProcessRequest (HttpContext context) 
    {
        context.Response.ContentType 
= "text/plain";
        context.Response.Write(
"Hello World");
    }
 
    
public bool IsReusable {
        
get {
            
return false;
        }
    }
}

在上面的基础上修改我们需要的代码如下:

<%@ WebHandler Language="C#" Class="Handler" %>
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Web;
using System.Web.SessionState;
public class Handler : IHttpHandler, IRequiresSessionState {
    
public void ProcessRequest(HttpContext context) {
       context.Response.ContentType 
= "image/gif";
       
//建立Bitmap对象,绘图
       Bitmap basemap = new Bitmap(20060);
       Graphics graph 
= Graphics.FromImage(basemap);
       graph.FillRectangle(
new SolidBrush(Color.White), 0020060);
       Font font 
= new Font(FontFamily.GenericSerif, 48, FontStyle.Bold, GraphicsUnit.Pixel);
       Random r 
= new Random();
       
string letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ";
       
string letter;
       StringBuilder s 
= new StringBuilder();
       
       
//添加随机的五个字母
       for (int x = 0; x < 5; x++) {
           letter 
= letters.Substring(r.Next(0, letters.Length  1), 1);
           s.Append(letter);
           graph.DrawString(letter, font, 
new SolidBrush(Color.Black), x * 38, r.Next(015));
       }
       
       
//混淆背景
       Pen linePen = new Pen(new SolidBrush(Color.Black), 2);
       
for (int x = 0; x < 6; x++)
           graph.DrawLine(linePen, 
new Point(r.Next(0199), r.Next(059)), new Point(r.Next(0199), r.Next(059)));
              
       
//将图片保存到输出流中       
       basemap.Save(context.Response.OutputStream, ImageFormat.Gif);
       context.Session[
"CheckCode"= s.ToString();   //如果没有实现IRequiresSessionState,则这里会出错,也无法生成图片
       context.Response.End();      
    }
    
public bool IsReusable {
       
get { return true; }
    }
}

最后的就是需要在页面上引用这个验证码了. 创建一个新的aspx页面.
<img src="Handler.ashx" alt="图片验证码" />

需要注意的就是我们继承了一个IRequiresSessionState接口:

public interface IRequiresSessionState{}

Handler类不仅需要实现 IHttpHandler接口,为了在这个Handler类中使用SessionState,还需要实现IRequiresSessionState接 口,对于这个接口,MSDN的解释是这样的:Specifies that the target HTTP handler requires read and write access to session-state values. This is a marker interface and has no methods.(中文大概的意思:指定当前Http Handler需要对SessionState值的读写访问权。这是一个标记接口,没有任何方法)。这个接口没有任何需要实现的方法或属性,大家只要记 得:如果想在HttpHandler中使用SessionState,必须实现这个接口,实际上也就是在类的标头将这个接口加进去。

收集整理与自己见解总结, 且当回忆, 下次再讨论一下HttpModule的理论与方法.

[ASP.NET]HttpModule的认识与深入理解

mikel阅读(581)

HttpModule 是向实现类提供模块初始化和处置事件。当一个HTTP请求到达HttpModule时,整个ASP.NET Framework系统还并没有对这个HTTP请求做任何处理,也就是说此时对于HTTP请求来讲,HttpModule是一个HTTP请求的“必经之路 ”,所以可以在这个HTTP请求传递到真正的请求处理中心(HttpHandler)之前附加一些需要的信息在这个HTTP请求信息之上,或者针对截获的 这个HTTP请求信息作一些额外的工作,或者在某些情况下干脆终止满足一些条件的HTTP请求,从而可以起到一个Filter过滤器的作用。

首先你要实现IHttpModule接口这个接口只有两个方法,一个是Init方法一个Dispose方法.


using System;
namespace System.Web
{
    
// Summary:
    
//     Provides module initialization and disposal events to the implementing class.
    public interface IHttpModule
    {
        
// Summary:
        
//     Disposes of the resources (other than memory) used by the module that implements
        
//     System.Web.IHttpModule.
        void Dispose();
        
//
        
// Summary:
        
//     Initializes a module and prepares it to handle requests.
        
//
        
// Parameters:
        
//   context:
        
//     An System.Web.HttpApplication that provides access to the methods, properties,
        
//     and events common to all application objects within an ASP.NET application
        void Init(HttpApplication context);
    }
}

 

一个HTTP请求在HttpModule容器的传递过程中,会在某一时刻(ResolveRequestCache事件)将这个HTTP请求传递给 HttpHandler容器。在这个事件之后,HttpModule容器会建立一个HttpHandler的入口实例,但是此时并没有将HTTP请求控制 权交出,而是继续触发AcquireRequestState事件以及PreRequestHandlerExcute事件。在 PreRequestHandlerExcute事件之后,HttpModule窗口就会将控制权暂时交给HttpHandler容器,以便进行真正的 HTTP请求处理工作。

而在HttpHandler容器内部会执行ProcessRequest方法来处理HTTP请求。在容器HttpHandler处理完毕整 个HTTP请求之后,会将控制权交还给HttpModule,HttpModule则会继续对处理完毕的HTTP请求信息流进行层层的转交动作,直到返回 到客户端为止。

HttpModule过程在下面的事件:
BeginRequest    指示请求处理开始。
AuthenticateRequest PostAuthenticateRequest    封装请求身份验证过程。
AuthorizeRequest   PostAuthorizeRequest    封装请求授权过程。 
ResolveRequestCache PostResolveRequestCache    封装检查是否能利用以前缓存的输出页面处理请求的过程。
PostMapRequestHandler    指示已发现用于处理请求的 HTTP 处理程序。
AcquireRequestState    PostAcquireRequestState    封装对请求会话状态的检索。
PostRequestHandlerExecute    指示用于处理请求的 HTTP 处理程序已执行。
ReleaseRequestState   PostReleaseRequestState    封装对请求会话状态的发布。
UpdateRequestCache    PostUpdateRequestCache    封装检查是否应对请求的资源的输出进行缓存以备今后重复使用的过程。
EndRequest    指示请求处理结束。

 

可以利用HttpModule通过调用HttpApplication.CompleteRequest()方法实现当满足某一个条件时终止此次的 HTTP请求。需要注意的是,即使调用了HttpApplication.CompleteRequest()方法终止了一个HTTP请 求,ASP.NET Framework仍然会触发HttpApplication后面的这3个事件:EndRequest事件、PreSendRequestHeaders 事件、PreSendRequestContent事件。
如果存在多个自定义的HttpModule的话,当Module1终止了一个HTTP请求,这个HTTP请求将不会再触发Module2中相应的事件了,但Module2的最后三个事件仍会被触发。

Code

HttpModule示例:

 过滤http请求

Code

在web.confg中添加httpModules节点注册事件
<httpModules>
     <add name="AuthenticationModule" type="Business.AuthenticationModule, Business"/>
</httpModules>

 
判断浏览器的版本 

Code

注册web.config事件

实现URL重写

Code

 注册web.config事件

————————————————-完成————————————————

从上面的例子看出,其实是一个好简单的实现方法,就是在init中注册处理过程事件,把代码写完后,在web.congif中HttpModules注册就完成了.

开发程序千变万变, 通过这一方法可以编写出好多合适自己用的功能,这需要大家去实践了. 收集与整理,且当成长的回忆.

[ASP.NET] HttpApplication的认识与加深理解

mikel阅读(630)

HttpApplication 对象是经由HttpApplicationFactory.GetApplicationInstance(并最终调用 HttpRuntime.CreateNonPublicInstance)创建的HttpApplicationFactory它的主要任务是使用 URL 信息来查找 URL 虚拟目录和汇集的 HttpApplication 对象之间的匹配关系。

这个工厂类的行为概括为有以下几点
1、工厂类维护, HttpApplication 对象池并使用它们来处理应用程序的请求。池的寿命与应用程序的寿命相同。
2、 应用程序的第一个请求到达时,工厂类提取有关应用程序类型的信息(global.asax 类)、设置用于监视更改的文件、创建应用程序状态并触发 Application_OnStart 事件。工厂类从池中获取一个 HttpApplication 实例,并将要处理的请求放入实例中。如果没有可用的对象,则创建一个新的 HttpApplication 对象。要创建 HttpApplication 对象,需要先完成 global.asax 应用程序文件的编译。
3、HttpApplication 开始处理请求,并且只能在完成这个请求后才能处理新的请求。如果收到来自同一资源的新请求,则由池中的其他对象来处理。
4、应用程序对象允许所有注册的 HTTP 模块对请求进行预处理,并找出最适合处理请求的处理程序类型。这通过查找请求的 URL 的扩展和配置文件中的信息来完成。

HttpApplicationFactory.GetApplicationInstance创建HttpApplication实例中有三个关键方法:
HttpApplicationFactory._theApplicationFactory.EnsureInited()  该方法检查HttpApplicationFactory是否被初始化,如果没有,就通过HttpApplicationFactory.Init()进行初始化。Init()中,先获取global.asax文件的完整路径,然后调用CompileApplication()global.asax进行编译。
HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context)  创建特定的HttpApplication实例,触发ApplicationOnStart事件,执行ASP.global_asax中的Application_Start(object sender, EventArgs e)方法。这里创建的HttpApplication实例在处理完事件后,就被回收。
HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context) 该方法创建HttpApplication实例并进行初始化,调用System.Web.HttpApplication.InitInternal() 方法。创建HttpApplication实例是根据实际的_theApplicationType进行创建。如果Web目录中没有global.asa 文件,也就是说没有动态编译生成ASP.global_asax类型,那就直接实例化  HttpApplication。如果创建了ASP.global_asax类型,那就对ASP.global_asa进行实例化。

创建HttpApplication实例之后就是调用实例的InitInternal方法。
InitInternal方法的主要功能如下:
  1. InitModules():根据Web.Config的设置,创建相应的HttpModules。

  2. HookupEventHandlersForAppplicationAndModules:根据发生的事件,调用HttpApplication实例中相应的事件处理函数。

  3. 创建很多实现IExecutionStep接口的类的实例并添加到当前HttpApplication实例的_execSteps中,等待回调时执行。从 这里我们可以看到HttpApplication是以异步的方式处理请求, 对请求的很多处理工作都放入了_execStep等待回调时执行。
     _execStep中主要的处理工作如下:
    1) 对请求的路径进行安全检查,禁止非法路径访问(ValidatePathExecutionStep)。
    2) 如果设置了UrlMappings, 进行RewritePath(UrlMappingsExecutionStep)。
    3) 执行事件处理函数,比如:BeginRequest、AuthenticateRequest等等。
下 面就是获取处理当前请求的HttpHandler,ASP.NET页面的动态编译也是在这里进行的。至此HttpApplication流程将会转到 HttpHandler流程.也就是说HttpApplication 对象负责查找应该使用哪种处理程序来处理请求。HttpApplication 对象还负责检测对动态创建的、表示资源的程序集(如 .aspx 页面或 .asmx Web 服务)所进行的更改。如果检测到更改,应用程序对象将确保编译并加载所请求的资源的最新来源。HttpApplication调用 ProcessRequest方法来处理用户请求,此方法会调用对应的HttpHandler来处理用户请求,HttpHandler根据用户请求的文件 的扩展名处理请求,并把请求的结果,也就是HTML发送到客户浏览器.

HttpApplication是HttpRuntime所创建的吗? 并不是,HttpRuntime只是向HttpApplicationFactory提出请求,要求返回一个HttpApplication对象。 HttpApplicationFactory在接收到请求后,会先检查是否有已经存在并空闲的对象,如果有就取出一个HttpApplication对 象返回给HttpRuntime,如果没有的话,则要创建一个HttpApplication对象给HttpRunTime。
关于HttpApplication这个类的方法的实现,就不再一一解释,需要了解的,在类里面写上一个HttpApplication单词,然后右键选择“转到定义“,就可以看到里面的元数据了。
从上面看出global类与HttpApplication十分紧密,其事上,global类是继承与System.Web.HttpApplication类。

public class Global : System.Web.HttpApplication
    {
        
protected void Application_Start(object sender, EventArgs e)
        {
        }
        
//省略
    }

假设在global类中定义变量与对象,我们在全局中是否能够取得或设置他的值呢?看下面的例子:

Code

在页面中设置:
WebApp.Global.UserName = "AAAA";
在设置前与设置后输出,你可以看到值的变化,但对变量的设置,会出现什么问题?这些就需要你在开发中去考虑了。
如果使用过MVC框架开发的朋友就很熟其路由规则配置,其实就是设置在Global类的Application_Start事件中,如下面:

 public class MvcApplication : System.Web.HttpApplication
    {
        
public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute(
"{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                
"UpcomingDinners",
                
"Dinners/Page/{page}",
                
new { controller = "Dinners", action = "Index" }
            );
            routes.MapRoute(
                
"Default",                                              // Route name
                "{controller}/{action}/{id}",                           // URL with parameters
                new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
            );
        }
        
protected void Application_Start()
        {
            RegisterRoutes(RouteTable.Routes);
        }
    }

其实我们经常用的Application对象是HttpApplication类的一个属性,至此HttpApplication已经完成,下次转到HttpHandler流程了。
随便谈识文断字,查阅大量的资料,等于收集整理加上自己的见解吧,且当成长过程中的回忆。

[ASP.NET]HttpRuntime的认识与加深理解

mikel阅读(510)

下面最先介绍HttpRuntime的Web.config里的配置

<httpRuntime
   executionTimeout 
= "number" 
   maxRequestLength 
= "number" 
   requestLengthDiskThreshold 
= "number" 
   useFullyQualifiedRedirectUrl 
= "[True|False]" 
   minFreeThreads 
= "number" 
   minLocalRequestFreeThreads 
= "number" 
   appRequestQueueLimit 
= "number"
   enableKernelOutputCache 
= "[True|False]" 
   enableVersionHeader 
= "[True|False]" 
   apartmentThreading 
= "[True|False]"
   requireRootedSaveAsPath 
= "[True|False]"
   enable 
= "[True|False]" 
   sendCacheControlHeader 
= "[True|False]" 
   shutdownTimeout 
= "number"
   delayNotificationTimeout 
= "number"
   waitChangeNotification 
= "number" 
   maxWaitChangeNotification 
= "number" 
   enableHeaderChecking 
= "[True|False]" 
/>

通过上面的配置说明, 下面是在Web.Config里节点的设置

<configuration>
  
<system.web>
  
<httpRuntime maxRequestLength="4000"
    enable 
= "True"
    requestLengthDiskThreshold
="512
    useFullyQualifiedRedirectUrl="True"
    executionTimeout
="45"
    versionHeader
="1.1.4128"/>
  
</system.web>
</configuration>

IIS 所收到的对某 Microsoft ASP.NET 页面的每个请求都被移交给 ASP.NET HTTP 管线。HTTP 管线由一系列托管对象组成,这些对象按顺序处理该请求,并完成从 URL 到普通 HTML 文本的转换。HTTP 管线的入口点是 HttpRuntime 类。ASP.NET 基础结构为辅助进程中所承载的每个 AppDomain 创建此类的一个实例请注意,该辅助进程为当前正在运行的每个 ASP.NET 应用程序维护一个不同的 AppDomain。

要激活 HTTP 管道,可以创建一个 HttpRuntime 类的新实例,然后调用其 ProcessRequest 方法。一个完整的页面请求会包括下面的流程:
首先被WWW服务器截获(inetinfo.exe进程), 该进程首先判断页面后缀, 然后根据IIS中配置决定调用具体的扩展程序。aspx就会调用aspnet_isapi.dll,
然后由aspnet_isapi.dll发送给w3wp.exe(iis 工作者进程,IIS6.0中叫做 w3wq.exe,IIS5.0中叫做 aspnet_wp.exe)。

接下来在w3wp.exe调用.NET类库进行具体处理,顺序如下:ISAPIRuntim, HttpRuntime, HttpApplicationFactory, HttpApplication, HttpModule, HttpHandlerFactory, HttpHandler

ISAPIRuntime:主要作用是调用一些非托管代码生成HttpWorkerRequest对象,HttpWorkerRequest对象包含当前请求的所有信息,然后传递给HttpRuntime
HttpRuntime: 根据HttpWorkerRequest对象生成HttpContext,HttpContext包含request、response等属性, 再调用HttpApplicationFactory来生成IHttpHandler, 调用HttpApplication对象执行请求
HttpApplicationFactory: 生成一个HttpApplication对象
HttpApplication:进行HttpModule的初始化,HttpApplication创建针对此Http请求的 HttpContext对象
HttpModule: 当一个HTTP请求到达HttpModule时,整个ASP.NET Framework系统还并没有对这个HTTP请求做任何处理,也就是说此时对于HTTP请求来讲,HttpModule是一个HTTP请求的“必经之路 ”,所以可以在这个HTTP请求传递到真正的请求处理中心(HttpHandler)之前附加一些需要的信息在这个HTTP请求信息之上,或者针对截获的 这个HTTP请求信息作一些额外的工作,或者在某些情况下干脆终止满足一些条件的HTTP请求,从而可以起到一个Filter过滤器的作用。
HttpHandlerFactory:把用户request 转发到HttpHandlerFactory,再由HttpHandlerFactory实例化HttpHandler对象来相应request
HttpHandle:Http处理程序,处理页面请求

从上面看出HttpRuntime其中有一个ProcessRequest 方法
public static void ProcessRequest(HttpWorkerRequest wr);  //驱动所有 ASP.NET Web 处理执行。伪代码如下:

public static void HttpRuntime.ProcessRequest(HttpWorkerRequest wr)
 {
   
// 检查当前调用者有没有作为ASP.NET宿主(Host)的权限
   InternalSecurityPermissions.AspNetHostingPermissionLevelMedium.Demand(); 
   
if(wr == null)
   {
     
throw new ArgumentNullException("custom");
   }
   RequestQueue queue 
= HttpRuntime._theRuntime._requestQueue;
   
if(queue != null)
   {
     
// 将参数中的Web页面请求放入请求队列中, 并从队列中使用FIFO策略获取一个页面请求
     wr = queue.GetRequestToExecute(wr);
   }
   
if(wr != null)
   {     
     HttpRuntime.CalculateWaitTimeAndUpdatePerfCounter(wr); 
// 更新性能计数器     
      HttpRuntime.ProcessRequestNow(wr); // 实际完成页面请求工作
   }
 }

ProcessRequestNow函数则直接调用缺省HttpRuntime实例的ProcessRequestInternal函数完成实际页面请求工作,伪代码如下:

internal static void HttpRuntime.ProcessRequestNow(HttpWorkerRequest wr)
{
   HttpRuntime._theRuntime.ProcessRequestInternal(wr);
}

ProcessRequestInternal函数逻辑稍微复杂一些,大致可分为四个部分:
检查当前HttpRuntime实例是否第一次被调用,如果是第一次调用则通过FirstRequestInit函数初始化
调用HttpResponse.InitResponseWriter函数初始化页面请求的返回对象HttpWorkerRequest.Response
调用HttpApplicationFactory.GetApplicationInstance函数获取当前 Web 应用程序实例
使用Web应用程序实例完成实际的页面请求工作
伪代码如下:

Code

HttpRuntime.ProcessRequestInternal函数中调用了 HttpApplicationFactory.GetApplicationInstance函数获取当前 Web 应用程序实例。至少HttpRuntime已经完完成,将转进HttpApplicationFactory阶段流程。大家可以看到,围绕 HttpRuntime的函数都有一个HttpWorkerRequest参数,下面简单介绍一下这个参数的作用。在ISAPIRuntime阶段,调用 一些非托管代码生成HttpWorkerRequest对象,该对象包含当前请求的所有信息,然后传递给HttpRuntime,这里生成的 HttpWorkerRequest对象可以直接在我们的页面里调用.
IServiceProvider provider=(IServiceProvider)HttpContext.Current;
HttpWorkerRequest wr=(HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest));
然后可以通过wr来调用里面的方法。关于HttpWorkerRequest类里面包含的方法,从其元数据里面可以看到,如下:


public abstract class HttpWorkerRequest
    {
        
// 摘要:
        
//     指定 AcceptHTTP 标头的索引号。
        public const int HeaderAccept = 20;
        
//
        
// 摘要:
        
//     指定 Accept-CharsetHTTP 标头的索引号。
        public const int HeaderAcceptCharset = 21;
        
//
        
// 摘要:
        
//     指定 Accept-EncodingHTTP 标头的索引号。
        public const int HeaderAcceptEncoding = 22;
        
//
        
// 摘要:
        
//     指定 Accept-LanguageHTTP 标头的索引号。
        public const int HeaderAcceptLanguage = 23;
        
//
        
// 摘要:
        
//     指定 Accept-RangesHTTP 标头的索引号。
        public const int HeaderAcceptRanges = 20;
        
//
        
// 摘要:
        
//     指定 AgeHTTP 标头的索引号。
        public const int HeaderAge = 21;
        
//
        
// 摘要:
        
//     指定 AllowHTTP 标头的索引号。
        public const int HeaderAllow = 10;
        
//
        
// 摘要:
        
//     指定 AuthorizationHTTP 标头的索引号。
        public const int HeaderAuthorization = 24;
        
//
        
// 摘要:
        
//     表示 HTTPCache-ControlHTTP 标头的索引。
        public const int HeaderCacheControl = 0;
        
//
        
// 摘要:
        
//     指定 ConnectionHTTP 标头的索引号。
        public const int HeaderConnection = 1;
        
//
        
// 摘要:
        
//     指定 Content-EncodingHTTP 标头的索引号。
        public const int HeaderContentEncoding = 13;
        
//
        
// 摘要:
        
//     指定 Content-LanguageHTTP 标头的索引号。
        public const int HeaderContentLanguage = 14;
        
//
        
// 摘要:
        
//     指定 Content-LengthHTTP 标头的索引号。
        public const int HeaderContentLength = 11;
        
//
        
// 摘要:
        
//     指定 Content-LocationHTTP 标头的索引号。
        public const int HeaderContentLocation = 15;
        
//
        
// 摘要:
        
//     指定 Content-MD5HTTP 标头的索引号。
        public const int HeaderContentMd5 = 16;
        
//
        
// 摘要:
        
//     指定 Content-RangeHTTP 标头的索引号。
        public const int HeaderContentRange = 17;
        
//
        
// 摘要:
        
//     指定 Content-TypeHTTP 标头的索引号。
        public const int HeaderContentType = 12;
        
//
        
// 摘要:
        
//     指定 CookieHTTP 标头的索引号。
        public const int HeaderCookie = 25;
        
//
        
// 摘要:
        
//     指定 DateHTTP 标头的索引号。
        public const int HeaderDate = 2;
        
//
        
// 摘要:
        
//     指定 ETagHTTP 标头的索引号。
        public const int HeaderEtag = 22;
        
//
        
// 摘要:
        
//     指定 ExceptHTTP 标头的索引号。
        public const int HeaderExpect = 26;
        
//
        
// 摘要:
        
//     指定 ExpiresHTTP 标头的索引号。
        public const int HeaderExpires = 18;
        
//
        
// 摘要:
        
//     指定 FromHTTP 标头的索引号。
        public const int HeaderFrom = 27;
        
//
        
// 摘要:
        
//     指定 HostHTTP 标头的索引号。
        public const int HeaderHost = 28;
        
//
        
// 摘要:
        
//     指定 If-MatchHTTP 标头的索引号。
        public const int HeaderIfMatch = 29;
        
//
        
// 摘要:
        
//     指定 If-Modified-SinceHTTP 标头的索引号。
        public const int HeaderIfModifiedSince = 30;
        
//
        
// 摘要:
        
//     指定 If-None-MatchHTTP 标头的索引号。
        public const int HeaderIfNoneMatch = 31;
        
//
        
// 摘要:
        
//     指定 If-RangeHTTP 标头的索引号。
        public const int HeaderIfRange = 32;
        
//
        
// 摘要:
        
//     指定 If-Unmodified-SinceHTTP 标头的索引号。
        public const int HeaderIfUnmodifiedSince = 33;
        
//
        
// 摘要:
        
//     指定 Keep-AliveHTTP 标头的索引号。
        public const int HeaderKeepAlive = 3;
        
//
        
// 摘要:
        
//     指定 Last-ModifiedHTTP 标头的索引号。
        public const int HeaderLastModified = 19;
        
//
        
// 摘要:
        
//     指定 LocationHTTP 标头的索引号。
        public const int HeaderLocation = 23;
        
//
        
// 摘要:
        
//     指定 Max-ForwardsHTTP 标头的索引号。
        public const int HeaderMaxForwards = 34;
        
//
        
// 摘要:
        
//     指定 PragmaHTTP 标头的索引号。
        public const int HeaderPragma = 4;
        
//
        
// 摘要:
        
//     指定 Proxy-AuthenticateHTTP 标头的索引号。
        public const int HeaderProxyAuthenticate = 24;
        
//
        
// 摘要:
        
//     指定 Proxy-AuthorizationHTTP 标头的索引号。
        public const int HeaderProxyAuthorization = 35;
        
//
        
// 摘要:
        
//     指定 RangeHTTP 标头的索引号。
        public const int HeaderRange = 37;
        
//
        
// 摘要:
        
//     指定 RefererHTTP 标头的索引号。
        public const int HeaderReferer = 36;
        
//
        
// 摘要:
        
//     指定 Retry-AfterHTTP 标头的索引号。
        public const int HeaderRetryAfter = 25;
        
//
        
// 摘要:
        
//     指定 ServerHTTP 标头的索引号。
        public const int HeaderServer = 26;
        
//
        
// 摘要:
        
//     指定 Set-CookieHTTP 标头的索引号。
        public const int HeaderSetCookie = 27;
        
//
        
// 摘要:
        
//     指定 TEHTTP 标头的索引号。
        public const int HeaderTe = 38;
        
//
        
// 摘要:
        
//     指定 TrailerHTTP 标头的索引号。
        public const int HeaderTrailer = 5;
        
//
        
// 摘要:
        
//     指定 Transfer-EncodingHTTP 标头的索引号。
        public const int HeaderTransferEncoding = 6;
        
//
        
// 摘要:
        
//     指定 UpgradeHTTP 标头的索引号。
        public const int HeaderUpgrade = 7;
        
//
        
// 摘要:
        
//     指定 User-AgentHTTP 标头的索引号。
        public const int HeaderUserAgent = 39;
        
//
        
// 摘要:
        
//     指定 VaryHTTP 标头的索引号。
        public const int HeaderVary = 28;
        
//
        
// 摘要:
        
//     指定 ViaHTTP 标头的索引号。
        public const int HeaderVia = 8;
        
//
        
// 摘要:
        
//     指定 WarningHTTP 标头的索引号。
        public const int HeaderWarning = 9;
        
//
        
// 摘要:
        
//     指定 WWW-AuthenticateHTTP 标头的索引号。
        public const int HeaderWwwAuthenticate = 29;
        
//
        
// 摘要:
        
//     指定请求的原因。
        public const int ReasonCachePolicy = 2;
        
//
        
// 摘要:
        
//     指定请求的原因。
        public const int ReasonCacheSecurity = 3;
        
//
        
// 摘要:
        
//     指定请求的原因。
        public const int ReasonClientDisconnect = 4;
        
//
        
// 摘要:
        
//     指定请求的原因。默认值为 System.Web.HttpWorkerRequest.ReasonResponseCacheMiss。
        public const int ReasonDefault = 0;
        
//
        
// 摘要:
        
//     指定请求的原因。
        public const int ReasonFileHandleCacheMiss = 1;
        
//
        
// 摘要:
        
//     指定请求的原因。
        public const int ReasonResponseCacheMiss = 0;
        
//
        
// 摘要:
        
//     指定 MaximumHTTP 请求标头的索引号。
        public const int RequestHeaderMaximum = 40;
        
//
        
// 摘要:
        
//     指定 MaximumHTTP 响应标头的索引号。
        public const int ResponseHeaderMaximum = 30;
        
// 摘要:
        
//     初始化 System.Web.HttpWorkerRequest 类的新实例。
        protected HttpWorkerRequest();
        
// 摘要:
        
//     获取 Machine.config 文件的完整物理路径。
        
//
        
// 返回结果:
        
//     Machine.config 文件的物理路径。
        public virtual string MachineConfigPath { get; }
        
//
        
// 摘要:
        
//     获取 ASP.NET 二进制文件的安装目录的物理路径。
        
//
        
// 返回结果:
        
//     ASP.NET 二进制文件的物理目录。
        public virtual string MachineInstallDirectory { get; }
        
//
        
// 摘要:
        
//     获取当前请求的 Windows 跟踪 ID 的对应事件跟踪。
        
//
        
// 返回结果:
        
//     当前 ASP.NET 请求的跟踪 ID。
        public virtual Guid RequestTraceIdentifier { get; }
        
//
        
// 摘要:
        
//     获取根 Web.config 文件的完整物理路径。
        
//
        
// 返回结果:
        
//     根 Web.config 文件的物理路径。
        public virtual string RootWebConfigPath { get; }
        
// 摘要:
        
//     终止与客户端的连接。
        public virtual void CloseConnection();
        
//
        
// 摘要:
        
//     由运行库使用以通知 System.Web.HttpWorkerRequest 当前请求的请求处理已完成。
        public abstract void EndOfRequest();
        
//
        
// 摘要:
        
//     将所有挂起的响应数据发送到客户端。
        
//
        
// 参数:
        
//   finalFlush:
        
//     如果这将是最后一次刷新响应数据,则为 true;否则为 false。
        public abstract void FlushResponse(bool finalFlush);
        
//
        
// 摘要:
        
//     返回当前正在执行的服务器应用程序的虚拟路径。
        
//
        
// 返回结果:
        
//     当前应用程序的虚拟路径。
        public virtual string GetAppPath();
        
//
        
// 摘要:
        
//     返回当前正在执行的服务器应用程序的物理路径。
        
//
        
// 返回结果:
        
//     当前应用程序的物理路径。
        public virtual string GetAppPathTranslated();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回当前 URL 的应用程序池 ID。
        
//
        
// 返回结果:
        
//     始终返回 null。
        public virtual string GetAppPoolID();
        
//
        
// 摘要:
        
//     获取从客户端读入的字节数。
        
//
        
// 返回结果:
        
//     包含读取的字节数的 Long。
        public virtual long GetBytesRead();
        
//
        
// 摘要:
        
//     在派生类中被重写时,从客户端发出的请求获取证书字段(以 X.509 标准指定)。
        
//
        
// 返回结果:
        
//     包含整个证书内容流的字节数组。
        public virtual byte[] GetClientCertificate();
        
//
        
// 摘要:
        
//     获取证书颁发者(以二进制格式表示)。
        
//
        
// 返回结果:
        
//     包含以二进制格式表示的证书颁发者的字节数组。
        public virtual byte[] GetClientCertificateBinaryIssuer();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回用于编码客户端证书的 System.Text.Encoding 对象。
        
//
        
// 返回结果:
        
//     表示为整数的证书编码。
        public virtual int GetClientCertificateEncoding();
        
//
        
// 摘要:
        
//     在派生类中被重写时,获取与客户端证书关联的 PublicKey 对象。
        
//
        
// 返回结果:
        
//     一个 PublicKey 对象。
        public virtual byte[] GetClientCertificatePublicKey();
        
//
        
// 摘要:
        
//     在派生类中被重写时,则获取证书开始生效的日期。此日期随区域设置的不同而不同。
        
//
        
// 返回结果:
        
//     表示证书生效时间的 System.DateTime 对象。
        public virtual DateTime GetClientCertificateValidFrom();
        
//
        
// 摘要:
        
//     获取证书到期日期。
        
//
        
// 返回结果:
        
//     表示证书失效日期的 System.DateTime 对象。
        public virtual DateTime GetClientCertificateValidUntil();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回当前连接的 ID。
        
//
        
// 返回结果:
        
//     始终返回 0。
        public virtual long GetConnectionID();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回所请求的 URI 的虚拟路径。
        
//
        
// 返回结果:
        
//     请求的 URI 的路径。
        public virtual string GetFilePath();
        
//
        
// 摘要:
        
//     返回请求的 URI 的物理文件路径(并将其从虚拟路径转换成物理路径:例如,从“/proj1/page.aspx”转换成“c:\dir\page.aspx”)
        
//
        
// 返回结果:
        
//     请求的 URI 的已转换的物理文件路径。
        public virtual string GetFilePathTranslated();
        
//
        
// 摘要:
        
//     返回请求标头的指定成员。
        
//
        
// 返回结果:
        
//     请求标头中返回的 HTTP 谓词。
        public abstract string GetHttpVerbName();
        
//
        
// 摘要:
        
//     提供对请求的 HTTP 版本(如“HTTP/1.1”)的访问。
        
//
        
// 返回结果:
        
//     请求标头中返回的 HTTP 版本。
        public abstract string GetHttpVersion();
        
//
        
// 摘要:
        
//     返回与指定的索引相对应的标准 HTTP 请求标头。
        
//
        
// 参数:
        
//   index:
        
//     标头的索引。例如,System.Web.HttpWorkerRequest.HeaderAllow 字段。
        
//
        
// 返回结果:
        
//     HTTP 请求标头。
        public virtual string GetKnownRequestHeader(int index);
        
//
        
// 摘要:
        
//     返回指定的 HTTP 请求标头的索引号。
        
//
        
// 参数:
        
//   header:
        
//     标头的名称。
        
//
        
// 返回结果:
        
//     在 header 参数中指定的 HTTP 请求标头的索引号。
        public static int GetKnownRequestHeaderIndex(string header);
        
//
        
// 摘要:
        
//     返回指定的 HTTP 请求标头的名称。
        
//
        
// 参数:
        
//   index:
        
//     标头的索引号。
        
//
        
// 返回结果:
        
//     在 index 参数中指定的 HTTP 请求标头的名称。
        public static string GetKnownRequestHeaderName(int index);
        
//
        
// 摘要:
        
//     返回指定的 HTTP 响应标头的索引号。
        
//
        
// 参数:
        
//   header:
        
//     HTTP 标头的名称。
        
//
        
// 返回结果:
        
//     在 header 参数中指定的 HTTP 响应标头的索引号。
        public static int GetKnownResponseHeaderIndex(string header);
        
//
        
// 摘要:
        
//     返回指定的 HTTP 响应标头的名称。
        
//
        
// 参数:
        
//   index:
        
//     标头的索引号。
        
//
        
// 返回结果:
        
//     在 index 参数中指定的 HTTP 响应标头的名称。
        public static string GetKnownResponseHeaderName(int index);
        
//
        
// 摘要:
        
//     提供对请求标头的指定成员的访问。
        
//
        
// 返回结果:
        
//     请求标头中返回的服务器 IP 地址。
        public abstract string GetLocalAddress();
        
//
        
// 摘要:
        
//     提供对请求标头的指定成员的访问。
        
//
        
// 返回结果:
        
//     请求标头中返回的服务器端口号。
        public abstract int GetLocalPort();
        
//
        
// 摘要:
        
//     返回具有 URL 扩展的资源的其他路径信息。即对于路径 /virdir/page.html/tail,GetPathInfo 值为 /tail。
        
//
        
// 返回结果:
        
//     资源的附加路径信息。
        public virtual string GetPathInfo();
        
//
        
// 摘要:
        
//     返回 HTTP 请求正文已被读取的部分。
        
//
        
// 返回结果:
        
//     HTTP 请求正文已被读取的部分。
        public virtual byte[] GetPreloadedEntityBody();
        
//
        
// 摘要:
        
//     使用指定的缓冲区数据和字节偏移量获取 HTTP 请求正文当前已被读取的部分。
        
//
        
// 参数:
        
//   buffer:
        
//     要读取的数据。
        
//
        
//   offset:
        
//     开始读取的位置的字节偏移量。
        
//
        
// 返回结果:
        
//     HTTP 请求正文已被读取的部分。
        public virtual int GetPreloadedEntityBody(byte[] buffer, int offset);
        
//
        
// 摘要:
        
//     获取 HTTP 请求正文当前已被读取部分的长度。
        
//
        
// 返回结果:
        
//     一个整数,包含当前已读取的 HTTP 请求正文的长度。
        public virtual int GetPreloadedEntityBodyLength();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回 HTTP 协议(HTTP 或 HTTPS)。
        
//
        
// 返回结果:
        
//     如果 System.Web.HttpWorkerRequest.IsSecure() 方法为 true,则为 HTTPS;否则,为 HTTP。
        public virtual string GetProtocol();
        
//
        
// 摘要:
        
//     返回请求 URL 中指定的查询字符串。
        
//
        
// 返回结果:
        
//     请求查询字符串。
        public abstract string GetQueryString();
        
//
        
// 摘要:
        
//     在派生类中被重写时,以字节数组的形式返回响应查询字符串。
        
//
        
// 返回结果:
        
//     包含响应的字节数组。
        public virtual byte[] GetQueryStringRawBytes();
        
//
        
// 摘要:
        
//     返回附加了查询字符串的请求标头中包含的 URL 路径。
        
//
        
// 返回结果:
        
//     请求标头的原始 URL 路径。
        public abstract string GetRawUrl();
        
//
        
// 摘要:
        
//     提供对请求标头的指定成员的访问。
        
//
        
// 返回结果:
        
//     客户端的 IP 地址。
        public abstract string GetRemoteAddress();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回客户端计算机的名称。
        
//
        
// 返回结果:
        
//     客户端计算机的名称。
        public virtual string GetRemoteName();
        
//
        
// 摘要:
        
//     提供对请求标头的指定成员的访问。
        
//
        
// 返回结果:
        
//     客户端的 HTTP 端口号。
        public abstract int GetRemotePort();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回请求的原因。
        
//
        
// 返回结果:
        
//     原因代码。默认值为 ReasonResponseCacheMiss。
        public virtual int GetRequestReason();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回本地服务器的名称。
        
//
        
// 返回结果:
        
//     本地服务器的名称。
        public virtual string GetServerName();
        
//
        
// 摘要:
        
//     从与请求关联的服务器变量词典返回单个服务器变量。
        
//
        
// 参数:
        
//   name:
        
//     请求的服务器变量的名称。
        
//
        
// 返回结果:
        
//     请求的服务器变量。
        public virtual string GetServerVariable(string name);
        
//
        
// 摘要:
        
//     返回一个字符串,该字符串描述指定的 HTTP 状态代码的名称。
        
//
        
// 参数:
        
//   code:
        
//     HTTP 状态代码。
        
//
        
// 返回结果:
        
//     状态说明。例如,System.Web.HttpWorkerRequest.GetStatusDescription(System.Int32) (404)
        
//     返回“未找到”。
        public static string GetStatusDescription(int code);
        
//
        
// 摘要:
        
//     获取整个 HTTP 请求正文的长度。
        
//
        
// 返回结果:
        
//     包含整个 HTTP 请求正文的长度的整数。
        public virtual int GetTotalEntityBodyLength();
        
//
        
// 摘要:
        
//     返回非标准的 HTTP 请求标头值。
        
//
        
// 参数:
        
//   name:
        
//     标头名称。
        
//
        
// 返回结果:
        
//     标头值。
        public virtual string GetUnknownRequestHeader(string name);
        
//
        
// 摘要:
        
//     获取所有非标准的 HTTP 标头的名称/值对。
        
//
        
// 返回结果:
        
//     标头的名称/值对的数组。
        [CLSCompliant(false)]
        
public virtual string[][] GetUnknownRequestHeaders();
        
//
        
// 摘要:
        
//     返回请求的 URI 的虚拟路径。
        
//
        
// 返回结果:
        
//     请求的 URI 的路径。
        public abstract string GetUriPath();
        
//
        
// 摘要:
        
//     当在派生类中被重写时,返回当前连接的上下文 ID。
        
//
        
// 返回结果:
        
//     始终返回 0。
        public virtual long GetUrlContextID();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回客户端的模拟标记。
        
//
        
// 返回结果:
        
//     表示客户端的模拟标记的值。默认值为 0。
        public virtual IntPtr GetUserToken();
        
//
        
// 摘要:
        
//     获取请求虚拟路径的模拟标记。
        
//
        
// 返回结果:
        
//     请求虚拟路径的标记的非托管内存指针。
        public virtual IntPtr GetVirtualPathToken();
        
//
        
// 摘要:
        
//     返回一个值,该值指示请求是否包含正文数据。
        
//
        
// 返回结果:
        
//     如果请求包含正文数据,则为 true;否则,为 false。
        public bool HasEntityBody();
        
//
        
// 摘要:
        
//     返回一个值,该值指示是否已为当前的请求将 HTTP 响应标头发送到客户端。
        
//
        
// 返回结果:
        
//     如果 HTTP 响应标头已发送到客户端,则为 true;否则,为 false。
        public virtual bool HeadersSent();
        
//
        
// 摘要:
        
//     返回一个值,该值指示客户端连接是否仍处于活动状态。
        
//
        
// 返回结果:
        
//     如果客户端连接仍处于活动状态,则为 true;否则,为 false。
        public virtual bool IsClientConnected();
        
//
        
// 摘要:
        
//     返回一个值,该值指示是否所有请求数据都可用,以及是否不需要对客户端进行进一步读取。
        
//
        
// 返回结果:
        
//     如果所有请求数据都可用,则为 true;否则,为 false。
        public virtual bool IsEntireEntityBodyIsPreloaded();
        
//
        
// 摘要:
        
//     返回一个指示连接是否使用 SSL 的值。
        
//
        
// 返回结果:
        
//     如果连接是 SSL 连接,则为 true;否则为 false。默认值为 false。
        public virtual bool IsSecure();
        
//
        
// 摘要:
        
//     返回与指定虚拟路径相对应的物理路径。
        
//
        
// 参数:
        
//   virtualPath:
        
//     虚拟路径。
        
//
        
// 返回结果:
        
//     与 virtualPath 参数中指定的虚拟路径相对应的物理路径。
        public virtual string MapPath(string virtualPath);
        
//
        
// 摘要:
        
//     读取客户端的请求数据(在尚未预加载时)。
        
//
        
// 参数:
        
//   buffer:
        
//     将数据读入的字节数组。
        
//
        
//   size:
        
//     最多读取的字节数。
        
//
        
// 返回结果:
        
//     读取的字节数。
        public virtual int ReadEntityBody(byte[] buffer, int size);
        
//
        
// 摘要:
        
//     使用指定的要从中读取数据的缓冲区、字节偏移量和最大字节数从客户端读取请求数据(当未预先加载时)。
        
//
        
// 参数:
        
//   buffer:
        
//     将数据读入的字节数组。
        
//
        
//   offset:
        
//     开始读取的位置的字节偏移量。
        
//
        
//   size:
        
//     最多读取的字节数。
        
//
        
// 返回结果:
        
//     读取的字节数。
        public virtual int ReadEntityBody(byte[] buffer, int offset, int size);
        
//
        
// 摘要:
        
//     将 Content-Length HTTP 标头添加到小于或等于 2 GB 的消息正文的响应。
        
//
        
// 参数:
        
//   contentLength:
        
//     响应的长度(以字节为单位)。
        public virtual void SendCalculatedContentLength(int contentLength);
        
//
        
// 摘要:
        
//     将 Content-Length HTTP 标头添加到大于 2 GB 的消息正文的响应。
        
//
        
// 参数:
        
//   contentLength:
        
//     响应的长度(以字节为单位)。
        public virtual void SendCalculatedContentLength(long contentLength);
        
//
        
// 摘要:
        
//     将标准 HTTP 标头添加到响应。
        
//
        
// 参数:
        
//   index:
        
//     标头索引。例如 System.Web.HttpWorkerRequest.HeaderContentLength。
        
//
        
//   value:
        
//     标头的值。
        public abstract void SendKnownResponseHeader(int index, string value);
        
//
        
// 摘要:
        
//     将指定文件的内容添加到响应并指定文件中的起始位置和要发送的字节数。
        
//
        
// 参数:
        
//   handle:
        
//     要发送的文件的句柄。
        
//
        
//   offset:
        
//     文件中的起始位置。
        
//
        
//   length:
        
//     要发送的字节数。
        public abstract void SendResponseFromFile(IntPtr handle, long offset, long length);
        
//
        
// 摘要:
        
//     将指定文件的内容添加到响应并指定文件中的起始位置和要发送的字节数。
        
//
        
// 参数:
        
//   filename:
        
//     要发送的文件的名称。
        
//
        
//   offset:
        
//     文件中的起始位置。
        
//
        
//   length:
        
//     要发送的字节数。
        public abstract void SendResponseFromFile(string filename, long offset, long length);
        
//
        
// 摘要:
        
//     将字节数组中指定数目的字节添加到响应。
        
//
        
// 参数:
        
//   data:
        
//     要发送的字节数组。
        
//
        
//   length:
        
//     要发送的字节数(从第一个字节开始)。
        public abstract void SendResponseFromMemory(byte[] data, int length);
        
//
        
// 摘要:
        
//     将内存块中指定数目的字节添加到响应。
        
//
        
// 参数:
        
//   data:
        
//     指向内存块的非托管指针。
        
//
        
//   length:
        
//     要发送的字节数。
        public virtual void SendResponseFromMemory(IntPtr data, int length);
        
//
        
// 摘要:
        
//     指定响应的 HTTP 状态代码和状态说明,例如 SendStatus(200, "Ok")。
        
//
        
// 参数:
        
//   statusCode:
        
//     要发送的状态代码
        
//
        
//   statusDescription:
        
//     要发送的状态说明。
        public abstract void SendStatus(int statusCode, string statusDescription);
        
//
        
// 摘要:
        
//     将非标准 HTTP 标头添加到响应。
        
//
        
// 参数:
        
//   name:
        
//     要发送的标头的名称。
        
//
        
//   value:
        
//     标头的值。
        public abstract void SendUnknownResponseHeader(string name, string value);
        
//
        
// 摘要:
        
//     在发送所有响应数据后注册可选通知。
        
//
        
// 参数:
        
//   callback:
        
//     在发送所有数据(带外)后调用的通知回调。
        
//
        
//   extraData:
        
//     回调的附加参数。
        public virtual void SetEndOfSendNotification(HttpWorkerRequest.EndOfSendNotification callback, object extraData);
        
// 摘要:
        
//     表示用于在完成发送响应后通知调用方的方法。
        
//
        
// 参数:
        
//   wr:
        
//     当前的 System.Web.HttpWorkerRequest。
        
//
        
//   extraData:
        
//     处理请求所需的任何其他数据。
        public delegate void EndOfSendNotification(HttpWorkerRequest wr, object extraData);
    }

 

[ASP.NET]ASP.NET底层的初步认识与理解

mikel阅读(707)

最近在国外的网站乱走一通,发现一些比较好的文章,收集整理加于自己的理解,作为笔记形式记录下来,让以后自己有个回忆。

ASP.NET是一个非常强大的构建Web应用的平台,它提供了极大的灵活性和能力以致于可以用它来构建所有类型的Web应用.绝大多数的人只熟悉 高层的框架如WebForms和WebServices-这些都在ASP.NET层次结构在最高层.在这篇文章中我将会讨论ASP.NET的底层机制并解 释请求(request)是怎么从Web服务器传送到ASP.NET运行时然后如何通过ASP.NET管道来处理请求.

ASP.NET是一个请求处理引擎.它接收一个发送过来的请求,把它传给内部的管道直到终点,作为一个开发人员的你可以在这里附加一些代码来处理请 求.这个引擎是和HTTP/Web服务器完全分隔的.事实上,HTTP运行时是一个组件,使你可以摆脱IIS或者任何其他的服务器程序,将你自己的程序寄 宿在内.

运行时提供了一个复杂但同时非常优雅的在管道中路由请求的机制.其中有很多相关的对象,大多数都是可扩展的(通过继承或者事件接口),在几乎所有的 处理流程上都是如此.所以这个框架具有高度可扩展性.通过这个机制,挂接到非常底层的接口(比如缓存,认证和授权)都变得可能了.你甚至可以在预处理或者 处理后过滤内容,也可以简单的将符合特殊标记的请求直接路由你的代码或者另一个URL上.存在着许多不同的方法来完成同一件事,但是所有这些方法都是可以 简单直接地实现的,同时还提供了灵活性,可以得到最好的性能和开发的简单性.

整个ASP.NET引擎是完全建立在托管代码上的,所有的扩展功能也是通过托管代码扩展来提供的.这是对.NET框架具有构建复杂而且高效的框架的 能力的最好的证明.ASP.NET最令人印象深刻的地方是深思熟虑的设计,使得框架非常的容易使用,又能提供挂接到请求处理的几乎所有部分的能力.

ASP.NET在微软的平台上就是通过ISAPI扩展来和IIS进行交互的,这个扩展寄宿着.NET运行时和ASP.NET运行时.ISAPI提供 了核心的接口,ASP.NET使用非托管的ISAPI代码通过这个接口来从Web服务器获取请求,并发送响应回客户端.ISAPI提供的内容可以通过通用 对象(例如HttpRequest和HttpResponse)来获取,这些对象通过一个定义良好并有很好访问性的接口来暴露非托管数据.

当用户发送一个URL请求时,在Web服务器端,IIS5或6,获得这个请求.在最底层,ASP.NET和IIS通过ISAPI扩展进行交互.在 ASP.NET环境中这个请求通常被路由到一个扩展名为.aspx的页面上,但是这个流程是怎么工作的完全依赖于处理特定扩展名的HTTP Handler是怎么实现的.在IIS中.aspx通过’应用程序扩展’(又称为脚本映射)被映射到ASP.NET的ISAPI扩展DLL- aspnet_isapi.dll.每一个请求都需要通过一个被注册到aspnet_isapi.dll的扩展名来触发ASP.NET(来处理这个请 求).

ISAPI是底层的非托管Win32 API.ISAPI定义的接口非常简单并且是为性能做了优化的.它们是非常底层的-处理指针和函数指针表来进行回调-但是它们提供了最底层和面向效率的接 口,使开发者和工具提供商可以用它来挂接到IIS上.因为ISAPI非常底层所以它并不适合来开发应用级的代码,而且ISAPI倾向于主要被用于桥接接 口,向上层工具提供应用服务器类型的功能.

下面来介绍HttpRuntime,HttpContext,HttpApplication

当一个请求到来时,它被路由到ISAPIRuntime.ProcessRequest()方法.这个方法调用HttpRuntime.ProcessRequest方法,它作一些重要的事情

为请求创建一个新的HttpContext实例
获取一个HttpApplication实例
调用HttpApplication.Init()方法来设置管道的事件
Init()方法触发开始ASP.NET管道处理的HttpApplication.ResumeProcessing()方法

首先一个新的HttpContext对象被创建并用来传递ISAPIWorkerRequest,这个上下文在整个请求的生命周期总都是可用的并总可以通过静态属性.
HttpContext.Currect 来访问.正像名字所暗示的那样,HttpContext对象代表了当前活动请求的上下文因为他包含了在请求生命周期中所有典型的你需要访问的重要对 象:Request,Response,Application,Server,Cache.在请求处理的任何时候 HttpContext.Current给你访问所有这些的能力.

HttpContext对象也包含一个非常有用的Items集合,你可以用它来保存针对特定请求的数据.上下文对象在请求周期的开始时被创建,在请 求结束时被释放,所有在Items集合中保存的数据只在这个特定的请求中可用.一个很好的使用的例子是请求日志机制,当你通过想通过在 Global.asax中挂接Application_BeginRequest和Application_EndRequest方法记录请求的开始和结 束时间(象在列表3中显示的那样).HttpContext对你就非常有用了-如果你在请求或页面处理的不同部分需要数据,你自由的使用它.

protected void Application_BeginRequest(Object sender, EventArgs e)
{
 if (App.Configuration.LogWebRequests)
 {
  Context.Items.Add("WebLog_StartTime",DateTime.Now);
 }
}

protected void Application_EndRequest(Object sender, EventArgs e)
{
 if (App.Configuration.LogWebRequests)
 {
  try
  { 
   TimeSpan Span = DateTime.Now.Subtract((DateTime) Context.Items["WebLog_StartTime"] );
   int MiliSecs = Span.TotalMilliseconds;
   WebRequestLog.Log(App.Configuration.ConnectionString, true, MilliSecs);
  } 
 }
}

HttpApplication

每个请求都被路由到一个HttpApplication对象上.HttpApplicationFactory类根据应用程序的负载为你的 ASP.NET应用创建一个HttpApplication对象池并为每个请求分发HttpApplication对象的引用.对象池的大小受 machine.config文件中ProcessModel键中的MaxWorkerThreads设置限制.
HttpApplication 是你的Web程序的外部包装器,而且它被映射到在Global.asax里面定义的类上.它是进入HttpRuntime的第一个入口点.如果你查看 Global.asax(或者对应的代码类)你会发现这个类直接继承自HttpApplication:
HttpApplication的主要职责是作为Http管道的事件控制器,所以它的接口主要包含的是事件.事件挂接是非常广泛的,大概包括以下这些:
BeginRequest
AuthenticateRequest
AuthorizeRequest
ResolveRequestCache
AquireRequestState
PreRequestHandlerExecute
PostRequestHandlerExecute
ReleaseRequestState
UpdateRequestCache
EndRequest

HttpModule和HttpHandler两者都是在HttpApplication.Init()函数调用的一部分中被载入并附加到调用链上

httpApplication它本身对发送给应用程序的数据一无所知-它只是一个通过事件来通讯的消息对象.它触发事件并通过 HttpContext对象来向被调用函数传递消息.实际的当前请求的状态数据由前面提到的HttpContext对象维护.它提供了所有请求专有的数据 并从进入管道开始到结束一直跟随请求

一旦管道被启动,HttpApplication开始象图六那样一个个的触发事件.每个事件处理器被触发,如果事件被挂接,这些处理器将执行它们自 己的任务.这个处理的主要任务是最终调用挂接到此特定请求的HttpHandler.处理器(handler)是ASP.NET请求的核心处理机制,通常 也是所有应用程序级别的代码被执行的地方.记住ASP.NET页面和Web服务框架都是作为HttpHandler实现,这里也是处理请求的的核心之处. 模块(module)趋向于成为一个传递给处理器(handler)的上下文的预处理或后处理器.ASP.NET中典型的默认处理器包括预处理的认证,缓 存以及后处理中各种不同的编码机制.

虽然HttpModule看上去很像ISAPI过滤器,它们都检查每个通过ASP.NET应用的请求,但是它们只检查映射到单个特定的 ASP.NET应用或虚拟目录的请求,也就是只能检查映射到ASP.NET的请求.这样你可以检查所有ASPX页面或者其他任何映射到ASP.NET的扩 展名.

实现一个HTTP模块是非常简单的:你必须实现之包含两个函数(Init()和Dispose())的IHttpModule接口.传进来的事件参 数中包含指向HTTPApplication对象的引用,这给了你访问HttpContext对象的能力.在这些方法上你可以挂接到 HttpApplication事件上.例如,如果你想挂接AuthenticateRequest事件到一个模块上

 

总的来说w3wp.exe调用.NET类库进行具体处理,顺序如下:ISAPIRuntim, HttpRuntime, HttpApplicationFactory, HttpApplication, HttpModule, HttpHandlerFactory, HttpHandler

有时间再对每个对象正进深入理解.

[C#]C# 4.0 新对象ExpandoObject

mikel阅读(676)

      今天无意中看了4.0的一些新特性,其中看到SystemDynamic 命名空间下的ExpandoObject 类很感兴趣,看了篇英文文章给大伙分享下。

先来看下该类的成员:

  http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject_members(VS.100).aspx

ExpandoObject instances can add and remove members at run time.什么意思呢?这意味着此类的实例能够在运行时动态的增加和删除成员。

其中有个新概念:dynamic language runtime (DLR)(动态语言运行时),我才疏学浅,还希望各位专家们多去研究下。

说说ExpandoObject这个动态特性的意义吧。

我们用XML来做下对比:

首先我们创建一个XML对象,

 


XElement contactXML =
    
new XElement("Contact",
        
new XElement("Name""Patrick Hines"),
        
new XElement("Phone""206-555-0144"),
        
new XElement("Address",
            
new XElement("Street1""123 Main St"),
            
new XElement("City""Mercer Island"),
            
new XElement("State""WA"),
            
new XElement("Postal""68042")
        )
    );

再来看看Dynamic对象,

 


dynamic contact = new ExpandoObject();
contact.Name 
= "Patrick Hines";
contact.Phone 
= "206-555-0144";
contact.Address 
= new ExpandoObject();
contact.Address.Street 
= "123 Main St";
contact.Address.City 
= "Mercer Island";
contact.Address.State 
= "WA";
contact.Address.Postal 
= "68402";

 

首先,我们看下dynamic对象的声明:dynamic contact = new ExpandoObject();

 

我没有写成 ExpandoObject contact = new ExpandoObject(), 因为我用静态的ExpandoObject 类型来声明则此对象没有在运行时增加成员的特性,所以我使用新的关键字dynamic.

其次,大家能注意到,我创建一个子节点只需要创建一个ExpandoObject实例作为contact对象的成员。

这样你可以很简单的看清父子节点之间的关系,更重要的是你可以很简单的访问每一个元素。

用linq to XML:

Console.WriteLine((string)contactXML.Element("Address").Element("State"));

用 ExpandoObject对象:

Console.WriteLine(contact.Address.State);
可是,当你有很多个contact对象时该怎么办呢?
呵呵,看代码:
//用XML 方式: XElement contactsXML =     new XElement("Contacts",         new XElement("Contact",             new XElement("Name""Patrick Hines"),             new XElement("Phone""206-555-0144")         ),         new XElement("Contact",             new XElement("Name""Ellen Adams"),             new XElement("Phone""206-555-0155")         )     ); //用dynamic对象: dynamic contacts = new List<dynamic>(); contacts.Add(new ExpandoObject()); contacts[0].Name = "Patrick Hines"; contacts[0].Phone = "206-555-0144"; contacts.Add(new ExpandoObject()); contacts[1].Name = "Ellen Adams"; contacts[1].Phone = "206-555-0155";

再来看看用Linq to Object怎么来操作dynamic吧,

 

var phones = from c in (contacts as List<dynamic>)
             
where c.Name == "Patrick Hines"
             select c.Phone;

 

大家看了这个新特性有什么感受呢?想不想立刻感受下C# 4.0?不管怎么样我是很期待啦。。希望.net越来越强大~~你可是我的饭碗啊(PS:坚决不会转向java)

[C#]C# 委托的同步调用和异步调用

mikel阅读(803)

转载:http://www.cnblogs.com/yinhu435/archive/2009/10/19/1585958.html

委托的Invoke方法用来进行同步调用。同步调用也可以叫阻塞调用,它将阻塞当前线程,然后执行调用,调用完毕后再继续向下进行。

同步调用的例子:

using System;
using System.Threading;
public delegate int AddHandler(int a, int b);
public class Foo {
static void Main() {
Console.WriteLine("**********SyncInvokeTest**************");
AddHandler handler = new AddHandler(Add);
int result = handler.Invoke(1,2);
Console.WriteLine("Do other work... ... ...");
Console.WriteLine(result);
Console.ReadLine();
}
static int Add(int a, int b) {
Console.WriteLine("Computing "+a+" + "+b+" ...");
Thread.Sleep(3000);
Console.WriteLine("Computing Complete.");
return a+b;
}
}

运行结果:

**********SyncInvokeTest**************

Computing 1 + 2 …

Computing Complete.

Do other work… … …

3

同步调用会阻塞线程,如果是要调用一项繁重的工作(如大量IO操作),可能会让程序停顿很长时间,造成糟糕
的用户体验,这时候异步调用就很有必要了。
异步调用不阻塞线程,而是把调用塞到线程池中,程序主线程或UI线程可以继续执行。
委托的异步调用通过BeginInvoke和EndInvoke来实现。
异步调用:

using System;
using System.Threading;
public delegate int AddHandler(int a, int b);
public class Foo {
static void Main() {
Console.WriteLine("**********AsyncInvokeTest**************");
AddHandler handler = new AddHandler(Add);
IAsyncResult result = handler.BeginInvoke(1,2,null,null);
Console.WriteLine("Do other work... ... ...");
Console.WriteLine(handler.EndInvoke(result));
Console.ReadLine();
}
static int Add(int a, int b) {
Console.WriteLine("Computing "+a+" + "+b+" ...");
Thread.Sleep(3000);
Console.WriteLine("Computing Complete.");
return a+b;
}
}

运行结果: **********AsyncInvokeTest**************
Do other work… … …
Computing 1 + 2 …
Computing Complete.
3  

可以看到,主线程并没有等待,而是直接向下运行了。
但是问题依然存在,当主线程运行到EndInvoke时,如果这时调用没有结束(这种情况很可能出现),这时为了等待调用结果,线程依旧会被阻塞。
解决的办法是用回调函数,当调用结束时会自动调用回调函数
回调异步:

public class Foo {
static void Main() {
Console.WriteLine("**********AsyncInvokeTest**************");
AddHandler handler = new AddHandler(Add);
IAsyncResult result = handler.BeginInvoke(1,2,new AsyncCallback(AddComplete),"AsycState:OK");
Console.WriteLine("Do other work... ... ...");
Console.ReadLine();
}
static int Add(int a, int b) {
Console.WriteLine("Computing "+a+" + "+b+" ...");
Thread.Sleep(3000);
Console.WriteLine("Computing Complete.");
return a+b;
}
static void AddComplete(IAsyncResult result) {
AddHandler handler = (AddHandler)((AsyncResult)result).AsyncDelegate;
Console.WriteLine(handler.EndInvoke(result));
Console.WriteLine(result.AsyncState);
}
}

[MVC]ASP.Net MVC 源码解析系列

mikel阅读(969)

转载自Q.yuChen的blog
他写的这系列的文章比较清晰明了,值得收藏,对学习ASP.NET MVC Framework原理很有帮助,特收藏之
ASP.NET MVC

ASP.NET MVC Preview 2 – 流程分析 (1)
ASP.NET MVC Preview 2 – 流程分析 (2)
ASP.NET MVC Preview 2 – 流程分析 (3)
ASP.NET MVC Preview 2 – RedirectToAction
ASP.NET MVC Preview 2 – ClientNoCacheFilterAttribute
ASP.NET MVC Preview 2 – AuthenticateFilterAttribute
ASP.NET MVC Preview 2 – ActionCacheFilterAttribute
ASP.NET MVC Preview 2 – NVelocityViewEngine
ASP.NET MVC Preview 2 – Context
ASP.NET MVC Preview 2 – ControllerActionFilter
ASP.NET MVC Preview 3 – 1. MvcHandler
ASP.NET MVC Preview 3 – 2. Controller
ASP.NET MVC Preview 3 – 3. View
ASP.NET MVC Preview 3 – 4. Route
ASP.NET MVC Preview 4 – 1. ActionInvoker
ASP.NET MVC Preview 4 – 2. Filter
ASP.NET MVC 1.0 – 1. 准备工作
ASP.NET MVC 1.0 – 2. 流程分析 (System.Web.Routing)
ASP.NET MVC 1.0 – 3. 流程分析 (MvcHandler & Controller)
ASP.NET MVC 1.0 – 4. 流程分析 (ControllerActionInvoker)
ASP.NET MVC 1.0 – 5. 流程分析 (ControllerActionInvoker 续)
ASP.NET MVC 1.0 – 6. 流程分析 (ViewResult)
ASP.NET MVC 1.0 – 7. Route Namespace
ASP.NET MVC 1.0 – 8. TempData
ASP.NET MVC 1.0 – 9. ModelBinder
ASP.NET MVC 1.0 – 10. Controller
ASP.NET MVC 1.0 – 11. ViewData
ASP.NET MVC 1.0 – 12. NVelocityViewEngine
ASP.NET MVC 1.0 – 13. OutputCacheAttribute
ASP.NET MVC 1.0 – 14. CompressAttribute
ASP.NET MVC 1.0 – 15. StaticCacheAttribute
ASP.NET MVC 1.0 – 16. NoClientCacheAttribute
ASP.NET MVC 1.0 – 17. Anti Attack
ASP.NET MVC 1.0 – 18. ControllerContext