Handling exceptions in global.asax ASP.NET MVC - asp.net

I have code which is catching all exceptions in Global.asax
protected void Application_Error(object sender, EventArgs e)
{
System.Web.HttpContext context = HttpContext.Current;
System.Exception exc = context.Server.GetLastError();
var ip = context.Request.ServerVariables["REMOTE_ADDR"];
var url = context.Request.Url.ToString();
var msg = exc.Message.ToString();
var stack = exc.StackTrace.ToString();
}
How I can get controller name where this error happened
How I can get request client IP?
And can I filter exceptions? I dont need 404, 504.... erors
thanks

Global.asax has not notion of controllers and actions, so I believe there is no an API for retrieving controller and action names. However you might give a try for resolving request URL:
HttpContextBase currentContext = new HttpContextWrapper(HttpContext.Current);
UrlHelper urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
RouteData routeData = urlHelper.RouteCollection.GetRouteData(currentContext);
string action = routeData.Values["action"] as string;
string controller = routeData.Values["controller"] as string;
To get the user IP you can use UserHostAddress property:
string userIP = HttpContext.Current.Request.UserHostAddress;
To filter out HTTP exceptions that you are not going to handle you can use something like:
HttpException httpException = exception as HttpException;
if (httpException != null)
{
switch (httpException.GetHttpCode())
{
case 404:
case 504:
return;
}
}
One last remark about exception handling - it is not a best practice to do it at global level when there is a way to perform it more locally. For instance in ASP.NET MVC base Controller class has a method:
protected virtual void OnException(ExceptionContext filterContext)
which, when overridden, will give you full control on the occurred exception. You can have all the info that is available for you in Global.asax plus ASP.NET MVC specific features like a reference to controller, view context, route data etc.

i used like this it's below
you can get user ip like this
var userip = context.Request.UserAgent;
and you can get your url where this error happened like this
var ururl = System.Web.HttpContext.Current.Request.Url;
i think this will help you...

I'd take a different tack and put use an attribute on your controllers (or base controller if you have one)
public class HandleErrorAttributeCustom : HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
//you can get controller by using
context.Controller.GetType()
//however, I'd recommend pluggin in Elmah here instead
//as it gives this easily and also can has filtering
//options that you want
}
}

Related

Is there a way to run request again from MVC filter

Is there a way to run request again from MVC exception filter?
I'd like to call the request again when I'm handling the exception in exception filter.
Lets suppose we have the following action and we have defined a custom exception filter as shown below. Now this action will generate error if the id is not provided or a string value is provided to the id.
[CustomExceptionFilter]
public string Welcome(int id)
{
return id.ToString();
}
Now as the action throws error. The following exception filter will handle the rest of the situation. What I have done here in this exception filter is that this will pick the controller name and action name from the RouteData, generate a random value and redirect back to this action.
This is a mere example that came to my mind, of course you can try different things with it.
public class CustomExceptionFilter : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (!filterContext.ExceptionHandled)
{
var rd = HttpContext.Current.Request.RequestContext.RouteData;
string controllerName = rd.GetRequiredString("controller");
string actionName = rd.GetRequiredString("action");
filterContext.Result = new RedirectResult($"/{controllerName}/{actionName}/{ new Random().Next(100, 200) }");
filterContext.ExceptionHandled = true;
}
}
}

Response.Redirect() Turns Response.RedirectLocation NULL

