I want to add custom error pages to my project.
I found this post about my problem and i try to implement it.
So :
i add 404.cshtml, 404.html, 500.cshtml and 500.html pages
set response status code in added cshtml files
comment adding HandleErrorAttribute to global filters
update my web.config file
But now when i try to go by path http://localhost:120/foo/bar where my app is on http://localhost:120 i get next page :
Server Error in '/' Application.
Runtime Error
Description: 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.
I set <customErrors mode="Off" to see what problem is. It was - The resource cannot be found. which is logical. But when i set <customErrors mode="On" - i again get Runtime error.
What can cause it and how to solve it?
My config file :
<system.web>
<customErrors mode="Off" redirectMode="ResponseRewrite" defaultRedirect="~/500.cshtml">
<error statusCode="404" redirect="~/404.cshtml"/>
<error statusCode="500" redirect="~/500.cshtml"/>
</customErrors>
</system.web>
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="404.html" responseMode="File"/>
<remove statusCode="500"/>
<error statusCode="500" path="500.html" responseMode="File"/>
</httpErrors>
</system.webServer>
IIS version 8.5
What version of IIS are you using? If 7+ then ignore custom errors using <customErrors mode="Off"> and use <httpErrors>. Using the method below the latter will show your error page without changing the URL which IMO is the preferred way of handling these things.
Set up an Error Controller and put your 404 and 500 in there like so:
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" />
<remove statusCode="500" />
<error statusCode="404" path="/error/notfound" responseMode="ExecuteURL" />
<error statusCode="500" path="/error/servererror" responseMode="ExecuteURL" />
</httpErrors>
In the controller:
public class ErrorController : Controller
{
public ActionResult servererror()
{
Response.TrySkipIisCustomErrors = true;
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return View();
}
public ActionResult notfound()
{
Response.TrySkipIisCustomErrors = true;
Response.StatusCode = (int)HttpStatusCode.NotFound;
return View();
}
}
Then obviously set up the corresponding view for each error covered.
Your redirect url is not correct. Make it ~/404.html and ~/500.html. Notice that I changed the extension from .cshtml. Make sure the files exist and you are pointing to the correct location.
I had to do the following:
Remove the defaultPath from httpErrors and defaultRedirect from customErrors.
Make sure in Global.asax I had registered a default route:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { action = "Index", id = UrlParameter.Optional
});
I'm not sure why step 1 was necessary and step 2 was because we're doing EPiServer.
Related
I have the following code in global.asax which transfers to a static NotFound.aspx file when there is a 404 exception. This works on my development machine, with debug or release builds. When deploying the release build to an azure app service, instead of getting my static NotFound.aspx file I get a page with only the text: The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
I have verified that the static files are present on the azure deployment.
The code in global.asax is:
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
Response.Clear();
HttpException httpException = exception as HttpException;
if (httpException != null)
{
ErrorLogger.Log(httpException);
Server.ClearError();
switch (httpException.GetHttpCode())
{
case 404:
// page not found
Response.StatusCode = 404;
Server.Transfer("~/NotFound.aspx");
break;
default:
Response.StatusCode = 500;
Server.Transfer("~/Error.aspx");
break;
}
}
}
The problem seems to be that the Azure server environment has it's httpErrors config section defined in a way that intercepts these errors before they get to Application_Error. You can either modify this to let the errors pass through, or use it to deal with the errors in the first place (Which seems to be the best option). Using responseMode="File" you can avoid having to issue a redirect, and just supply a custom error page and the proper status code directly. This seems to be a more efficient and correct approach.
Example:
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace" >
<remove statusCode="404"/>
<error statusCode="404" path="NotFound.html" responseMode="File"/>
<remove statusCode="500"/>
<error statusCode="500" path="Error.html" responseMode="File"/>
<remove statusCode="400"/>
<error statusCode="400" path="Error.html" responseMode="File"/>
</httpErrors>
</system.webServer>
For more info:
https://www.iis.net/configreference/system.webserver/httperrors
You could also try specifying the redirect rule in the Web.config:
<configuration>
<system.webServer>
<httpErrors errorMode="DetailedLocalOnly" existingResponse="PassThrough">
<remove statusCode="404"/>
<add statusCode="404" path="/NotFound.aspx" responseMode="Redirect" />
</httpErrors>
</system.webServer>
</configuration>
Then in your Web.Release.config(or other configuration you use in Azure):
<configuration>
<system.webServer>
<httpErrors errorMode="DetailedLocalOnly" existingResponse="Replace" xdt:Transform="SetAttributes">
</httpErrors>
</system.webServer>
</configuration>
You can add your code 500 error page in a similar fashion.
Setting responseMode to Redirect makes IIS redirect the user with a 302, setting it to ExecuteURL will replace the response with the error page but keep the URL in the address bar.
Here is nice article about handling errors in this way: http://tedgustaf.com/blog/2011/5/custom-404-and-error-pages-for-asp-net-and-static-files/
I'm trying to redirect to a custom page when there's an error. So I added to the Web.Config the following code
<customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="~/Views/Error/Error500.cshtml">
<error statusCode="404" redirect="~/Views/Error/Error404"/>
<error statusCode="500" redirect="~/Views/Error/Error500"/>
</customErrors>
</system.web>
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" path="~/Views/Error/Error404" responseMode="ExecuteURL" />
<remove statusCode="500" subStatusCode="-1" />
<error statusCode="500" path="~/Views/Error/Error500" responseMode="ExecuteURL" />
</httpErrors>
But when I try localhost:23920/aFakeURl it redirects me to a blank page and it doesn't reach my ErrorController.
If I try localhost:23920/Error/Error404 it goes in my controller
// GET: /Error/Error404
public ActionResult Error404()
{
Response.StatusCode = 404;
return View();
}
then it returns a statusCode 404 and IIS doesn't know what to do with it and it gives me a blank page. So I'm pretty sure the problem is the path in the Web.Config.
I tried
~/Views/Error/Error404"
~/Views/Error/Error404.cshtml"
/Views/Error/Error404"
/Views/Error/Error404.cshtml"
It might be good to mention that when the path doesn't have a ~ it returns a runtime exception instead of a blank page.
So I have 2 questions.
What's the proper way to write the Web.Config?
Should I return the proper status code like this
Response.StatusCode = 404; in the ErrorController?
Thank you
I don't know if it's good to mention but I use Elmah for error handling and logging. No idea if it has something to do with this problem but I read in their documentation that it should work with mode="On". Is there a better way to handle all this?
EDIT
Now I use this
<customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="/Error/Error500">
<error statusCode="404" redirect="/Error/Error404"/>
<error statusCode="500" redirect="/Error/Error500"/>
</customErrors>
</system.web>
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" path="/Error/Error404" responseMode="ExecuteURL" />
<remove statusCode="500" subStatusCode="-1" />
<error statusCode="500" path="/Error/Error500" responseMode="ExecuteURL" />
</httpErrors>
but it always return an error 500 when I type a bad url.
The exception is System.Web.HttpException: The controller for path '/aBadUrllllll' was not found or does not implement IController.
How come this doesn't return a 404 error?
Do I have to change something in the Route.config?
Is ok to place the tag:
also you need specify the general errors codes and pages like this:
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Auto">
<remove statusCode="403" subStatusCode="14"/>
<error statusCode="403" subStatusCode="14" responseMode="ExecuteURL"
path="/App/Error/Forbidden"/>
<remove statusCode="404"/>
<error statusCode="404" responseMode="ExecuteURL" path="/App/Error/NotFound"/>
</httpErrors>...
Remember creates the page NotFound in the controller Error!
I was having similar issues and it was eventually resolved by ensuring I did all of the following:
1) customErrors mode="On" was set in web.config
2) Created an ErrorController with actions for specific errors that required a custom error page.
e.g.
[AllowAnonymous]
public virtual ActionResult NotFound(string message = "")
{
HttpContext.Response.StatusCode = 404;
return View();
}
3) Created a Filter.config file to add the HandleErrorAttribute and called this method in app start in global.asax.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
4) Added global error handler in global.asax. Here's an incomplete sample of it.
void Application_Error(object sender, EventArgs e)
{
var exception = Server.GetLastError();
Response.Clear();
var httpException = exception as HttpException;
if (httpException != null)
{
var action = string.Empty;
switch (httpException.GetHttpCode())
{
case 401:
action = "Unauthorized";
break;
case 403:
action = "Forbidden";
break;
case 404:
action = "NotFound";
break;
case 500:
action = "ServerError";
break;
default:
action = "ServerError";
break;
}
Server.ClearError();
Response.Redirect(string.Format("~/Error/{0}/?message={1}", action, exception.Message));
}
Logger.Error("Unhandled website exception", exception);
}
With all this in place, then hard exceptions will be properly handled and intentional redirects, as well. For example, in cases upon login where a business rule was not met that prevented a user from proceeding, this was done.
return RedirectToAction("Unauthorized", "Error", new { message = "Your account is restricted from using this functionality." });
One word of advice, though, is that returning a 401 error will end up not displaying the error page and will take you to the login page, unless you handle it appropriately. I'm sure that is explained in some other post.
One other thing, and I just now discovered this when I deployed the latest to our Dev and UAT environments. In my local IIS, I was seeing the custom error pages just fine, but in other environments it was showing the default IIS pages. Routing to my ErrorController was working just fine, but the custom pages were still not showing. Turns out I needed this web.config entry
httpErrors existingResponse="PassThrough"
under system.webServer, which now makes sense.
I am trying to force my project to use custom made error page with /error address.
I did write ActionResult Error function in controller
public ActionResult Error()
{
return View();
}
made a simple View for Error
#{
ViewBag.Title = "Error Page";
}
<h1>ERROR</h1>
added an error route to global
routes.MapRoute(
"Error",
"Error/",
new { controller = "Home", action = "Error" }
);
and changed webconfig customErrors
<customErrors mode="On" defaultRedirect="Error">
</customErrors>
yet when I type non-existing url I get
Server Error in '/' Application. The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its
dependencies) could have been removed, had its name changed, or is
temporarily unavailable. Please review the following URL and make
sure that it is spelled correctly.
Requested URL: /Galery
Version Information: Microsoft .NET Framework Version:2.0.50727.1433;
ASP.NET Version:2.0.50727.1433
------>
what am I doing wrong.
Well if I type localpath/Error I can get to Error path and it works fine. Why the redirection is not working?
I am using VS2012 and server started by it.
You'll also have to override the <system.webServer> error block to catch IIS level errors:
<configuration>
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="PassThrough">
<clear />
<error statusCode="502" path="/Error" responseMode="File" />
<error statusCode="501" path="/Error" responseMode="File" />
<error statusCode="500" path="/Error" responseMode="File" />
<error statusCode="412" path="/Error" responseMode="File" />
<error statusCode="406" path="/Error" responseMode="File" />
<error statusCode="405" path="/Error" responseMode="File" />
<error statusCode="404" path="/Error" responseMode="File" />
<error statusCode="403" path="/Error" responseMode="File" />
<error statusCode="402" path="/Error" responseMode="File" />
<error statusCode="401" path="/Error" responseMode="File" />
</httpErrors>
</system.webServer>
</configuration>
Errors and 404s are not the same thing. Your custom error thing will fire for unhandled exceptions, but a 404 is not considered to be an unhandled exception by the MVC framework. Create a "catch-all" route of "{*anything}" and route it to the error action method.
Also make sure you put this route as the VERY LAST ROUTE so the other routes aren't captured by it.
I have setup a custom error handler to show diagnostic information to a user if a crash occurs. The problem is the custom error page is not shown and I get an exception (according to the webpage) thrown whilst trying to show the error page. I cannot figure out what is causing it though? I have similar pages set up for 500 and 404 errors which work fine.
The error page says Server Error in '/' Application.
Description: 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.
Ill show snippets of my setup, if anyone wants to see more please ask. FYI I am throwing the exception by removing the connection string details from my web.config (its an actual error we are seeing during deployment so I want to target this specifically)
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404" />
<error statusCode="404" path="/error/notfound" responseMode="ExecuteURL" />
<remove statusCode="403" />
<error statusCode="403" path="/error/forbidden" responseMode="ExecuteURL" />
<remove statusCode="500" />
<error statusCode="500" path="/error/" responseMode="ExecuteURL" />
</httpErrors>
</system.webServer>
<!-- .... -->
<system.web>
<customErrors mode="On" defaultRedirect="~/Error" redirectMode="ResponseRewrite">
<error redirect="~/Error/NotFound" statusCode="404" />
<error redirect="~/Error/Forbidden" statusCode="403" />
<error redirect="~/Error/" statusCode="500" />
</customErrors>
</system.web>
I then have an ErrorController with a default Index() function (no breakpoint within this controller gets hit)
public ViewResult Index()
{
return View("Error");
}
Note: I have functions for Forbidden() and NotFound() - I just didnt copy them here - these errors work perfectly fine.
Do you have all those roots available you specified in web.config? Seems that your controller just have index action, returning some view, but it supposed to have NotFound and Forbidden actions as well, or at least correct routs such as:
routes.MapRoute(
"NotFound",
"Error/NotFound",
new { controller = "Error", action = "Index" }, //bind all routes to single action
null, controllerNamespaces
);
routes.MapRoute(
"Forbidden",
"Error/Forbidden",
new { controller = "Error", action = "Index" }, //bind all routes to single action
null, controllerNamespaces
);
If you don't have NotFound and Forbidden actions setup those errors are likely throwing a second exception that is going to your index page since it is the default. Trying adding those two others your controller and see if they get hit and perhaps you can find the problem easier.
Also note that ResponseRewrite isn't compatible with mvc, see the answer here: CustomErrors does not work when setting redirectMode="ResponseRewrite"
You cannot use redirectMode="ResponseRewrite" when redirecting to another action, you have to use redirectMode="ResponseRedirect". You will loose the ability to use Server.GetLastError(), but if you don't need that, you will be fine.
We're using the following technique to catch all non-existing URLs and provide our own resulting page:
<handlers>
<add name="Foo" path="foo.aspx" verb="*" type="Foo.UrlHandler" preCondition="integratedMode,runtimeVersionv2.0"/>
</handlers>
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<remove statusCode="405"/>
<error statusCode="404" path="/foo.aspx" responseMode="ExecuteURL"/>
<error statusCode="405" path="/foo.aspx" responseMode="ExecuteURL"/>
</httpErrors>
However, when I check which request headers are being passed to the UrlHandler, I see all but one: the If-Modified-Since header doesn't get passed. I see all the others though (Cache-Control, Accept, etc).
Had any experience with this? It's kind of related to this question:
Posting forms to a 404 + HttpHandler in IIS7: why has all POST data gone missing?
Update: I'm not alone - http://www.webmasterworld.com/microsoft_asp_net/3935439.htm
Solved. In case anyone has the same problem:
I changed the project to a .NET MVC (2, but 1-3 should all do fine). Made a single route to catch-all:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("All", "{*url}", new { controller = "CatchAll", action = "Index" });
}
Then added a single CatchAll controller to do exactly as my HttpHandler once did.