Вызовите по умолчанию asp.net HttpHandler из пользовательского обработчика

Я добавляю маршрутизацию ASP.NET в более старое приложение веб-форм. Я использую пользовательский HttpHandler для обработки всего. В некоторых ситуациях я хотел бы сопоставить конкретный путь к файлу aspx, поэтому мне нужно просто передать управление HttpHandler по умолчанию для asp.net.

Самое близкое, что я получил, это

public void ProcessRequest(HttpContext context) {
    // .. when we decide to pass it on

    var handler = new System.Web.UI.Page();
    handler.ProcessRequest(context);

    MemoryStream steam = new MemoryStream();
    StreamWriter writer = new StreamWriter(stream);
    HtmlTextWriter htmlWriter = new HtmlTextWriter(writer);
    handler.RenderControl(htmlWriter);


    // write headers, etc. & send stream to Response
}

Он ничего не делает, в поток ничего не выводится. Документация MS для System.Web.UI.Page(как IHttpHandler) говорит о том, что "не вызывайте метод ProcessRequest, он используется для внутреннего использования".

От взгляда вокруг кажется, что вы можете сделать это с помощью MVC, например.: MvcHttpHandler, похоже, не реализует IHttpHandler

Существует также эта вещь System.Web.UI.PageHandlerFactory, которая кажется, что она просто создала обработчик страницы для aspx файла, но она внутренняя, и я не могу использовать ее напрямую.

Эта страница: http://msdn.microsoft.com/en-us/library/bb398986.aspx относится к "обработчику asp.net по умолчанию", но не идентифицирует класс или не дает никаких указаний о том, как его можно было бы использовать.

Любые идеи о том, как я могу это сделать? Возможно ли это?

Ответ 1

Стойкость окупается! Это действительно работает, и поскольку эта информация, похоже, доступна почти нигде, я думал, что отвечу на мой собственный вопрос. Благодаря Роберту для этого сообщения о создании экземпляров с помощью внутренних конструкторов это ключ.

http://www.rvenables.com/2009/08/instantiating-classes-with-internal-constructors/

public void ProcessRequest(HttpContext context) {
    // the internal constructor doesn't do anything but prevent you from instantiating
    // the factory, so we can skip it.
    PageHandlerFactory factory =
        (PageHandlerFactory)System.Runtime.Serialization.FormatterServices
        .GetUninitializedObject(typeof(System.Web.UI.PageHandlerFactory));

     string newTarget  = "default.aspx"; 
     string newQueryString = // whatever you want
     string oldQueryString = context.Request.QueryString.ToString();
     string queryString = newQueryString + oldQueryString!="" ? 
         "&" + newQueryString :
         "";

     // the 3rd parameter must be just the file name.
     // the 4th parameter should be the physical path to the file, though it also
     //   works fine if you pass an empty string - perhaps that only to override
     //   the usual presentation based on the path?

     var handler = factory.GetHandler(context, "GET",newTarget,
         context.Request.MapPath(context,newTarget));

     // Update the context object as it should appear to your page/app, and
     // assign your new handler.

     context.RewritePath(newTarget  , "", queryString);
     context.Handler = handler;

     // .. and done

     handler.ProcessRequest(context);
}

... и, как и небольшое чудо, страница aspx обрабатывает и обрабатывает весь процесс без необходимости перенаправления.

Я ожидаю, что это будет работать только в IIS7.

Ответ 2

Я использую Routing в webforms, вы должны просто добавить маршрут игнорирования для определенных файлов .aspx, которые вы хотите. Затем будет обрабатываться HttpHandler по умолчанию.

http://msdn.microsoft.com/en-us/library/dd505203.aspx

Ответ 3

Другой вариант - инвертировать логику, обрабатывая случаи, в которых вы НЕ хотите возвращать ответ по умолчанию и переназначать других на свой собственный IHttpHandler. Всякий раз, когда myCondition является ложным, ответ будет "по умолчанию". Коммутатор реализован как IHttpModule:

public class SwitchModule: IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.PostAuthenticateRequest += app_PostAuthenticateRequest;
    }

    void app_PostAuthenticateRequest(object sender, EventArgs e)
    {
        // Check for whatever condition you like           
        if (true)
            HttpContext.Current.RemapHandler(new CustomHandler());

    }

    public void Dispose()        
}

internal class CustomHandler: IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        context.Response.Write("hallo");
    }

    public bool IsReusable { get; }
}