I have a group of Asp.Net applications that all share a common HttpModule that contains some code like this:
public class MyModule : IHttpModule
{
public void Dispose()
{
}
public void Init( HttpApplication context )
{
context.Error += new EventHandler( context_Error );
}
private void context_Error( object sender, EventArgs e )
{
var lastError = HttpContext.Current.Server.GetLastError();
doSomeStuffWithError(lastError);
var response = HttpContext.Current.Response;
var redirect = GetErrorRedirect(lastError);
response.Redirect(redirect, true);
}
}
This works totally fine for all of my applications except for one. In the case of the one that doesn't work correctly, response.Redirect(...) doesn't seem to work. Instead of the redirect I expect, Asp.Net is redirecting to its standard error page. I've checked the configuration of this application and don't see anything wrong or significantly different from the other applications.
While investigating this issue, I modified the error handler with one more line of code as follows:
private void context_Error( object sender, EventArgs e )
{
var lastError = HttpContext.Current.Server.GetLastError();
doSomeStuffWithError(lastError);
var response = HttpContext.Current.Response;
var redirect = GetErrorRedirect(lastError);
//setting a break point here, i've verified that 'redirect' has a value in all cases
response.Redirect(redirect, true);
var wtf = response.RedirectLocation;
//inspecting the value of 'wtf' here shows that it is null for one application, but equal to 'redirect' in all others.
}
When I set a break point on 'wtf' I'm seeing some strange behavior. For applications that work, wtf contains the same value as redirect. However, for my app that isn't working, wtf is null.
Anyone have any ideas on what would cause wtf to be null in this way?
The overload of Response.Redirect you are using will call Response.End and throw a ThreadAbortException. It says so in the documentation. So, the fact that "it works" in other applications is interesting as it should never execute var wtf = response.RedirectLocation; During a debugging session, it is not surprising that it's null, either, as there is likely some reason that it allows to execute that line during debug.
In addition, it will of course execute the default error page if you have the mode set to On or RemoteOnly for the <customErrors> setting in Web.config unless you clear the error before redirecting. This is by design.
If you need to execute additional code after you have already called Response.Redirect, pass false as the second parameter to avoid the Response.End call and clear the error using HttpContext.Current.ClearError().
Based on your example, I would rewrite your HttpModule like so:
public class MyModule : IHttpModule
{
public void Dispose()
{
}
public void Init( HttpApplication context )
{
context.Error += new EventHandler( context_Error );
}
private void context_Error( object sender, EventArgs e )
{
var context = HttpContext.Current;
var lastError = context.Server.GetLastError();
doSomeStuffWithError(lastError);
var response = context.Response;
var redirect = GetErrorRedirect(lastError);
context.ClearError();
// pass true if execution must stop here
response.Redirect(redirect, false);
// do other stuff here if you pass false in redirect
}
}

Call the default asp.net HttpHandler from a custom handler

I'm adding ASP.NET routing to an older webforms app. I'm using a custom HttpHandler to process everything. In some situations I would like to map a particular path back to an aspx file, so I need to just pass control back to the default HttpHandler for asp.net.
The closest I've gotten is this
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
}
It doesn't do anything, there's nothing output to the stream. MS's documentation for System.Web.UI.Page (as an IHttpHandler) say something to the effect of "do not call the ProcessRequest method. It's for internal use."
From looking around it seems like you can do this with MVC, e.g. : MvcHttpHandler doesn't seem to implement IHttpHandler
There is also this thing System.Web.UI.PageHandlerFactory which appears that it would just produce a Page handler for an aspx file, but it's internal and I can't use it directly.
This page: http://msdn.microsoft.com/en-us/library/bb398986.aspx refers to the "default asp.net handler" but does not identify a class or give any indication how one might use it.
Any ideas on how I can do this? Is it possible?
Persistence pays off! This actually works, and since this information seems to be available pretty much nowhere I thought I'd answer my own question. Thanks to Robert for this post on instantiating things with internal constructors, this is the key.
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's 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);
}
... and like some small miracle, an aspx page processes & renders completely in-process without the need to redirect.
I expect this will only work in IIS7.
I'm you're using Routing in webforms you should be able to just add an ignore route for the specific .aspx files you want. This will then be handled by the default HttpHandler.
http://msdn.microsoft.com/en-us/library/dd505203.aspx
Another option is to invert the logic by handling the cases in which you do NOT want to return the default response and remap the others to your own IHttpHandler. Whenever myCondition is false, the response will be the "default". The switch is implemented as an 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; }
}

Best way to abort/cancel action and response from ActionFilter

