ASP.NET Post Application_Error 事件

发布于 2024-07-13 23:25:39 字数 335 浏览 12 评论 0原文

我试图找到一个在所有 Application_Error 事件处理程序之后立即触发的事件,以便我可以修改发送的响应(弄乱状态代码和“位置”标头并专门创建一个新主体)使用自定义 HttpModule。

我尝试挂钩 Application_EndRequest (因为我读到这是唯一保证触发的处理程序),但这在请求处理中为时已晚,无法修改响应标头,我得到了HttpException

Server cannot append header after HTTP headers have been sent.

I'm trying to find an event that will fire immediately after all Application_Error event handlers so that I can modify the response sent (messing with the status code and 'location' headers and creating a new body specifically) using a custom HttpModule.

I've tried hooking into Application_EndRequest (as I've read that this is the only handler that is guaranteed to fire) but this is too late in the request processing to modify the response headers and I get a HttpException:

Server cannot append header after HTTP headers have been sent.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

可遇━不可求 2024-07-20 23:25:39

我认为 ASP.Net 在 ApplicationError 之后启动了一个新的响应,只是针对不同的错误处理页面。 尝试加入这个回应。

更新 好的:以下是具体操作方法!

我想确保我们都可以附加错误处理程序,并且可以优雅地处理页面中的事件,因此我们不能使最后一个服务器错误为空,而且我们还希望能够添加标头!

在 web.config 中,添加:

<customErrors redirectMode="ResponseRewrite" mode="On">
    <error statusCode="500" redirect="~/ErrorHandler.aspx"/>
</customErrors>

或任何您想要捕获的错误。 在 httpModules 下:

<add name="ErrorModule" type="TestErrorLifecycle.ErrorModule, TestErrorLifecycle"/>

记录错误的代码位于此模块中:

public class ErrorModule : IHttpModule
{
    private volatile object locker = new object();

    public void Init(HttpApplication context)
    {
        context.Error += context_Error;
    }

    void context_Error(object sender, EventArgs e)
    {
        var app = sender as HttpApplication;

        lock (locker)
        {
            assertLogDirectory(app.Server);

            using (var fs = File.AppendText(app.Server.MapPath("~/logs/errorlog.txt")))
            {
                var lastException = app.Server.GetLastError();
                if (lastException == null) throw new ApplicationException("Not expected...");
                fs.WriteLine(lastException.Message);
            }
        }

        // we could also do a Request.Redirect() here...
    }

    private void assertLogDirectory(HttpServerUtility server)
    {
        var logdir = server.MapPath("~/logs/");
        if (!Directory.Exists(logdir))
            Directory.CreateDirectory(logdir);
    }

    public void Dispose()
    {
    }
}

我刚刚将其写入文件系统。 您可以使用内核文件事务或独占锁或其他任何东西,但因为我知道我只会从这里写入该文件,所以我选择了一个简单的私有信号量。

我在 Default.aspx.cs 中添加了这个只是为了测试:

protected void Page_Load(object sender, EventArgs e)
{
    throw new ApplicationException("wtf, something wrong??!");
}

创建了包含此文件的 ErrorHandler.aspx 文件(为简短起见,省略了一些部分):

<body>
    <h2>Error handler</h2>
    <form id="form1" runat="server">
        <p>Last error: <asp:Label ID="lblError" runat="server" /></p>
    </form>
</body>

这是页面类:

public partial class ErrorHandler : Page
{
    public ErrorHandler()
    {
        Load += ErrorHandler_Load;
    }

    private void ErrorHandler_Load(object sender, EventArgs e)
    {
        Response.AddHeader("X-TESTING", "Yes it works...");
        lblError.Text = Server.GetLastError() == null ? "noo, why is it null?!?" : Server.GetLastError().Message;
    }
}

最后,我在 ErrorHandler.aspx 处获得了这些响应标头:

Server: ASP.NET Development Server/9.0.0.0
Date: Mon, 23 Feb 2009 00:37:45 GMT
X-AspNet-Version: 2.0.50727
X-TESTING: Yes it works...
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 725
Connection: Close
200 OK

