I have an MVC website (v5, though I don't think it's related) where I have intentionally introduced an error upon when attempting to establish a database connection (wrong server IP in the connection string). When the user hits the HomeController one dependencies for the constructor is a UserRepository (to get the current user profile data) which depends on a database connection/session to be available. When it's not, the Dependency Resolver can't inject the UserRepository and when that happens it causes an error (as it does with any dependency of any controller), and I get a generic "No parameterless constructor defined for this object". Which is pretty useless.
So I'm trying to use a custom error page to retrieve the inner exception and display it in a friendly manner. (Because this error is happening when trying to acquire the HomeController, it never actually reaches the HandleErrorAttribute, hence the relying on CustomErrors).
So I have an ErrorsController with a series of actions...
Snippet from ErrorsComtroller.cs
public ActionResult Error()
{
return View("Error_500");
}
public ActionResult NotFound()
{
return View("Error_404");
}
Snippet from web.config
<customErrors mode="On">
<error statusCode="404" redirect="~/errors/notfound" />
<error statusCode="500" redirect="~/errors/error" />
</customErrors>
The Error_500 page is pretty basic, it has a model type of HandleErrorInfo, but if it's not present it checks for Exception details using Server.GetLastError(). Problem is, GetLastError() is always null, and I get my custom error page but no additional details beyond my generic feedback of "An unexpected error has occured". After doing some digging I found that the method doesn't work after a redirect, which is the default way the CustomErrors functions. So I changed the web.config to use this line instead...
Snippet from web.config
This way it won't cause a redirect and the GetLastError() should have my exception details about the database connection problem. Thing is, now I get the default ASP.NET error page with this message.
An exception occurred while processing your request. Additionally,
another exception occurred while executing the custom error page for
the first exception. The request has been terminated.
So I did some more digging using intellitrace, and I see the exception about the database connection. A little farther down I see the error about not having a parameterless constructor on HomeController and then one about encountering an error trying to create the controller of type 'HomeController'. But then I see one that says
Error executing child request for /errors/error
So I navigated directly to that path and the page works fine. But when it's used in customerrors WITH the ResponseRewrite for the redirectmode, it errors out. I put a break line on the first (and only) line of the ErrorsController.Error() action, but it never gets hit. If I substitute the redirect path in the custom errors to a static file it works, but if I change it back to the ~/errors/error it fails again.
Is there a issue when using MVC actions as url's for the CustomErrors when ResponseRewrite is specified?
"This happens because "ResponseRewrite" mode uses Server.Transfer under the covers, which looks for a file on the file system. As a result you need to change the redirect path to a static file, for example to an .aspx or .html file:"
<customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="~/Error.aspx"/>
See: https://dusted.codes/demystifying-aspnet-mvc-5-error-pages-and-error-logging
"Apparently, Server.Transfer is not compatible with MVC routes, therefore, if your error page is served by a controller action, Server.Transfer is going to look for /Error/Whatever, not find it on the file system, and return a generic 404 error page!"
See: CustomErrors does not work when setting redirectMode="ResponseRewrite"
In other words, you cannot use ResponseRewrite with views.
This is a well known issue that has been problematic for developers because it does not afford itself to either an easy or elegant solution. Bottom line, MVC does not play nice when using custom views for exception handling and customer user-friendly pages for HTTP errors. The stock error.cshtml file (i.e., a View) in the Views\Shared folder is a great thing to have because it maintains the layout of the web page and provides exception errors. But, when you get HTTP errors then you need to create a view to handle the status code errors (e.g., 404, 500, etc.). Note: if you go the route of sending HTTP errors to a view then the URL line will contain non-ideal info (see weblinks below for further explanation).
You could route HTTP errors to the Error view, but I don't recommend it because the Error view should be for application errors (i..e, exceptions) whereas a separate custom user-friendly page should be created for generic HTTP errors. The difference is that the former is an application problem that the site developer needs to look at whereas the latter is a user error (or at least should be) that does not require the developer to look at it (just my 2 cents).
An alternative is to bypass the views and use custom user-friendly pages for both application exceptions and HTTP errors. But, beware of two problems:
1.) The wrong status code is returned (usually 200), which can be a problem because it will be picked up and indexed by search engines (you do not want this!)
2.) The URL specifies a non-sensical URL in the web browser
These can be handled easy enough. See the following link (go down to the section customErrors in web.config): https://dusted.codes/demystifying-aspnet-mvc-5-error-pages-and-error-logging
Below are other weblinks that I also found useful:
http://benfoster.io/blog/aspnet-mvc-custom-error-pages
https://msdn.microsoft.com/en-us/library/bb397417.aspx
https://www.youtube.com/watch?v=nNEjXCSnw6w
How do I display custom error pages in Asp.Net Mvc 3?
The last one appears to be yet another alternative: a custom hack to get around the problem of not being able to couple views with ResponseRewrite. This works by completely bypassing CustomErrors (i.e., CustomErrors mode="Off"). I have not yet tried this yet, but I am looking into it.
Final thought, keep an eye on all site status codes when either error or exception codes are thrown - make sure there are no 200 (i.e., OK) codes.
Related
I have a global error handling for the controller which is working fine. But sometimes when any web.config element are missing or can't able to load it shows the error screen and hitting the application-error in the global.asax as the the exception never reaches the pipe line. Also I was able to redirect to a custom error page by setting custom error as remoteonly with redirection page but not able to log it as not hitting application-error in global.asax. Does anyone know how to handle the exception here
I guess, Exception handles your code only, the exception which you were taking is before any code to be execute and it will handle by Web Server i.e. IIS
I have a global error handler in Global.asax and am trying to display the exception information in a page called ErrorPage.aspx. I've read Microsoft's page about passing information between asp.net pages (http://msdn.microsoft.com/en-us/library/6c3yckfw.aspx). I don't think any of these will work:
QueryString: I'm concerned that the length of the exception will
exceed the maximum length of the QueryString.
Post Information: The Global.asax page doesn't have any form fields.
Session State: Session state is not available in Global.asax
Public Values: I think this only works with .aspx pages
Control from Source Page: Global.asax can't have asp.net controls.
My current thought is that it would be logical to create an instance of the ErrorPage object and navigate to it (as you could do in a Windows Forms application), but I don't know how to do that or if it is possible in asp.net.
Ultimately, I'm looking for a way to display errors caught by a global error handler in a standard .aspx page. Any suggestions would be helpful.
What I would do:
Log exception information including stack trace in a log file,
Redirect to an ErrorPage with error code (regular http errorcodes + a generic 'unknown error' one + maybe some custom errors that make sense for the application) passed in query string parameter,
Display a predefined message for this error code.
There is little reason to display actual error messages to end users, more detailed error information not withstanding. If you want to display full information for debugging purposes, turn custom errors off in web.config
Can I configure ASP.NET custom errors so that it would redirect to other site when error has occurred. Even more, I would like to redirect to different web page every time.
Here is my simplified actual case:
User opens my pages with query ?urlpage=http://test.com/error.html and I would like to redirect to this page when error occurs.
How should I act in this scenario?
See http://msdn.microsoft.com/en-us/library/ed577840.aspx
And in the Page_Error method, just do:
var redirectUrl = Request.QueryString["urlpage"];
if (redirectUrl != null)
Response.Redirect(redirectUrl);
There are a number of different resources in ASP.NET that will allow you to handle errors.
I found this article on the web.config file very informative : http://articles.sitepoint.com/article/web-config-file-demystified
In addition you can setup error handling inside of the global.asax file that will trap application wide errors and allow you to send emails, log information, etc.
You can also setup page specific error handling for more custom error information - but I find that a good general purpose error handling in global.asax works for most situations.
Finally, you can go into the properties for IIS and modify where errors are redirected. For instance, set the 500 error to point to a specific file (/error.aspx or similar).
As is suggested elsewhere, I am using redirectMode = ResponseRewrite in my custom error configuration so my custom error page can access the exception info. This has worked like a charm for some time.
In adding some more "help the user recover from an error" type functionality, we need a piece of info that has been previously stored in Session. When implementing this, I found that the various avenues to Session end in null when redirectMode=ResponseRewrite, but they are all populated when redirectMode=ResponseRedirect (or isn't defined).
Anyone know why? It seems odd that we'd have to choose between having exception info (ResponseRewrite) or having Session (ResponseRedirect).
The MSDN article on Rich Custom Error handling tells me that Session is only available when the control passing method is Server.Transfer, which is what I assumed ResponseRewrite used under the hood. Evidently that isn't the case.
I don't know the answer to the question yet, but to get past it, I took the redirectMode attribute out of my web config and put custom logic in the Global.asax Application_Error handler to do what I wanted. I am replacing the exception with a "user friendly" message exception, but essentially the transfer logic is:
if(Context.IsCustomErrorEnabled)
{
Server.Transfer("~/Error.aspx");
}
Then the Error.aspx page has Page_Load code to pull the error out of context and display the message.
I'm trying to understand how error handling works when using the Authorize [Authorize] Action Filter in MVC Preview 4.
I have an action that looks like this:
[Authorize(Roles = "DOMAIN\\NOTAUTHORISED_ROLE" )]
[HandleError]
public ActionResult NeedAuthorisation()
{
throw new NotImplementedException();
}
When I visit the url: http://localhost:2197/testAuthorisation/NeedAuthorisation, I get a blank page in my browser. In Firebug I can see that a request was made and a response-status of 401 - Unauthorised has been returned. But I'm not being redirected or having a customError returned. Everything works as expected when using a role that I'm authorized for.
This is using Windows authentication. I'm in the middle of writing some code to try out Forms authentication to see if I get the same issue.
I have <customerrors mode="On"/> set and have created error pages, both in the testAuthorisation folder and the Shared folder.
I eventually found this MVC tutorial which solved my problem:
Exactly what happens when you attempt to invoke a controller action
without being the right permissions depends on the type of
authentication enabled. By default, when using the ASP.NET Development
Server, you simply get a blank page. The page is served with a 401 Not
Authorized HTTP Response Status.
If you've got CustomErrors set to Off or RemoteOnly then you won't get re-directed to the page specified by HandleError (default is Error.aspx). Set it to "On" and then see what happens. Any custom error pages you specify explicitly will take precedence, however, so you need to remove these, and have just:
<customErrors mode="On" />
You need an error view in the corresponding view folder, i.e. you need the file Views/TestAuthorization/Error.aspx in order to have anything show up.
You can also customize this behaviour by what view that you want to use and to what exception you want it to be triggered with.
[HandleError(ExceptionType = typeof(SqlException), View = "DatabaseError")]]
[HandleError(ExceptionType = typeof(NullReferenceException), View = "LameErrorHandling")]]