zoukankan      html  css  js  c++  java
  • HttpContext.Current

    了解ASP.NET的开发人员都知道它有个非常强大的对象 HttpContext,而且为了方便,ASP.NET还为它提供了一个静态属性HttpContext.Current来访问它, 今天的博客打算就从HttpContext.Current说起。

    无处不在的HttpContext

    由于ASP.NET提供了静态属性HttpContext.Current,因此获取HttpContext对象就非常方便了。
    也正是因为这个原因,所以我们经常能见到直接访问HttpContext.Current的代码:

    public class Class1
    {
        public Class1()
        {
            string file = HttpContext.Current.Request.MapPath("~/App_Data/xxxxxx.xml");
    
            string text = System.IO.File.ReadAllText(file);
    
            //..........其它的操作
        }
    
        // 或者在一些方法中直接使用HttpContext.Current
        public void XXXXX()
        {
            string url = HttpContext.Current.Request.RawUrl;
    
            string username = HttpContext.Current.Session["username"].ToString();
    
            string value = (string)HttpContext.Current.Items["key"];
        }
    
        // 甚至还设计成静态属性
        public static string XXX
        {
            get
            {
                return (string)HttpContext.Current.Items["XXX"];
            }
        }
    }
    

    这样的代码,经常能在类库项目中看到,由此可见其泛滥程度。

    难道这些代码真的没有问题吗?
    有人估计会说:我写的代码是给ASP.NET程序使用的,又不是给控制台程序使用,所以没有问题。

    真的是这样吗?

    HttpContext.Current到底保存在哪里?

    的确,在一个ASP.NET程序中,几乎任何时候,我们都可以访问HttpContext.Current得到一个HttpContext对象, 然而,您有没有想过它是如何实现的呢?

    如果您没有想过这个事情,那我今天就来告诉您吧。请看下面的代码:

    protected void Page_Load(object sender, EventArgs e)
    {
        HttpContext context1 = HttpContext.Current;
    
        HttpContext context2 = System.Runtime.Remoting.Messaging.CallContext.HostContext as HttpContext;
    
        bool isEqual = object.ReferenceEquals(context1, context2);
    
        Response.Write(isEqual);
    }
    

    猜猜会显示什么? 







    这就是我看到的结果,不信的话您也可以试试。

    从这段代码来看,HttpContext其实是保存在CallContext.HostContext这个属性中, 如果您还对HostContext感到好奇的话,您可以自己用Reflector.exe去看,我不想再贴代码了,因为有些类型和方法并不是公开的。

    我们还是来看看MSDN是如何解释CallContext.HostContext的吧:

    获取或设置与当前线程相关联的主机上下文。

    这个解释非常含糊,不过有二个关键词我们可以记下来:【当前线程】,【关联】。

    是说:和当前线程相关联的某个东西吗?
    我是这样理解的。

    我们在一个ASP.NET程序中,为什么可以到处访问HttpContext.Current呢?
    因为ASP.NET会为每个请求分配一个线程,这个线程会执行我们的代码来生成响应结果, 即使我们的代码散落在不同的地方(类库),线程仍然会执行它们, 所以,我们可以在任何地方访问HttpContext.Current获取到与【当前请求】相关的HttpContext对象, 毕竟这些代码是由同一个线程来执行的嘛,所以得到的HttpContext引用也就是我们期待的那个与请求相关的对象。

    因此,将HttpContext.Current设计成与【当前线程】相关联是合适的。

    HttpContext并非无处不在!

    【当前线程】是个什么意思? 我为什么要突出这个词呢?

    答:
    1. 当前线程是指与【当前请求】相关的线程。
    2. 在ASP.NET中,有些线程并非总是与请求相关。

    感觉有点绕口吗? 不容易理解吗? 还是继续往下看吧。

    虽然在ASP.NET程序中,几乎所有的线程都应该是为响应请求而运行的,
    但是,还有一些线程却不是为了响应请求而运行,例如:
    1. 定时器的回调。
    2. Cache的移除通知。
    3. APM模式下异步完成回调。
    4. 主动创建线程或者将任务交给线程池来执行。

    在以上这些情况中,如果线程执行到HttpContext.Current,您认为会返回什么?
    还是一个HttpContext的实例引用吗?
    如何是,那它与哪个请求关联?

    显然,在1,2二种情况中,访问HttpContext.Current将会返回 null 。
    因为很有可能任务在运行时根本没有任何请求发生。
    了解异步的人应该能很容易理解第3种情况(就当是个结论吧)
    第4种情况就更不需要解释了,因为确实不是当前线程。

    既然是这样,那我们再看一下本文开头的一段代码:

    public Class1()
    {
        string file = HttpContext.Current.Request.MapPath("~/App_Data/xxxxxx.xml");
    
        string text = System.IO.File.ReadAllText(file);
    
        //..........其它的操作
    }
    

    想像一下:如果Class1是在定时器回调或者Cache的移除通知时被创建的,您认为它还能正常运行吗?

    此刻您心里应该有答案了吧?

    可能您会想:为什么我在其它任何地方又可以访问HttpContext.Current得到HttpContext引用呢?
    答:那是因为ASP.NET在调用您的代码前,已经将HttpContext设置到前面所说的CallContext.HostContext属性中。
    HttpApplication有个内部方法OnThreadEnter(),ASP.NET在调用外部代码前会调用这个方法来切换HttpContext, 例如:每当执行管线的事件处理器之前,或者同步上下文(AspNetSynchronizationContext)执行回调时。 切换线程的CallContext.HostContext属性之后,我们的代码就可以访问到HttpContext引用。 注意:HttpContext的引用其实是保存在HttpApplication对象中。

    有时候我们会见到【ASP.NET线程】这个词,今天正好来说说我对这个词的理解: 当前线程是与一个HttpContext相关的线程,由于线程与HttpContext相关联,也就意味着它正在处理发送给ASP.NET的请求。 注意:这个线程仍然是线程池的线程。

    如何获取文件绝对路径?

    在定时器回调或者Cache的移除通知中,有时确实需要访问文件,然而对于开发人员来说, 他们并不知道网站会被部署在哪个目录下,因此不可能写出绝对路径, 他们只知道相对于网站根目录的相对路径,为了定位文件路径,只能调用HttpContext.Current.Request.MapPath或者 HttpContext.Current.Server.MapPath来获取文件的绝对路径。 如果HttpContext.Current返回了null,那该如何如何访问文件?

    其实方法并非MapPath一种,我们可以访问HttpRuntime.AppDomainAppPath获取网站的路径,然后再拼接文件的相对路径即可:

    看到没:图片中HttpContext.Current显示的是 null ,所以您要是再调用MapPath,就必死无疑!

    在此我也奉劝大家一句:尽量不要用MapPath,HttpRuntime.AppDomainAppPath才是更安全的选择。

    异步调用中如何访问HttpContext?

    前面我还提到在APM模式下的异步完成回调时,访问HttpContext.Current也会返回null,那么此时该怎么办呢?

    答案有二种:
    1. 在类型中添加一个字段来保存HttpContext的引用(异步开始前)。
    2. 将HttpContext赋值给BeginXXX方法的最后一个参数(object state)

    建议优先选择第二种方法,因为可以防止以后他人维护时数据成员被意外使用。

    安全地使用HttpContext.Current

    有时我们会写些通用类库给ASP.NET或者WindowsService程序来使用,例如异常记录的工具方法。 对于ASP.NET程序来说,我们肯定希望在异常发生时,能记录URL,表单值,Cookie等等数据,便于事后分析。 然而对于WindowsService这类程序来说,您肯定没想过要记录Cookie吧? 那么如何实现一个通用的功能呢?

    方法其实也简单,就是要判断HttpContext.Current是否返回null,例如下面的示例代码:

    public static void LogException(Exception ex)
    {
        StringBuilder sb = new StringBuilder();
        sb.Append("异常发生时间:").AppendLine(DateTime.Now.ToString());
        sb.AppendLine(ex.ToString());
    
        // 如果是ASP.NET程序,还需要记录URL,FORM, COOKIE之类的数据
        HttpContext context = HttpContext.Current;
        if( context != null ) {
            // 能运行到这里,就肯定是在处理ASP.NET请求,我们可以放心地访问Request的所有数据
            sb.AppendLine("Url:" + context.Request.RawUrl);
    
            // 还有记录什么数据,您自己来实现吧。
        }
    
        System.IO.File.AppendAllText("日志文件路径", sb.ToString());
    }
    

    就是一个判断,解决了所有问题,所以请忘记下面这类不安全的写法吧:

    HttpContext.Current.Request.RawUrl;
    HttpContext.Current.Server.MapPath("xxxxxx");
    

    下面的方法才是安全的:

    HttpContext context = HttpContext.Current;
    if( context != null ) {
        // 在这里访问与请求有关的东西。
    }
    
     
     
    分类: Asp.net
     
  • 相关阅读:
    第十二章学习笔记
    UVa OJ 107 The Cat in the Hat (戴帽子的猫)
    UVa OJ 123 Searching Quickly (快速查找)
    UVa OJ 119 Greedy Gift Givers (贪婪的送礼者)
    UVa OJ 113 Power of Cryptography (密文的乘方)
    UVa OJ 112 Tree Summing (树的求和)
    UVa OJ 641 Do the Untwist (解密工作)
    UVa OJ 105 The Skyline Problem (地平线问题)
    UVa OJ 100 The 3n + 1 problem (3n + 1问题)
    UVa OJ 121 Pipe Fitters (装管子)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3003194.html
Copyright © 2011-2022 走看看