如果您尝试直接添加到 Response.Headers-collection,则需要以管道模式运行 IIS: http://forums.asp.net/p/1253457/2323117.aspx

我希望这有帮助! 干杯
亨里克

I think ASP.Net launches a new response after ApplicationError, just for a different error-handling page. Try tacking into this response.

Update OK: Here's how to do it!

I wanted to make sure we can both attach an error handler and that we can gracefully handle the event in a page, so we can't make the last server error null, and we also want to be able to add headers!

In web.config, add:

<customErrors redirectMode="ResponseRewrite" mode="On">
    <error statusCode="500" redirect="~/ErrorHandler.aspx"/>
</customErrors>

Or whatever error specifically you're looking to catch. And under httpModules:

<add name="ErrorModule" type="TestErrorLifecycle.ErrorModule, TestErrorLifecycle"/>

The code to log the error is in this module:

public class ErrorModule : IHttpModule
{
    private volatile object locker = new object();

    public void Init(HttpApplication context)
    {
        context.Error += context_Error;
    }

    void context_Error(object sender, EventArgs e)
    {
        var app = sender as HttpApplication;

        lock (locker)
        {
            assertLogDirectory(app.Server);

            using (var fs = File.AppendText(app.Server.MapPath("~/logs/errorlog.txt")))
            {
                var lastException = app.Server.GetLastError();
                if (lastException == null) throw new ApplicationException("Not expected...");
                fs.WriteLine(lastException.Message);
            }
        }

        // we could also do a Request.Redirect() here...
    }

    private void assertLogDirectory(HttpServerUtility server)
    {
        var logdir = server.MapPath("~/logs/");
        if (!Directory.Exists(logdir))
            Directory.CreateDirectory(logdir);
    }

    public void Dispose()
    {
    }
}

I just wrote it to the filesystem. You could use kernel file transactions or an exclusive lock or whatever, but because I know I will only write to this file from here, I chose a simple private semaphore.

I added this in Default.aspx.cs just to test:

protected void Page_Load(object sender, EventArgs e)
{
    throw new ApplicationException("wtf, something wrong??!");
}

Created file ErrorHandler.aspx with this in it (some parts omitted for shortness):

<body>
    <h2>Error handler</h2>
    <form id="form1" runat="server">
        <p>Last error: <asp:Label ID="lblError" runat="server" /></p>
    </form>
</body>

And here's the page-class:

public partial class ErrorHandler : Page
{
    public ErrorHandler()
    {
        Load += ErrorHandler_Load;
    }

    private void ErrorHandler_Load(object sender, EventArgs e)
    {
        Response.AddHeader("X-TESTING", "Yes it works...");
        lblError.Text = Server.GetLastError() == null ? "noo, why is it null?!?" : Server.GetLastError().Message;
    }
}

Finally, I get these response headers at the ErrorHandler.aspx:

Server: ASP.NET Development Server/9.0.0.0
Date: Mon, 23 Feb 2009 00:37:45 GMT
X-AspNet-Version: 2.0.50727
X-TESTING: Yes it works...
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 725
Connection: Close
200 OK

If you try to add to Response.Headers-collection directly you need to run IIS in pipeline mode: http://forums.asp.net/p/1253457/2323117.aspx

I hope this helps! Cheers
Henrik

祁梦 2024-07-20 23:25:39

除非您在发送任何响应之前移动所有容易出错的代码,否则您将无法解决此问题。 如果您想这样做,则需要设置一个 HttpHandler,以便在将任何信息发送回客户端之前运行所有代码。

有多种方法可以重新架构以将信息返回给客户端。 了解应用程序在哪里崩溃并通过 try ... catch 运行它会有所帮助。

Unless you move all error prone code prior to any of the Response being sent, you will not be able to solve this issue. If you want to do this, you need to set up an HttpHandler to work through having all of the code run prior to sending any information back to the client.

There are ways to re-architect to get info back to the client. Knowing where the application blows up and running it through try ... catch will help.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文