为什么我的嵌套HttpModule EndRequest事件处理程序没有触发?

58

我尝试在一个嵌套的 HttpModule 中使用 EndRequest 事件处理程序修改标头,但在 MVC 5.2.2 和 .NET 4.6.2 上出现了一些奇怪的行为。如果我不在顶层的 HttpModule 中修改 EndRequest,那么即使我知道已经在嵌套的 HttpModule 上调用了 Init,它的事件处理程序似乎也永远不会触发。

我的问题是,为什么我的代码会阻止 "TestNested" 标头出现在响应标头中,除非我包含已注释掉的添加无操作的 EndRequest 事件处理程序的代码?


动态注册我的顶层 HttpModule

[assembly: PreApplicationStartMethod(typeof(PreApplicationStartClass), "Start")]
namespace MyNamespace
{
    public class PreApplicationStartClass
    {
        public static void Start()
        {
            DynamicModuleUtility.RegisterModule(typeof(TopHttpModule));
        }
    }
}

从一个顶级模块调用Init,以便初始化所有其他HttpModules

namespace MyNamespace
{
    public class TopHttpModule: IHttpModule
    {
        private readonly Lazy<IEnumerable<IHttpModule>> _modules = 
            new Lazy<IEnumerable<IHttpModule>>(RetrieveModules);

        private static IEnumerable<IHttpModule> RetrieveModules()
        {
            return DependencyResolver.Current.GetServices<IHttpModule>();
        }

        public void Init(HttpApplication context)
        {
            var modules = _modules.Value;
            foreach (var module in modules
                .Where(module => module.GetType() != typeof(TopHttpModule)))
            {
                module.Init(context);
            }

            context.BeginRequest += (sender, e) =>
            {
                var app = sender as HttpApplication;
                if (app != null)
                {
                    //This shows that NestedHttpModule was found
                    app.Context.Response.Headers.Add(
                        "TestModules",
                        string.Join(",", modules.Select(_ => _.GetType().ToString())));
                }
            };

            //Add this and the NestedHttpModule EndRequest handler works
            //context.EndRequest += (sender, e) =>
            //{
            //    //Do Nothing
            //};
        }

        public void Dispose()
        {
            var modules = _modules.Value;
            foreach (var disposable in modules
                .Where(disposable => disposable.GetType() != typeof(TopHttpModule)))
            {
                disposable.Dispose();
            }
        }
    }
}

EndRequest 事件处理程序中修改一些头信息

namespace MyNamespace
{
    public class NestedHttpModule: IHttpModule
    {
        public void Init(HttpApplication context)
        {
            //This gets called whether or not the TopHttpModule modifies context.EndRequest 
            MvcHandler.DisableMvcResponseHeader = true;

            context.EndRequest += Application_EndRequest;
        }

        public void Application_EndRequest(object sender, EventArgs e)
        {
            var app = sender as HttpApplication;
            if (app != null && app.Context != null)
            {
                //This doesn't appear to be called unless TopHttpModule modifies context.EndRequest
                app.Context.Response.Headers.Add("TestNested", "Found");
            }
        }

        public void Dispose()
        {
            //Do Nothing
        }
    }
}

13
显然的问题是,为什么你要使用HttpModule来更改标头,而不是使用MVC API(如过滤器)来做这件事呢?为什么要为已经可以在MVC中轻松修改的内容使用复杂的ASP.NET遗留技术呢? - NightOwl888
1
你使用的是哪个版本的Asp.Net MVC和.NET框架?对我来说,NestedHttpModule.EndRequest触发了。 - Praveen Reddy
1
@ThomasLangston,这个问题还在进行中吗?您能否回答一下自己的问题并说明您所做的事情? - PSGuy
5
你能否用你所使用的解决方案回答自己的问题? - garfbradaz
2
你可能需要查看 Context.ApplicationInstance.CompleteRequest()。几年前我遇到了这样一种情况,应用程序接收请求后会做其他事情,并在不让请求完成的情况下进行重定向。我相信这就是我用来处理这个问题的方法。 - Bardicer
显示剩余7条评论
1个回答

1
我也想修改我的头部信息,但我需要尽可能地隐藏它。对于添加或删除或两者都是一样的,只是头部信息不同。
1)您可以在global.asax中设置MvcHandler.DisableMvcResponseHeader = true;
        protected void Application_Start()
        {
            MvcHandler.DisableMvcResponseHeader = true;
        }

and

        protected void Application_PreSendRequestHeaders()
        {
            Response.Headers.Remove("Server");
            Response.Headers.Remove("X-AspNet-Version");
        }

2) 对于几乎相同的工作,您不应该真正使用diff模块,而是创建一个HeadersModule,仅处理标题修改,并使用PreSendRequestHeaders添加或删除任何标题。您始终可以注入某些服务以获取要添加或删除的标题列表。

    public class HeadersModule : IHttpModule
    {
        public void Init(HttpApplication context)
        {
            context.PreSendRequestHeaders += OnPreSendRequestHeaders;
        }

        public void Dispose() {

        }

        void OnPreSendRequestHeaders(object sender, EventArgs e)
        {

            var r = sender as HttpApplication;
            r.Response.Headers.Remove("Server");
            r.Response.Headers.Remove("X-AspNetMvc-Version");
            r.Response.Headers.Remove("X-AspNet-Version");
            r.Response.Headers.Remove("X-Powered-By");
        }
    }

3)为了更加确保某些标题显示或者不显示,您可以将以下内容添加到配置文件中:

  <system.webServer>
    <modules>
      <add name="HeadersModule " type="MyNamespace.Modules.HeadersModule " />
    </modules>
    <httpProtocol>
      <customHeaders>
        <remove name="X-Powered-By" />
        <remove name="Server" />
        <remove name="X-AspNet-Version" />
        <remove name="X-AspNetMvc-Version" />
      </customHeaders>
      <redirectHeaders>
        <clear />
      </redirectHeaders>
    </httpProtocol>
  </system.webServer>

4) 测试所有页面,包括 404 页面、错误页面和奇怪的路径名称,因为它们可能会泄露某些头信息或显示您没有预期的头信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接