Edit
Got the answer here
So I wanted to check out MiniProfiler to troubleshoot some performance issues.
Before using it on production code I wanted to try it out with the a sample so went ahead with creating a MVC 5 application. This is plain vanilla app that gets created with the template.
Added this code in the Index() method of HomeController:
var profiler = MiniProfiler.Current;
using (profiler.Step("Set page title"))
{
ViewBag.Title = "Home Page";
}
using (profiler.Step("Doing complex stuff"))
{
using (profiler.Step("Step A"))
{ // something more interesting here
Thread.Sleep(100);
}
using (profiler.Step("Step B"))
{ // and here
Thread.Sleep(250);
}
}
return View();
Added this line below the jquery bundle in _Layout:
#Scripts.Render("~/bundles/jquery")
#StackExchange.Profiling.MiniProfiler.RenderIncludes()
#Scripts.Render("~/bundles/bootstrap")
#RenderSection("scripts", required: false)
Ran the app.
Nothing shows up. No profiling, nothing.
What am I missing?
Regards.
This is what I had to do to get MiniProfiler working in my ASP.NET MVC5 project:
Installed the MiniProfiler and MiniProfiler.MVC4 NuGet packages (the MVC4 package supports MVC5)
Add the following to Application_Start() in Global.asax:
protected void Application_Start()
{
...
// Setup profiler for Controllers via a Global ActionFilter
GlobalFilters.Filters.Add(new ProfilingActionFilter());
// initialize automatic view profiling
var copy = ViewEngines.Engines.ToList();
ViewEngines.Engines.Clear();
foreach (var item in copy)
{
ViewEngines.Engines.Add(new ProfilingViewEngine(item));
}
}
Add the following to 'Application_BeginRequest()' and 'Application_EndRequest()', also in Global.asax:
protected void Application_BeginRequest()
{
if (Request.IsLocal)
{
MiniProfiler.Start();
}
}
protected void Application_EndRequest()
{
MiniProfiler.Stop();
}
Add the following to _Layout.cshtml (just before the </body> tag):
...
#StackExchange.Profiling.MiniProfiler.RenderIncludes()
</body>
</html>
Add the following to the <handlers> section of Web.config:
<system.webServer>
...
<handlers>
...
<add name="MiniProfiler" path="mini-profiler-resources/*" verb="*"
type="System.Web.Routing.UrlRoutingModule" resourceType="Unspecified"
preCondition="integratedMode" />
...
</handlers>
</system.webServer>
That was enough to profile each of the MVC Controller Actions and Views.
In my particular project I was using Entity Framework 6, so I also did the following:
a) Installed the MiniProfiler.EF6 package
b) Added the following to the end of Application_Start() in Global.asax:
...
MiniProfilerEF6.Initialize();
}
Also you have to add call:
MiniProfiler.Start();
In Global.asax.cs to Application_BeginRequest event.
And:
MiniProfiler.Stop();
In Global.asax.cs to Application_EndRequest event.
Related
I am trying to secure a folder under my project that just has some static files, a combination of .htm and .js files. I have tried creating a custom HttpHandler like:
public class StaticFilesHttpHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.Request.IsAuthenticated)
{
// continue with the request
}
else
{
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
}
public bool IsReusable => false;
}
Then register it to be used with a route via Route.Config
routes.RouteExistingFiles = true;
routes.Add("helpRoute", new Route("folder/*.htm", new StaticFilesRouteHandler ()));
and a route handler to provide the
public class StaticFilesRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext context)
{
return new StaticFilesHttpHandler ();
}
}
and also via web.config under system.webServer
<handlers>
<add name="StaticFileHandler" verb="GET" path="~/help/default.htm" type="StaticFilesHttpHandler "/>
</handlers>
Files in the folder are provided by a 3rd party. I am to call a function inside a js file in the folder which then redirects the user to a proper .htm file inside it's sub structure. I do not want users to be able to type the url and access any of the files. What am I doing wrong?
can you change the type to TransferRequestHandler and make sure your path is correct.
<handlers>
<add name="StaticFileHandler" verb="GET" path="~/help/default.htm" type="TransferRequestHandler" />
</handlers>
in your global.asax file you can access the request in Application_BeginRequest to verify if the request is authenticated or not.
My application use MVC4, Entity Framework 6. I want custom Actions return to page error (500, 404, 403) when an error occurs use Filter Action on MVC.
Currently, I'm using Application_Error method in file Global.asax to return page error, but it not working when action call from AJAX.
Ex:
This is page
[ExecuteCustomError]
public ActionResult TestAction()
{
Rerurn View();
}
This is view returned after AJAX call
[ExecuteCustomError]
public ActionResult ReturnView()
{
//If return error code, i have return message error here.
return PartialView();
}
Looks like you haven't provided correct path of your error page.
Like you need to add your error page in shared view folder then you can access this page.
If your page is in the other folder then you have to specify the correct path of your error view page.
Like below :
return PartialView("~/Views/ErrorPartialView.cshtml", myModel);
We have other options to call error page through web. In Config file you can do the below settings :
<configuration>
...
<system.webServer>
...
<httpErrors errorMode="Custom" existingResponse="Replace">
<clear />
<error statusCode="400" responseMode="ExecuteURL" path="/ServerError.aspx"/>
<error statusCode="403" responseMode="ExecuteURL" path="/ServerError.aspx" />
<error statusCode="404" responseMode="ExecuteURL" path="/PageNotFound.aspx" />
<error statusCode="500" responseMode="ExecuteURL" path="/ServerError.aspx" />
</httpErrors>
...
</system.webServer>
...
</configuration>
Here we go for Global Exception Filter :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MVCGlobalFilter.Filters
{
public class ExecuteCustomErrorHandler : ActionFilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
Exception e = filterContext.Exception;
filterContext.ExceptionHandled = true;
filterContext.Result = new ViewResult()
{
ViewName = "CommonExceptionPage"
};
}
}
}
Now you have to register your ExecuteCustomErrorHandler class in Global.asax file :
/// <summary>
/// Application start event
/// </summary>
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
log4net.Config.XmlConfigurator.Configure();
// Calling Global action filter
GlobalFilters.Filters.Add(new ExecuteCustomErrorHandler());
}
You need to add CommonExceptionPage view in Shared folder :
CommonExceptionPage.cshtml :
#{
ViewBag.Title = "Execute Custom Error Handler";
}
<hgroup class="title">
<h1 class="error">Error.</h1>
<h2 class="error">An error occurred while processing your request.</h2>
</hgroup>
I am using a custom authorize attribute in a ASP.NET MVC 5 application like following:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext context)
{
if (context.HttpContext.Request.IsAuthenticated)
{
context.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
}
else
{
base.HandleUnauthorizedRequest(context);
}
}
}
In system.web section of my web.config I mentioned error paths like:
<system.web>
<customErrors mode="On" defaultRedirect="/Error/Error">
<error statusCode="403" redirect="/Error/NoPermissions"/>
</customErrors>
</system.web>
But I am never redirected to my custom error page at /Error/NoPermissions. Instead the browser display the general error page saying "HTTP Error 403.0 - Forbidden".
[1]: Remove all 'customErrors' & 'httpErrors' from Web.config
[2]: Check 'App_Start/FilterConfig.cs' looks like this:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
[3]: in 'Global.asax' add this method:
public void Application_Error(Object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
Server.ClearError();
var routeData = new RouteData();
routeData.Values.Add("controller", "ErrorPage");
routeData.Values.Add("action", "Error");
routeData.Values.Add("exception", exception);
if (exception.GetType() == typeof(HttpException))
{
routeData.Values.Add("statusCode", ((HttpException)exception).GetHttpCode());
}
else
{
routeData.Values.Add("statusCode", 500);
}
Response.TrySkipIisCustomErrors = true;
IController controller = new ErrorPageController();
controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
Response.End();
}
[4]: Add 'Controllers/ErrorPageController.cs'
public class ErrorPageController : Controller
{
public ActionResult Error(int statusCode, Exception exception)
{
Response.StatusCode = statusCode;
ViewBag.StatusCode = statusCode + " Error";
return View();
}
}
[5]: in 'Views/Shared/Error.cshtml'
#model System.Web.Mvc.HandleErrorInfo
#{
ViewBag.Title = (!String.IsNullOrEmpty(ViewBag.StatusCode)) ? ViewBag.StatusCode : "500 Error";
}
<h1 class="error">#(!String.IsNullOrEmpty(ViewBag.StatusCode) ? ViewBag.StatusCode : "500 Error"):</h1>
//#Model.ActionName
//#Model.ControllerName
//#Model.Exception.Message
//#Model.Exception.StackTrace
:D
Thanks everyone, but problem is not with 403 code. Actually the problem was with the way i was trying to return 403. I just changed my code to throw an HttpException instead of returning the HttpStatusCodeResult and every things works now. I can return any HTTP status code by throwing HttpException exception and my customErrors configuration catches all of them. May be HttpStatusCodeResult is not doing the exact job I expected it to do.
I just replaced
context.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
with
throw new HttpException((int)System.Net.HttpStatusCode.Forbidden, "Forbidden");
That's it.
Happy coding.
I also had this issue. Code in the OP’s question is perfectly working except the custom error code in <system.web> section in the web.config file. To fix the issue what I need to do was add the following code to <system.webServer>. Note that ‘webserver’ instead of ‘web’.
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="403" />
<error statusCode="403" responseMode="ExecuteURL" path="/Error/UnAuthorized" />
</httpErrors>
If someone is using following environment, here is the complete solution:
The Environment:
Visual Studio 2013 Update 4
Microsoft .NET Framework 4.5.1 with ASP.NET MVC 5
Project: ASP.NET Web Application with MVC & Authentication: Individual User Account template
Custom Attribute class:
Add the following class to your web site’s default namespace. The reason explained here in the accepted answer Stack Overflow question: Why does AuthorizeAttribute redirect to the login page for authentication and authorization failures?
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
}
Then add the following code the web.config file
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="403" />
<error statusCode="403" responseMode="ExecuteURL" path="/Error/UnAuthorized" />
</httpErrors>
</system.webServer>
Following article explain more about this: ASP.NET MVC: Improving the Authorize Attribute (403 Forbidden)
And httpErrors in web.config section in this article: Demystifying ASP.NET MVC 5 Error Pages and Error Logging
Then add the ErrorController.cs to Controllers folder
public class ErrorController : Controller
{
// GET: UnAuthorized
public ActionResult UnAuthorized()
{
return View();
}
public ActionResult Error()
{
return View();
}
}
Then add a UnAuthorized.cshtml to View/Shared folder
#{
ViewBag.Title = "Your Request Unauthorized !"; //Customise as required
}
<h2>#ViewBag.Title.</h2>
This will show customised error page instead of browser generated error page.
Also note that for the above environment, it is not required to comment the code inside RegisterGlobalFilters method added by the template as suggested in one of the answers.
Please note that I just cut and paste code from my working project therefore I used Unauthorized instead OP’s NoPermissions in the above code.
These seem to be complicated workarounds. Here is basically all you need to do to get your custom error pages (CEP) working:
Add an Error Controller in your controllers folder.
Inside your Error Controller annotate the class with [HandleError].
For each error you want to display a CEP for, create an ActionResult method.
Create a View for each CEP inside ~/Views/Shared/Error folder, and customize it how you like. (The Error folder should have been created when you created your controller. If it did not, you will need to create the Error folder first.)
Open the web.config file at the root level *Note: there are two (2) web.config files. One in your Views folder, the other at the Root level of your application.
Inside <system.web>, add <customError mode="On" defaultRedirect="~/Error/Error">. Any statusCode you don't have a CEP for will be handled by the defaultRedirect.
For each error code you have a CEP for; add <error statusCode="[StatusCode]" redirect="~/Error/[CEP Name]">. You can leave off the file extension.
Controller Example:
namespace NAMESPACE_Name.Controllers
{
[HandleError]
public class ErrorController : Controller
{
// GET: Error
public ActionResult BadRequest()
{
return View();
}
public ActionResult Error()
{
return View();
}
public ActionResult Forbidden()
{
return View();
}
public ActionResult InternalServerError()
{
return View();
}
public ActionResult NotFound()
{
return View();
}
public ActionResult NotImplemented()
{
return View();
}
public ActionResult ServerBusyOrDown()
{
return View();
}
public ActionResult ServerUnavailable()
{
return View();
}
public ActionResult Timeout()
{
return View();
}
public ActionResult Unauthorized()
{
return View();
}
}
}
View Example:
#{
Layout = "~/Views/Shared/_FullWidthLayout.cshtml";
ViewBag.Title = "404 Error";
}
<div class="opensans margin-sides text-center">
<div class="text-center">
<h1 class="text-normal">Uh oh! Something went wrong!</h1>
<div class="img-container text-center">
<div class="centered">
<h1 class="bold">404 - Not Found</h1>
</div>
<img class="img text-center" src="~/Images/BackgroundImg.png" style="opacity: 0.15;" />
</div>
<p class="text-left">
This is usually the result of a broken link, a web page that has been moved or deleted, or a mistyped URL.
<ol class="text-left">
<li>Check the URL you entered in the address bar for typos,</li>
<li>If the address you entered is correct, the problem is on our end.</li>
<li>Please check back later as the resource you requested could be getting worked on,</li>
<li>However, if this continues for the resource you requested, please submit a trouble ticket.</li>
</ol>
</p>
</div>
</div>
Web.Config Example:
<customErrors mode="On" defaultRedirect="~/Error/Error">
<!--The defaultRedirect page will display for any error not listed below.-->
<error statusCode="400" redirect="~/Error/BadRequest"/>
<error statusCode="401" redirect="~/Error/Unauthorized"/>
<error statusCode="403" redirect="~/Error/Forbidden"/>
<error statusCode="404" redirect="~/Error/NotFound"/>
<error statusCode="408" redirect="~/Error/Timeout"/>
<error statusCode="500" redirect="~/Error/InternalServerError"/>
<error statusCode="501" redirect="~/Error/NotImplemented"/>
<error statusCode="502" redirect="~/Error/ServerUnavailable"/>
<error statusCode="503" redirect="~/Error/ServerBusyOrDown"/>
</customErrors>
That's it! Step-by-step solution to a problem that really shouldn't be a problem!
Again, any statusCode you don't have a CEP for, will be handled by the defaultRedirect page.
since I ran into a very similar issue I wanted to shed more light on it.
customErrors will only capture actual http exceptions thrown in your ASP.NET application. The HttpStatusCodeResult doesn't throw an exception though. It just writes a response with the according status code, which makes more sense in your example.
If you are running on IIS 7.0 or higher you should be using httpErrors now, as this will show you custom error pages in all cases. This is an IIS level setting.
I wrote a whole blog post about this to explain the differences:
http://dusted.codes/demystifying-aspnet-mvc-5-error-pages-and-error-logging
Update
You only need to do that special redirect for 403 errors. All other 500 errors should take effect through your defaultRedirect="/Error/Error" setting in customErrors. However, you need to remove or comment out the HandleErrorAttribute registration in the App_Start/FilterConfig.cs file for custom errors to actually work. Otherwise, that attribute will redirect all errors to the Error.cshtml file in the Views/Shared directory.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
// remove this line below
//filters.Add(new HandleErrorAttribute());
}
}
Original Answer
As far as I know, you cannot use customErrors in the web.config to handle 403 errors for some reason. I feel your pain as it seems like something that should be as simple as the code you already have, but apparently 403 errors are treated as a web server concern.
What you can do instead is just redirect the user to your desired "NoPermissions" page like this:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext context)
{
if (context.HttpContext.Request.IsAuthenticated)
{
context.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
action = "NoPermissions",
controller = "Error",
area = ""
}));
}
else
{
base.HandleUnauthorizedRequest(context);
}
}
}
The request will have a 200 status code instead of a 403, but if you can live with that, this is an easy workaround.
Here is a similar SO question for more info: Returning custom errors.
Also, this article explains how to go the IIS route: http://kitsula.com/Article/MVC-Custom-Error-Pages
I tried to do redirect from w/i httpmodule. I have the following code in place, and hooked up in the httpModules section. This Error event is fired as expected, but this didn't go to the /Error/Index page.
public class MyHttpModule : IHttpModule {
public void Dispose() { }
public void Init(HttpApplication context) {
context.Error += delegate {
var exception = context.Server.GetLastError();
context.Response.Clear();
context.Response.TrySkipIisCustomErrors = true;
context.Server.ClearError();
var routeData = new RouteData();
routeData.Values.Add("controller", "Error");
routeData.Values.Add("action", "Index");
IController errorController = new ErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(HttpContext.Current), routeData));
};
}
}
Please advice,
Thanks
I created a new ASP.NET MVC 2 and ASP.NET MVC 3 application.
I added a new HttpModule and copy/pasted your code.
I registered the new HttpModule in the Web.config.
<?xml version="1.0"?>
<configuration>
<system.web>
<httpModules>
<add name="MyHttpModule"
type="MvcApplication.MyHttpModule, MvcApplication" />
</httpModules>
</system.web>
</configuration>
I throw an exception in one of the action methods of my controller, e.g.:
throw new InvalidOperationException("An exception occured.");
Whenever an unhandled exception occurs I get redirected to the Error/Index page without a problem.
Have you correctly registered the HttpModule? I only get the described behavior if the module is not registered correctly. Keep in mind that for the Visual Studio Development Web Server (Cassini) and IIS 7.X you need to register the HttpModule in different sections of the Web.config.
For IIS 7.X please use:
<?xml version="1.0"?>
<configuration>
<system.webServer>
<modules>
<add name="MyHttpModule"
type="MvcApplication.MyHttpModule, MvcApplication" />
</modules>
</system.webServer>
</configuration>
Let's say I put the following code somewhere in a Master page in my ASP.NET MVC site:
throw new ApplicationException("TEST");
Even with a [HandleError] attribute placed on my controller, this exception still bubbles up. How can I deal with errors like this? I would like to be able to route to an Error page and still be able to log the exception details.
What is the best way to deal with something like this?
Edit: One solution I was considering would be to add a a new controller: UnhandledErrorController. Can I put in an Application_Error method in Global.asax and then redirect to this controller (where it decides what to do with the exception)?
Note: the defaultRedirect in the customErrors web.config element does not pass along the exception info.
Enable customErrors:
<customErrors mode="On" defaultRedirect="~/Error">
<error statusCode="401" redirect="~/Error/Unauthorized" />
<error statusCode="404" redirect="~/Error/NotFound" />
</customErrors>
and redirect to a custom error controller:
[HandleError]
public class ErrorController : BaseController
{
public ErrorController ()
{
}
public ActionResult Index ()
{
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return View ("Error");
}
public ActionResult Unauthorized ()
{
Response.StatusCode = (int)HttpStatusCode.Unauthorized;
return View ("Error401");
}
public ActionResult NotFound ()
{
string url = GetStaticRoute (Request.QueryString["aspxerrorpath"] ?? Request.Path);
if (!string.IsNullOrEmpty (url))
{
Notify ("Due to a new web site design the page you were looking for no longer exists.", false);
return new MovedPermanentlyResult (url);
}
Response.StatusCode = (int)HttpStatusCode.NotFound;
return View ("Error404");
}
}
As MVC is built on top of asp.net you should be able to define a global error page in web.config, just like you could in web forms eg.
<customErrors mode="On" defaultRedirect="~/ErrorHandler" />
You can create a Filter that looks for an Exception in the OnActionExecuted method:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class WatchExceptionAttribute : ActionFilterAttribute {
public override void OnActionExecuted(ActionExecutedContext filterContext) {
if (filterContext.Exception != null) {
// do your thing here.
}
}
}
Then you can put [WatchException] on a Controller or Action Method, and it will let log exceptions. If you have a lot of Controllers, that might be tedious, so if you have a common Base Controller you can override OnActionExecuted there and do the same thing. I prefer the filter method.
As far as what page to display, you'll need to create a customErrors section in your web.config and set it up for whatever status codes you want to handle.
Example:
<customErrors defaultRedirect="GenericError.htm" mode="RemoteOnly">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
As far as logging exceptions, I would recommend using ELMAH. It integrates nicely with ASP.NET MVC sites.