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>
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 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.
I have an HttpHandler that I'm trying to use to put a little security layer over a certain directory in my site, but it's behaving strangely.
I've got it registered like this in my Web.Config: no longer valid since I'm in IIS 7.5
<httpHandlers>
<add verb="*" path="/courses/*" type="CoursesAuthenticationHandler" />
I can't tell if it's actually being called or not, because regardless of the code, it always seems to do nothing. On the flip side, if there are any errors in the code, it does show me an error page until I've corrected the error.
Here's the handler itself:
using System;
using System.Web;
public class CoursesAuthenticationHandler : IHttpHandler
{
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
if (!context.Request.IsAuthenticated)
context.Response.Redirect("/");
}
}
So... that's pretty much it. The handler is being registered and analyzed at compile time, but doesn't actually do what it's expected to.
Edit: I realized that I'm using IIS 7.5 and that does indeed have an impact on this implementation.
For IIS 7, here's the Web.Config registration I used:
<handlers accessPolicy="Read, Execute, Script">
<add name="CoursesAuthenticationHandler"
verb="*"
path="/courses/*"
type="CoursesAuthenticationHandler"
resourceType="Unspecified" />
Edit 2: Progress! When not logged in, requests made to the /courses/ directory are redirected to the login page. However, authenticated requests to the /courses/ directory return empty pages...
Edit 3: Per #PatrickHofman's suggestion, I've switched to using an HttpModule.
The Web.Config registration:
<modules>
<add name="CourseAuthenticationModule" type="CourseAuthenticationModule" />
The code:
using System;
using System.Web;
public class CourseAuthenticationModule : IHttpModule
{
public void Dispose() { }
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(BeginRequest);
}
public void BeginRequest(Object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
HttpContext context = app.Context;
HttpRequest request = context.Request;
HttpResponse response = context.Response;
if (request.Path.ToLower().StartsWith("/courses/") && !request.IsAuthenticated)
{
response.Redirect("/");
}
}
}
Now the problem is that !request.IsAuthenticated is always false. If I'm logged in, and navigate to the /courses/ directory, I'm redirected to the homepage.
What's the deal?
I think the last problem lies in the fact that a HttpHander handles stuff. It is the end point of a request.
Since you didn't add anything to the request, the response will end up empty.
Are you looking for HttpModules? They can be stacked.
As a possible solution when only files are necessary: read the files yourself in the request by either reading and writing to response or use TransmitFile. For ASP.NET pages you need modules.
Is there a way to write some code that would be executed for each request to a .aspx or a .cshtml page in asp.net 4.5 apart from using a base page class. it is a very huge project and making changes to all pages to use a base page is a nightmare. Also i am not sure how would this be done for a cshtml page since they don't have a class.
Can we use the Application_BeginRequest and target only the aspx and cshtml files since the website is running in integrated mode.?
basically, i have to check if a user who is accessing the website has a specific ip address against a database and if yes then allow access otherwise redirect.
we are using IIS8 and ASP.Net 4.5 and ASP.Net Razor Web Pages
Also i am not sure how would this be done for a cshtml page since they don't have a class.
You could place a _ViewStart.cshtml file whose contents will get executed on each request.
Alternatively you could write a custom Http Module:
public class MyModule: IHttpModule
{
public void Init(HttpApplication app)
{
app.BeginRequest += new EventHandler(OnBeginRequest);
}
public void Dispose()
{
}
public void OnBeginRequest(object s, EventArgs e)
{
// this code here's gonna get executed on each request
}
}
and then simply register this module in your web.config:
<system.webServer>
<modules>
<add name="MyModule" type="SomeNamespace.MyModule, SomeAssembly" />
</modules>
...
</system.webServer>
or if you are running in Classic Mode:
<system.web>
<httpModules>
<add name="MyModule" type="SomeNamespace.MyModule, SomeAssembly" />
</httpModules>
</system.web>
basically, i have to check if a user who is accessing the website has
a specific ip address against a database and if yes then allow access
otherwise redirect.
Inside the OnBeginRequest method you could get the current user IP:
public void OnBeginRequest(object sender, EventArgs e)
{
var app = sender as HttpApplication;
var request = app.Context.Request;
string ip = request.UserHostAddress;
// do your checks against the database
}
Asp.net MVC filters are especially designed for that purpose.
You would implement ActionFilterAttribute like this (maybe put this new class in a Filters folder in your webapp solution):
public class IpFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string ip = filterContext.HttpContext.Request.UserHostAddress;
if(!testIp(ip))
{
if (true /* You want to use a route name*/)
filterContext.Result = new RedirectToRouteResult("badIpRouteName");
else /* you want an url */
filterContext.Result = new RedirectResult("~/badIpController/badIpAction");
}
base.OnActionExecuting(filterContext);
}
private bool testIp(string inputIp)
{
return true /* do you ip test here */;
}
}
Then you have to decorate any action that would perform the ipcheck with IpFilter like so :
[IpFilter]
public ActionResult AnyActionWhichNeedsGoodIp()
{
/* do stuff */
}
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.