Best way to abort/cancel action from ActionFilter
I've got this ActionFilter, and it's suppose to end the connection immediately and return a 401 Unauthroized:
public class SignInRequired : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// User is verified, continue executing action
if (Acme.Web.CurrentUser != null)
{
return;
}
// End response with 401 Unauthorized
var response = HttpContext.Current.Response;
response.StatusCode = (int)HttpStatusCode.Unauthorized;
response.End();
// Prevent the action from actually being executed
filterContext.Result = new EmptyResult();
}
}
I learned how you can cancel the action from executing by setting 'context.Result = new EmptyResult()` here, but I'm not sure if this is the best way to flush the response and close the connection.
Setting the response will mean the action doesn't get called.
public override void OnActionExecuting(HttpActionContext actionContext)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
As other answers have said, though, authentication should be done with an AuthorizeAttribute (Docs for Web.API or for MVC).
On .net core 2.2, 3.0 and 3.1 and .net 5 the below example works fine
public override void OnActionExecuting(ActionExecutingContext context)
{
context.Result = new UnauthorizedObjectResult("user is unauthorized");
}
The answer that #OdeyinkaOlubunmi is correct for Web API or specifically System.Web.Http.Filters.ActionFilterAttribute but it can't be used for System.Web.Mvc.ActionFilterAttribute. AuthorizeAttribute and overriding AuthorizeCore is a good way to go but if you use #Vadim's example for a GlobalFilter you will end up with the following error in a standard configuration:
HTTP Error 404.15 - Not Found The request filtering module is
configured to deny a request where the query string is too long.
This is because the default /Login?ReturnUrl= will keep appending new values until the query string causes an exception.
The way I have solved it for MVC is like this:
public class DebugActionFilter : System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext actionContext)
{
actionContext.Result = new HttpStatusCodeResult(HttpStatusCode.Unauthorized);
return;
}
}
You can set the result of filterContext for the Exception page like this:
filterContext.Result = new RedirectResult("~/Error/Unauthorized");
See more details here on section Canceling Filter Execution
You probably want to make it an AuthorizeAttribute. That will set the result to be an UnAuthorizedResult automatically, plus it has the benefit of being run before any other filters. Alternatively you can set the Result to be a new HttpUnauthorizedResult
public class SignInRequiredAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return !Acme.Web.CurrentUser != null;
}
}
using .net core 2.1 the solutions above did not work for me , so i tried this and it worked :-
context.HttpContext.Response.StatusCode = 401;
return;
if there is better solutions for .net core 2.1 i am open for suggestions

Disable Session state per-request in ASP.Net MVC

