views:

744

answers:

2

I've enabled trace pageoutput="true" in my web.config and I like the easy way it provides of seeing all this stuff at the bottom of the page.

I'd like to get the same output from trace at the bottom of the output from my httphandler. Is there a way to dump out the same trace info via code that would follow this code:

public class UploadHandler : IHttpHandler
{

    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/plain";
        context.Response.Write("Hello World");
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

I particularly want to see the Forms and QueryString collections but all this gives is "Hello World".

-- edit update 7/25/2009:

    public class UploadHandler : IHttpHandler
    {

    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/plain";
        context.Response.Write("Hello World");

        object htw = new System.Web.UI.Html32TextWriter(context.Response.Output);
        {
            typeof(TraceContext)
                .GetMethod("Render", System.Reflection.BindingFlags.NonPublic)
                .Invoke(HttpContext.Current.Trace, new object[] { htw });
        }

    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

I am also open to any other ideas about how to most easily get a formatted dump of the forms and querystring collections like pageOutput trace does.

A: 

I don't think that ASP.NET tracing works except for pages.

Does it matter if the output is at the bottom of the page? What if the output were in a separate file, or in the event log? You could probably do thsi with ASP.NET Health Monitoring.

John Saunders
+1  A: 

You can get your own trace events from HttpContext.Current.Trace.TraceFinished. Unfortunately, the page tracing (which includes all the goodies of Forms, QueryString, etc.) is locked away in internal methods.

If you're okay with reflection, you can call it like:

using (var htw = new System.Web.UI.Html32TextWriter(response.Output)) {
    typeof(TraceContext)
        .GetMethod("Render",BindingFlags.NonPublic | BindingFlags.Instance)
        .Invoke(HttpContext.Current.Trace, new object[] { htw });
}

Reflectoring over System.Web.TraceContext.EndRequest should give you enough to create your own TracingHttpHandler if you can't use reflection.

Edit: It looks like you forgot BindingFlags.Instance. Also, I'm guessing you changed using (var htw = ...) to using (object htw = ...) which would give you the "type must be implicitly convertible to IDisposable" error. If you can't use var, then you'll have to write it as using (Html32TextWriter htw = ...).

Full sample:

<%@ WebHandler Language="C#" Class="UploadHandler" %>

using System;
using System.Web;
using System.Web.UI;
using System.Reflection;

public class UploadHandler : IHttpHandler {
    public bool IsReusable { 
       get { return true; }
    }

    public void ProcessRequest(HttpContext context) {
       // the output will suck as text/plain - Render outputs HTML.
       context.Response.ContentType = "text/html"; 
       context.Response.Write("Hello World!");

       // depending on web.config settings, you may need to enable tracing manually
       HttpContext.Current.Trace.IsEnabled = true;
       // I had to write a custom trace message, or the Request context wasn't captured - YMMV
       HttpContext.Current.Trace.Write(null);

       using (Html32TextWriter htw = new Html32TextWriter(context.Response.Output)) {
          typeof(TraceContext)
              .GetMethod("Render", BindingFlags.NonPublic | BindingFlags.Instance)
             .Invoke(HttpContext.Current.Trace, new object[] { htw });
       } 
    }
}
Mark Brackett
Tried your using block and he complained about having to implement IDisposable so I tried: public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; context.Response.Write("Hello World"); object htw = new System.Web.UI.Html32TextWriter(context.Response.Output); { typeof(TraceContext) .GetMethod("Render", System.Reflection.BindingFlags.NonPublic) .Invoke(HttpContext.Current.Trace, new object[] { htw }); } }Err on typeof
John Galt
@John Galt - Html32TextWriter *does* implement IDisposable (http://msdn.microsoft.com/en-us/library/system.web.ui.html32textwriter.dispose.aspx) and typeof is standard C# (http://msdn.microsoft.com/en-us/library/58918ffs(VS.71).aspx). Post all of your code into your question (so it's formatted), and I can check the syntax.
Mark Brackett
Hi Mark...thanks...please see the edit update above.
John Galt
Mark, Thanks for your code and explanation. It worked exactly as I hoped it might and yet I re-read your comment closely - "Unfortunately, the page tracing (which includes all the goodies of Forms, QueryString, etc.) is locked away in internal methods." Is there any relatively simple way I can "dump out" the Forms and Querystring collections with code by extending the routine you've provided here? Or is it just not available inside HTTPHandler? That is, not sure what you mean by "locked away in internal methods".
John Galt
Mark, ..still not sure what you meant by "Unfortunately, the page tracing (which includes all the goodies of Forms, QueryString, etc.) is locked away in internal methods. BUT - the good news is that I looked closely at the output produced by the code you provided and sure enough- the Form, Querystring collections and others are all formatted just like for an .aspx page. So - thanks very much!
John Galt
@John Galt - It means there's no *public* API that can be called to get the formatted output. Reflection bypasses the public API, and calls an internal method - nromally only available to MS. Internal methods are, by definition, unsupported - they can be changed or removed by MS at any time and would not be considered "breaking". Also, reflection requires Full Trust - meaning you couldn't run this code on most commercial ASP.NET web hosts.
Mark Brackett