I write a custom handler to handle requests with OPTIONS verb.
public class OptionsRequestHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string origin = context.Request.Headers.Get("Origin");
context.Response.AddHeader("Access-Control-Allow-Origin", origin);
context.Response.AddHeader("Access-Control-Allow-Methods", "*");
context.Response.AddHeader("Access-Control-Allow-Headers", "accept, authorization, content-type");
}
public bool IsReusable
{
get { return false; }
}
}
And have registered this handler in web.config.
<system.webServer>
<modules>
......
</modules>
<handlers>
......
<add name="OptionsHandler" path="*" verb="OPTIONS" type="REAMS.Infrastructure.RequestHandlers.OptionsRequestHandler"/>
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,POST,DELETE,PUT,HEAD" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
But the handler is never selected for options requests. Is there anything wrong? Thanks!
Finally figured out this problem. Because, by default, MVC framework map request to a handler by path, it is not possible to map a handler to a request by the request verb.
To do this, I will need to preempt the handler selection of MVC and implement my own module. Here is a working copy for anyone interested.
public class OptionsVerbModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PostRequestHandlerExecute += onPostRequestHandlerExecute;
context.PostResolveRequestCache += onPostResolveRequestCache;
}
private void onPostResolveRequestCache(object sender, EventArgs eventArgs)
{
if (string.Equals(HttpContext.Current.Request.HttpMethod, "OPTIONS", StringComparison.OrdinalIgnoreCase))
{
HttpContext.Current.RemapHandler(new OptionsRequestHandler());
}
}
private void onPostRequestHandlerExecute(object sender, EventArgs e)
{
string origin = HttpContext.Current.Request.Headers.Get("Origin");
if (origin != null)
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", origin);
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials", "true");
}
}
public void Dispose()
{
}
}
What the module does is to check the verb of the request and if it is of 'OPTIONS' request, selected my customized handler in the question rather than map to MVC handler.
I do this because I find existing EnableCors for Web API is not suitable for my application needs and by using this customized process I have more control as well.
Related
I have an Asp.Net Web Api project, and i am trying to create a simple IHttpModule for logging errors.
The module gets loaded correctly, because i could register to BeginRequest / EndRequest events. However, the Error event is never triggered.
I have also added and removed the runAllManagedModulesForAllRequests="true" attribute from web.config, but still with no effect.
public class ErrorLogModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.Error += Context_Error;
}
// method never triggered
private void Context_Error(object sender, EventArgs e)
{
HttpContext ctx = HttpContext.Current;
Exception exception = ctx.Server.GetLastError();
// todo
// log Exception
}
public void Dispose()
{
}
}
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="ErrorLogger" type="HttpModules.HttpModules.ErrorLogModule" />
</modules>
</system.webServer>
[HttpGet]
[Route("triggerError")]
public string TriggerError()
{
int test = 0;
var a = 1 / test;
return "Hello Workd";
}
You can use better logging approach, that 100% working.
See this Microsoft article.
Shortly speaking you can implement
YourExceptionLogger: ExceptionLogger
with just one override method and register it by
config.Services.Add(typeof(IExceptionLogger), new YourExceptionLogger());
I would like to handle static file web requests through an HttpModule to show the documents in my CMS according to some policies. I can filter out a request, but I don't know how to directly process such a request as asp.net should do.
Is this what you're looking for? Assuming you're running in integrated pipeline mode, all requests should make it through here, so you can kill the request if unauthorized, or let it through like normal otherwise.
public class MyModule1 : IHttpModule
{
public void Dispose() {}
public void Init(HttpApplication context)
{
context.AuthorizeRequest += context_AuthorizeRequest;
}
void context_AuthorizeRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
// Whatever you want to test to see if they are allowed
// to access this file. I believe the `User` property is
// populated by this point.
if (app.Context.Request.QueryString["allow"] == "1")
{
return;
}
app.Context.Response.StatusCode = 401;
app.Context.Response.End();
}
}
<configuration>
<system.web>
<httpModules>
<add name="CustomSecurityModule" type="MyModule1"/>
</httpModules>
</system.web>
</configuration>
I create an IIS Module that appends text to the page before it loads. When I go to the URL, this works perfect the first time the page loads. On subsequent loads, however, the text is never appended.
Any thoughts on how to remedy this?
== CODE ==
Here's my web.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.webServer>
<modules>
<add name="MIModule" type="MI.MyModule, MI" />
</modules>
<caching enabled="false" enableKernelCache="false" />
</system.webServer>
</configuration>
Some module code:
public void context_PreRequestHandlerExecute(Object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
HttpRequest request = app.Context.Request;
string pageContent = app.Response.Output.ToString();
string useragent = "HI!<br />" + pageContent + "<hr />" ;
try
{
_current.Response.Output.Write(useragent);
}
catch
{
}
}
and the rest of the code:
private HttpContext _current = null;
#region IHttpModule Members
public void Dispose()
{
throw new Exception("Not implemented");
}
public void Init(HttpApplication context)
{
_current = context.Context;
context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
}
#endregion
Is the _current variable actually HttpContext.Current? Is it a static field in your module? When/how is it initialized? My guess is that the empty catch clause gobbles up all errors, and following that thought, you most probably get a null reference on _current. Try removing the try/catch to learn more on what's wrong with your code
I am a little new to .net and trying to grasp a few concepts.
I have been writing in Coldfusion for a while, and in CF there is an event under the Application.cfc called onRequest() that fires each time there is a page.
What in .net is used to capture the request information?
And moreover is there a way to latch on or extend the Request event to fire off my own events?
You can also find global.asax file and use one of events of HttpApplication class (for example BeginRequest):
http://msdn.microsoft.com/en-us/library/system.web.httpapplication.aspx
HttpApplication has Request property.
You can catch every request there, not only related to Controller (images, css, wrong address).
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_BeginRequest(object sender, EventArgs e)
{
//Request.Have_fun
}
}
If you don't want to write code in global.asax file, you should consider using HttpModule.
Create new class with this example code:
using System;
using System.Web;
namespace MyProject
{
public class MyHttpModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.BeginRequest += ApplicationBeginRequest;
application.EndRequest += ApplicationEndRequest;
}
private void ApplicationEndRequest(object sender, EventArgs e)
{
//do something here with HttpContext.Current.Request
}
private static void ApplicationBeginRequest(Object source, EventArgs e)
{
//do something here with HttpContext.Current.Request
}
public void Dispose()
{
}
}
}
Add two entries in web.config (registers HttpModule):
<system.web>
<httpModules>
<add name="MyHttpModule" type="MyProject.MyHttpModule" />
</httpModules>
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="MyHttpModule" type="MyProject.MyHttpModule" />
</modules>
</system.webserver>
Because of changes in IIS7(adding system.webServer section), you have to add two entries in web.config.
You'll probably want something like OnActionExecuting which is called before the action is hit.
To access the current request you could do the following:
protected virtual void OnActionExecuting(ActionExecutingContext filterContext) {
//Do the default OnActionExecuting first.
base.OnActionExecuting(filterContext);
//The request variable will allow you to see information on the current request.
var request = filterContext.RequestContext.HttpRequest;
}
If you want to access this in every controller, then you should probably create a base controller and add this there.
public class BaseController : Controller
{
//Code above
}
And in your Home controller:
public class HomeController : BaseController
{
}
If you are working in ASP.NET MVC 3 I would recommend using global action filters (use one per "event" you want to handle) instead of tapping directly into the ASP.NET Application/Request stack.
I could really do with updating a user's session variables from within my HTTPModule, but from what I can see, it isn't possible.
UPDATE: My code is currently running inside the OnBeginRequest () event handler.
UPDATE: Following advice received so far, I tried adding this to the Init () routine in my HTTPModule:
AddHandler context.PreRequestHandlerExecute, AddressOf OnPreRequestHandlerExecute
But in my OnPreRequestHandlerExecute routine, the session state is still unavailable!
Thanks, and apologies if I'm missing something!
Found this over on the ASP.NET forums:
using System;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Diagnostics;
// This code demonstrates how to make session state available in HttpModule,
// regardless of requested resource.
// author: Tomasz Jastrzebski
public class MyHttpModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.PostAcquireRequestState += new EventHandler(Application_PostAcquireRequestState);
application.PostMapRequestHandler += new EventHandler(Application_PostMapRequestHandler);
}
void Application_PostMapRequestHandler(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
if (app.Context.Handler is IReadOnlySessionState || app.Context.Handler is IRequiresSessionState) {
// no need to replace the current handler
return;
}
// swap the current handler
app.Context.Handler = new MyHttpHandler(app.Context.Handler);
}
void Application_PostAcquireRequestState(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
MyHttpHandler resourceHttpHandler = HttpContext.Current.Handler as MyHttpHandler;
if (resourceHttpHandler != null) {
// set the original handler back
HttpContext.Current.Handler = resourceHttpHandler.OriginalHandler;
}
// -> at this point session state should be available
Debug.Assert(app.Session != null, "it did not work :(");
}
public void Dispose()
{
}
// a temp handler used to force the SessionStateModule to load session state
public class MyHttpHandler : IHttpHandler, IRequiresSessionState
{
internal readonly IHttpHandler OriginalHandler;
public MyHttpHandler(IHttpHandler originalHandler)
{
OriginalHandler = originalHandler;
}
public void ProcessRequest(HttpContext context)
{
// do not worry, ProcessRequest() will not be called, but let's be safe
throw new InvalidOperationException("MyHttpHandler cannot process requests.");
}
public bool IsReusable
{
// IsReusable must be set to false since class has a member!
get { return false; }
}
}
}
HttpContext.Current.Session should Just Work, assuming your HTTP Module isn't handling any pipeline events that occur prior to the session state being initialized...
EDIT, after clarification in comments: when handling the BeginRequest event, the Session object will indeed still be null/Nothing, as it hasn't been initialized by the ASP.NET runtime yet. To work around this, move your handling code to an event that occurs after PostAcquireRequestState -- I like PreRequestHandlerExecute for that myself, as all low-level work is pretty much done at this stage, but you still pre-empt any normal processing.
Accessing the HttpContext.Current.Session in a IHttpModule can be done in the PreRequestHandlerExecute handler.
PreRequestHandlerExecute: "Occurs just before ASP.NET starts executing an event handler (for example, a page or an XML Web service)." This means that before an 'aspx' page is served this event gets executed. The 'session state' is available so you can knock yourself out.
Example:
public class SessionModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += BeginTransaction;
context.EndRequest += CommitAndCloseSession;
context.PreRequestHandlerExecute += PreRequestHandlerExecute;
}
public void Dispose() { }
public void PreRequestHandlerExecute(object sender, EventArgs e)
{
var context = ((HttpApplication)sender).Context;
context.Session["some_sesion"] = new SomeObject();
}
...
}
If you're writing a normal, basic HttpModule in a managed application that you want to apply to asp.net requests through pages or handlers, you just have to make sure you're using an event in the lifecycle after session creation. PreRequestHandlerExecute instead of Begin_Request is usually where I go. mdb has it right in his edit.
The longer code snippet originally listed as answering the question works, but is complicated and broader than the initial question. It will handle the case when the content is coming from something that doesn't have an ASP.net handler available where you can implement the IRequiresSessionState interface, thus triggering the session mechanism to make it available. (Like a static gif file on disk). It's basically setting a dummy handler that then just implements that interface to make the session available.
If you just want the session for your code, just pick the right event to handle in your module.
Since .NET 4.0 there is no need for this hack with IHttpHandler to load Session state (like one in most upvoted answer). There is a method HttpContext.SetSessionStateBehavior to define needed session behaviour.
If Session is needed on all requests set runAllManagedModulesForAllRequests to true in web.config HttpModule declaration, but be aware that there is a significant performance cost running all modules for all requests, so be sure to use preCondition="managedHandler" if you don't need Session for all requests.
For future readers here is a complete example:
web.config declaration - invoking HttpModule for all requests:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="ModuleWithSessionAccess" type="HttpModuleWithSessionAccess.ModuleWithSessionAccess, HttpModuleWithSessionAccess"/>
</modules>
</system.webServer>
web.config declaration - invoking HttpModule only for managed requests:
<system.webServer>
<modules>
<add name="ModuleWithSessionAccess" type="HttpModuleWithSessionAccess.ModuleWithSessionAccess, HttpModuleWithSessionAccess" preCondition="managedHandler"/>
</modules>
</system.webServer>
IHttpModule implementation:
namespace HttpModuleWithSessionAccess
{
public class ModuleWithSessionAccess : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += Context_BeginRequest;
context.PreRequestHandlerExecute += Context_PreRequestHandlerExecute;
}
private void Context_BeginRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
app.Context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
private void Context_PreRequestHandlerExecute(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
if (app.Context.Session != null)
{
app.Context.Session["Random"] = $"Random value: {new Random().Next()}";
}
}
public void Dispose()
{
}
}
}
Try it: in class MyHttpModule declare:
private HttpApplication contextapp;
Then:
public void Init(HttpApplication application)
{
//Must be after AcquireRequestState - the session exist after RequestState
application.PostAcquireRequestState += new EventHandler(MyNewEvent);
this.contextapp=application;
}
And so, in another method (the event) in the same class:
public void MyNewEvent(object sender, EventArgs e)
{
//A example...
if(contextoapp.Context.Session != null)
{
this.contextapp.Context.Session.Timeout=30;
System.Diagnostics.Debug.WriteLine("Timeout changed");
}
}