I am creating an ActionResult in ASP.Net MVC to serve images. With Session state enabled, IIS will only handle one request at a time from the same user. (This is true not just in MVC.)
Therefore, on a page with multiple images calling back to this Action, only one image request can be handled at a time. It's synchronous.
I'd like this image Action to be asynchronous -- I'd like multiple image requests to each execute without needing the previous one to complete. (If the images were just static files, IIS would serve them up this way.)
So, I'd like to disable Session just for calls to that Action, or to specify that certain requests do not have Session state. Anyone know how this is done in MVC? Thanks!
If anyone is in the situation I was in, where your image controller actually needs read only access to the session, you can put the SessionState attribute on your controller
[SessionState(SessionStateBehavior.ReadOnly)]
See http://msdn.microsoft.com/en-us/library/system.web.mvc.sessionstateattribute.aspx for more info.
Thanks to https://stackoverflow.com/a/4235006/372926
Rather than implementing an action filter for this, why don't you implement a RouteHandler?
Here's the deal - IRouteHandler has one method - GetHttpHandler. When you make an ASP.Net MVC request to a controller, by default the routing engine handles the request by creating a new instance of MvcRouteHandler, which returns an MvcHandler. MvcHandler is an implementation of IHttpHandler which is marked with the (surprise!) IRequiresSessionState interface. This is why a normal request uses Session.
If you follow my blog post on how to implement a custom RouteHandler (instead of using MvcRouteHandler) for serving up images - you can skip returning a session-tagged IHttpHandler.
This should free IIS from imposing synchronicity on you. It would also likely be more performant because it's skipping all the layers of the MVC code dealing with filters.
I also came across the same problem and after doing R&D this link worked for me
Reference:
https://techatfingers.wordpress.com/2016/06/14/session-state-on-action/
Create custom Attribute
Override the “GetControllerSessionBehavior” method present in class DefaultControllerFactory.
Register it in global.aspx
1> Create custom Attribute
public sealed class ActionSessionStateAttribute : Attribute
{
public SessionStateBehavior SessionBehavior { get; private set; }
public ActionSessionStateAttribute(SessionStateBehavior sessionBehavior)
{
SessionBehavior = sessioBehavior;
}
}
2. Override
public class SessionControllerFactory : DefaultControllerFactory
{
protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return SessionStateBehavior.Default;
var actionName = requestContext.RouteData.Values["action"].ToString();
Type typeOfRequest=requestContext.HttpContext.Request.RequestType.ToLower() =="get"?typeof(HttpGetAttribute):typeof(HttpPostAttribute);
// [Line1]
var cntMethods = controllerType.GetMethods()
.Where(m =>
m.Name == actionName &&
( ( typeOfRequest == typeof(HttpPostAttribute) &&
m.CustomAttributes.Where(a => a.AttributeType == typeOfRequest).Count()>0
)
||
( typeOfRequest == typeof(HttpGetAttribute) &&
m.CustomAttributes.Where(a => a.AttributeType == typeof(HttpPostAttribute)).Count() == 0
)
)
);
MethodInfo actionMethodInfo = actionMethodInfo = cntMethods != null && cntMethods.Count() == 1 ? cntMethods.ElementAt(0):null;
if (actionMethodInfo != null)
{
var sessionStateAttr = actionMethodInfo.GetCustomAttributes(typeof(ActionSessionStateAttribute), false)
.OfType<ActionSessionStateAttribute>()
.FirstOrDefault();
if (sessionStateAttr != null)
{
return sessionStateAttr.Behavior;
}
}
return base.GetControllerSessionBehavior(requestContext, controllerType);
}
3. Register class in Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// --- other code ---
ControllerBuilder.Current.SetControllerFactory(typeof(SessionControllerFactory));
}
}
Try serving the images from another domain. So something like images.mysite.com.
This will provide you two benefits: One, sessions are tracked by a cookie, so images.mysite.com won't have the cookie. Two, it will give you an additional two concurrent requests to retrieve images.
Have you considered setting up a HttpHandler to serve up your images?
SessionState attribute is quite helpful if u use mvc3. How to achieve this with mvc2 needs a little more coding.
Idea is to tell the asp.net that specific request wont use session object.
So, Create a custom route handler for specific requests
public class CustomRouteHandler : IRouteHandler
{
public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.HttpContext.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.ReadOnly);
return new MvcHandler(requestContext);
}
}
SessionStateBehavior enum has 4 members, you should use "disabled" or "readonly" modes to get async behavior.
After creating this custom route handler, be sure that your specific requests goes through this handler. This can be done via defining new routes at Global.asax
routes.Add("Default", new Route(
"{controller}/{action}",
new RouteValueDictionary(new { controller = "Home", action = "Index"}),
new CustomRouteHandler()
));
Adding this route makes all your requests to be handled by your custom route handler class. You can make it specific by defining different routes.
Change DefaultCOntrollerFactory to custom ControllerFactory class. Default Controller.TempDataProvider use SessionStateTempDataProvider. you can change it.
1.Set web.config/system.web/sessionState:mode="Off".
2.create DictionaryTempDataProvider class.
public class DictionaryTempDataProvider : ITempDataProvider
{
public IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
return new Dictionary<string, object>();
}
public void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
}
}
3.Create DictionaryTempDataControllerFactory
public class DictionaryTempDataControllerFactory : DefaultControllerFactory
{
public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
var controller = base.CreateController(requestContext, controllerName) as Controller;
if (controller!=null)
controller.TempDataProvider = new DictionaryTempDataProvider();
return controller;
}
}
4.In global.asax.cs Apprication_Start event set DictionaryTempDataControllerFactory.
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(
new DictionaryTempDataControllerFactory()
);
}
On our server, IIS doesn't even know about sessions - it's the ASP.NET stack that handles one request per session at a time. Static files, like images, are never affected.
Is it possible that your ASP.NET app is serving the files instead of IIS?
Create new Controller
Decorate controler with [SessionState(SessionStateBehavior.Disabled)]
Refactor code you want seesion stated disabled for to that controller
I would to share my solution for disable ASP.NET Session for an specific request (in my case, a WCF Service) using an HttpModule:
public class AspNetSessionFilterModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PostMapRequestHandler += OnPostMapRequestHandler;
}
private void OnPostMapRequestHandler(object sender, EventArgs e)
{
var context = (sender as HttpApplication).Context;
DisableSessionForSomeRequests(context);
}
private void DisableSessionForSomeRequests(HttpContext context)
{
if ("~/Services/MyService.svc".Equals(context.Request.AppRelativeCurrentExecutionFilePath, StringComparison.InvariantCultureIgnoreCase))
{
context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Disabled);
}
}
public void Dispose()
{ }
}

Resources