zoukankan      html  css  js  c++  java
  • HttpContext 来源(System.Web.HttpContext.Current值为null的问题)

    转大神文档:https://www.cnblogs.com/linJie1930906722/p/5708966.html

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

    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Web;
     5 
     6 namespace Test_HttpContext.Current
     7 {
     8     public class Test
     9     {
    10 
    11         public Test()
    12         {
    13             string file =System.Web.HttpContext.Current.Request.MapPath("~/App_Data/1.xml");
    14 
    15             string text = System.IO.File.ReadAllText(file);
    16 
    17             //..........其它的操作
    18         }
    19 
    20         // 或者在一些方法中直接使用HttpContext.Current
    21         public void Test_1()
    22         {
    23             string url = System.Web.HttpContext.Current.Request.RawUrl;
    24 
    25             string username = System.Web.HttpContext.Current.Session["username"].ToString();
    26 
    27             string value = (string)System.Web.HttpContext.Current.Items["key"];
    28         }
    29 
    30         // 甚至还设计成静态属性
    31         public static string Test_2
    32         {
    33             get
    34             {
    35                 return (string)System.Web.HttpContext.Current.Items["XXX"];
    36             }
    37         }
    38 
    39         /// <summary>
    40         /// 获取文件绝对路径
    41         /// </summary>
    42         /// <param name="fileName">文件名称</param>
    43         /// <returns></returns>
    44         public string Test_3(string fileName)
    45         {
    46             return System.Web.HttpContext.Current.Server.MapPath("~/Log" + fileName);
    47         }
    48 
    49     }
    50 }
    复制代码

    上面的这些代码这样写真的没有问题吗?
    答案是否定的

    请看下面的验证:

    我们先来看看HttpContext到底存储在哪里:

    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Web;
     5 using System.Web.UI;
     6 using System.Web.UI.WebControls;
     7 
     8 namespace Test_HttpContext.Current
     9 {
    10     public partial class WebForm1 : System.Web.UI.Page
    11     {
    12         protected void Page_Load(object sender, EventArgs e)
    13         {
    14 
    15             HttpContext context1 = System.Web.HttpContext.Current;
    16 
    17             HttpContext context2 = System.Runtime.Remoting.Messaging.CallContext.HostContext as HttpContext;  //当前(请求)线程上下文
    18 
    19             bool isEqual = object.ReferenceEquals(context1, context2);
    20 
    21             Response.Write("context1与context2是否相同的实例:" + isEqual);
    22         }
    23     }
    24 }
    复制代码

    上面的代码运行的结果是true:

    从这段代码来看,HttpContext其实是保存在System.Runtime.Remoting.Messaging.CallContext.HostContext这个属性中, System.Runtime.Remoting.Messaging.CallContext.HostContext在MSDN的解释是 获取或设置与当前线程相关联的主机上下文

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

    当前线程是什么意思? 
    我的理解是:
    1. 当前线程是指与当前请求相关的线程。
    2. 在ASP.NET程序中,有些线程并非总是与请求相关。

    虽然在ASP.NET程序中,几乎所有的线程都应该是为响应请求而运行的,但是还有一些线程却不是为了响应请求而(产生)运行的,

    例如:
    1. 定时器的回调。
    2. Cache的移除通知。
    3. APM模式下异步完成回调。
    4. 主动创建线程或者将任务交给线程池来执行。

    5.异步任务Task

    至于什么APM网上资料很多,这里我就不说明了

    在这些情况下使用System.Web.HttpContext.Current获取HttpContext对象得到的结果都是null,因为处理他们的线程不是当前线程(为处理请求产生线程)

    说的这里我们再回头看看本文开始写的(部分)代码:

    复制代码
    1          /// <summary>
    2         /// 获取文件绝对路径
    3         /// </summary>
    4         /// <param name="fileName">文件名称</param>
    5         /// <returns></returns>
    6         public string Test_3(string fileName)
    7         {
    8             return System.Web.HttpContext.Current.Server.MapPath("~/Log" + fileName);
    9         }
    复制代码

    如果这段代码在那5种情况下运行,都会抛空指针异常,因为System.Web.HttpContext.Current得到是null。

    为什么会得到null呢?

    因为运行这段代码线程不是处理当前请求的当前线程

    为什么其他地方得到又不是null呢?

    因为ASP.NET程序在调用您的代码前,已经将HttpContext对象设置到前面所说的System.Runtime.Remoting.Messaging.CallContext.HostContext属性中。

    HttpApplication有个内部方法OnThreadEnter(),ASP.NET在调用外部代码前会调用这个方法来切换HttpContext, 例如:每当执行管线的事件处理器之前,或者同步上下文(AspNetSynchronizationContext)执行回调时。 切换线程的CallContext.HostContext属性之后,我们的代码就可以访问到HttpContext引用。 注意:HttpContext的引用其实是保存在HttpApplication对象中。

    这种情况下该如何获取文件的绝对路径呢?

    我们可以访问System.Web.HttpRuntime.AppDomainAppPath获取程序的根路径,然后再拼接文件的相对路径即可

    上面的代码得到的HttpContext对象是null,再调用MapPath来获取站点根目录,就必死无疑了!

    所以在此建议大家在获取程序(站点)的根目录时尽量使用System.Web.HttpRuntime.AppDomainAppPath进行获取站点的根目录

    那么在异步调用调用时访问HttpContext对象呢?

    前面我还提到在APM模式下的异步完成回调时,访问HttpContext.Current也会返回null,那么此时该怎么办呢?
    1. 在类型中添加一个字段来保存HttpContext的引用(异步开始前)。
    2. 将HttpContext赋值给BeginXXX方法的最后一个参数(object state)

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

  • 相关阅读:
    数据库的初始创建
    组合总和 II (Leetcode 暴力)
    Leetcode 最大正方形(两种解法)
    Python PyAPNs 实现消息推送
    Windows下nginx+web.py+fastcgi服务搭建
    Python学习预备
    《Effective C++》笔记:IV
    《Effective C++》笔记:III
    《Effective C++》笔记:II
    《Effective C++》笔记:I
  • 原文地址:https://www.cnblogs.com/janeaiai/p/8794066.html
Copyright © 2011-2022 走看看