Serving Up Pages for HTTP Status Codes - asp.net

I'm trying to set up 500 and 404 pages for an ASP.NET 4.5.2 Web Forms project. I've got the 404 working when the non-existent page is a .ASPX page. - I have code written to serve up a custom NotFound.aspx page in response.
My problem occurs when I am using the Web.config to serve up a 404.html page for non-existent HTML pages. My 404.html page is in the root folder. If I test this with a non-existent HTML page in the root folder, 404.html renders correctly. However, if I test it with a non-existent HTML page in a non-existent sub-folder, the 404.html is served up, but the CSS/JS paths are no longer correct? It's like it's treating the CSS/JS paths as being relevant to the non-existent sub-folder I'm specifying as part of the test?
Global.asax.cs:
void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
if (ex != null && ex is HttpUnhandledException)
{
Server.ClearError();
Server.Transfer("~/Error.aspx", true);
}
if (ex != null)
{
var httpException = ex as HttpException;
if (httpException.GetHttpCode() == 404)
{
Server.ClearError();
Server.Transfer("~/NotFound.aspx", true);
}
}
}
Web.config:
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" />
<error statusCode="404" path="404.html" responseMode="File" />
</httpErrors>

Use absolute URLs for the CSS/JS paths. For example, in your 404.html (bootstrap and jquery are just examples):
<link href="/Content/bootstrap.min.css" rel="stylesheet" />
<script src="/Scripts/jquery-3.0.0.min.js"></script>
<script src="/Scripts/bootstrap.min.js"></script>
Another setup would be to set responseMode="Redirect":
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" />
<error statusCode="404" path="/Errors/404.html" responseMode="Redirect" />
</httpErrors>
In this example, 404.html is in folder Errors and the URLs can be relative:
<link href="../Content/bootstrap.min.css" rel="stylesheet" />
<script src="../Scripts/jquery-3.0.0.min.js"></script>
<script src="../Scripts/bootstrap.min.js"></script>
and that will work because the redirect URL is no longer relative to whatever URL you've requested.

I was able to solve it as follows:
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" />
<error statusCode="404" path="http://localhost/myproject/404.html"
responseMode="File" />
</httpErrors>
The key takeaway is that the path must be absolute for this version to work. So my next step would be to try and set the path programmatically so that in debug mode it would go to my local and in release mode go to the production URL. I don't want to mess with Web.config transform files.
If anyone has any further info, please feel free.

Related

Custom error page on when application error occurred

How to show a custom error page when an application error occurred without changing the url?
When application error occurs, then how to show customer a custom error page without routing to another Url?
Inside your web.config check that the configuration looks like:
<system.web>
...
<customErrors mode="On">
<error statusCode="404" redirect="~/custom404.html"/>
<customErrors
</system.web>
<customErrors mode="On" defaultRedirect="~/custom404.html">
</customErrors>
You can do this in code too. For a MVC project one can ovverride the Controller's function OnException, perform some logging and other stuff then load the contents from an Error.URL in background where the error information is formated.
protected override void OnException(ExceptionContext filterContext)
{
if (filterContext != null && filterContext.Exception != null && !filterContext.ExceptionHandled)
{
ViewBag.Exception = filterContext.Exception;
filterContext.Result = View("~/Views/Shared/Error.cshtml");
filterContext.ExceptionHandled = true;
Log.Error(filterContext.Exception.Message +":" + filterContext.Exception.StackTrace);
}
}
In this snippet all Controllers in the project inherit from a BaseController where the function OnException is being overridden.
Try this in your Web.config file
<customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="/errors/error">
</customErrors>
In any error it will redirect to the /errors/error page. Please note the redirectMode attribute. With the value ResponseRewrite the url will not change.
Now, if you want to show a different page for a specific error, you can set it with the following.
<customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="/errors/error">
<error statusCode="404" redirect="/errors/error404" />
<error statusCode="500" redirect="/errors/error500" />
</customErrors>

.NET MVC throws 500 Server Error instead of 404 Not Found when cannot match a Controller path

For some reason, when entering a dud URL to a file/directory/controller that does not exist, the following error is thrown:
System.Web.HttpException
The controller for path '' was not found or does not implement IController
System.Web.Mvc.IController > GetControllerInstance(System.Web.Routing.RequestContext, System.Type)
IIS then follows the regular error handling and shows the page appropriate for a 500 Internal Server Error. A 404 Not Found error handling logic should be followed. Another web application I am testing on DOES NOT throw this HttpException when it can't find a route, and returns 404 normally. So what triggeres this HttpException?
Why and how to follow a 404 route for this type of error instead of a 500? Below is the configuration of the error handling. No other code is handling errors. So why is the 500 error always shown. It's as if the default handling handles the 'can't find controller' exception as an error when in fact it's a not-found.
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace" defaultPath="/StaticErrors/Default.html" defaultResponseMode="ExecuteURL">
<clear />
<error statusCode="404" path="/mvcError/Http404" responseMode="ExecuteURL" />
<error statusCode="500" path="/mvcError/Http500" responseMode="ExecuteURL" />
</httpErrors>
</system.webServer>
<system.web>
<customErrors defaultRedirect="/StaticErrors/Default.html" mode="On" redirectMode="ResponseRewrite">
<error redirect="/mvcError/Http404" statusCode="404" />
<error redirect="/mvcError/Http500" statusCode="500" />
</customErrors>
</system.web>
Failed Request Trace shows this. Basically since no route is round, the HttpException is thrown, and the 500 route handling kicks in, instead of a 404. I'm not doing anything to overide any normal default behaviour. The HandleErrorAttribute is not being added either to the MVC filters.
You should add a filter controller to override some IIS custom error.
public class mvcErrorController : Controller
{
public ActionResult Http404()
{
Response.StatusCode = 404;
Response.TrySkipIisCustomErrors = true;
return View();
}
}
You need to remove the
redirectMode="ResponseRewrite"
Option from you customErrors tag. Unfortunately, this does mean you will have a 302 before your 404, but it will fix your issue.
Alternatively, use ASPX pages for your error pages:
<customErrors defaultRedirect="/StaticErrors/Default.html" mode="On" redirectMode="ResponseRewrite">
<error redirect="/StaticErrors/Http404.aspx" statusCode="404" />
<error redirect="/StaticErrors/Http500.aspx" statusCode="500" />
</customErrors>
There is previous discussion on this issue on SO here

Custom Error Handling with MVC4

I have a relatively untouched MVC4 project with the following in my Web.Release.config:
<system.web>
<customErrors mode="On" defaultRedirect="~/Error">
<error redirect="~/Error/NotFound" statusCode="404" />
</customErrors>
</system.web>
It's not working though - I get normal error pages when in Release mode.
If I place that code in my Web.Config, it works as expected. I only want this applied when in Release though.
I also tried this in web.release.config:
<system.web>
<customErrors mode="On" defaultRedirect="~/Error" xdt:Transform="Replace">
<error redirect="~/Error/NotFound" statusCode="404" />
</customErrors>
<compilation xdt:Transform="RemoveAttributes(debug)" />
</system.web>
To no avail.
Why would this be happening?
UPDATE: If I use the following in my global.asax:
void Application_Error(object sender, EventArgs e)
{
#if DEBUG
#else
Response.Redirect("/Error");
#endif
return;
}
I get the desired behavior. I feel like I should be able to use the web.config settings only though... so I'd like to leave this open.
The problem is you're not publishing. The web.release.config file is only transformed during publish as far as I know. If you're just building and running locally that file won't be used.
Also in your Application_Error you should get the status code for the error. Something like
var exception = Server.GetLastError();
var httpException = exception as HttpException;
if (httpException != null)
{
if (httpException.GetHttpCode() == 404)
RedirectToMyNotFoundPage();
}
Otherwise you will basically just handle all errors, which may or may not be what you want to do.
If you want to handle MvcErrors, and get the controller/action that the exception occured in, you should look into Filters.
Here's a good article on error filtering if that's what you're going for.
http://blogs.msdn.com/b/gduthie/archive/2011/03/17/get-to-know-action-filters-in-asp-net-mvc-3-using-handleerror.aspx

Searching on 404 page returns back to 404 page back

We have a 404 page. When we are in 404 page, we have search box. if we try to search from search box, it again returns to 404 page. How could we handle this?
PS: My search button is inside user control and user control is inside masterpage.
When we go to the site below;
http://www.x.com/dk
it goes to 404.
then if we make search from th,s 404 page it directs us to the link below;
http://www.x.com/dk?404%3bhttp%3a%2f%2fwww.x.com%3a80%2fdk
my web config sets
<customErrors mode="Off" defaultRedirect="Error.aspx">
<error statusCode="404" redirect="PageNotFound.aspx" />
</customErrors>
<httpErrors>
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" path="/PageNotFound.aspx" prefixLanguageFilePath="" responseMode="ExecuteURL" />
</httpErrors>
I solved this problem via javascript
<script type="text/javascript">
$("#btnSearch").attr("onclick", "Search('/Search.aspx?text=' + encodeURI($('#txtSearchInput').val())); return false;");
function Search(text) {
location.href = text;
}
</script>

How to return 'own' 404 custom page?

In case if error occurred on my web site I do the following:
Server.Transfer("/error.aspx");
and that page has code:
protected void Page_Load(object sender, EventArgs e)
{
...
Response.StatusCode = 404;
}
If I work on the localhost then together with 404 status returned for the page, page displays 'proper error description'.
Once I published the same code to the internet all pages with errors are still displayed with 404 status code, but the don't have the content. Instead, they have the standard 404 error message:
404 - File or directory not found.
if the line "Response.StatusCode = 404" commented out then the proper page is provided, but it has 200 status code.
Question: how to return user-friendly error page that in the same time has 404 error status code?
Any thoughts are welcome! Thanks a lot in advance!
P.S. ASP.NET 4.0
<customErrors mode="On" defaultRedirect="~/Error/GenericErrorPage.aspx">
<error statusCode="404" redirect="~/Error/404.aspx" />
</customErrors>
http://msdn.microsoft.com/en-us/library/h0hfz6fc(v=vs.71).aspx
http://msdn.microsoft.com/en-us/library/aa479319.aspx
A ha!
Try this:
Response.TrySkipIisCustomErrors = true;
Response.Status = "404 Not Found";
Response.StatusCode = 404;
I found as soon as I added Response.TrySkipIisCustomErrors=true before setting the status code, I would see the normal page copy AND a 404 is returned. Without this line the standard IIS 404 page is displayed.
Alternatively, this can be set in the web.config like so:
<system.webServer>
<httpErrors existingResponse="PassThrough">
// custom error page mappings
</httpErrors>
</system.webServer>
The key thing here is existingResponse="PassThrough"
This was added to IIS7 thus is required on sites running in Integrated Pipeline mode.
For more info: http://msdn.microsoft.com/en-us/library/system.web.httpresponse.tryskipiiscustomerrors.aspx
Update
Updating for clarrification/more info as people are still finding this useful.
It's worth noting that if you need a simple, generic 404 page use the web.config method (and best make it a plain HTML page).
The method I describe works best if you have a heavily dynamic or CMS driven site where any given page could potentially return a 404 but you want to show your visitor related info.
For example, a special offers page (offers/some-offer) could do a lookup for the offer (some-offer) and if it doesn't exist show alternative or related offers while returning a 404 under the bonnet. As far as the visitor is aware they've just been told the offer is no longer available but they're still in the offers section but we're also telling robots to un-index the URL.
This would be a lot harder to do if there was just one generic 404 page.
You can achieve this by configuring your web.config file. Please check the link below to an article, which explains at the bottom of the page, how to display different custom error pages for different HTTP error statuses.
Displaying a Custom Error Page
To show your own page with the correct 404 statuscode, you can use the following code:
1) In your web.config add the following:
<customErrors mode="On" redirectMode="ResponseRewrite">
<error statusCode="404" redirect="404.htm" />
</customErrors>
and:
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="404.htm" responseMode="File"/>
</httpErrors>
2) Add a 404.htm file to the root of your website:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>404 - Not Found</title>
<meta http-equiv="refresh" content="5;url=/404-PAGE" />
</head>
<body>
CONTENT
</body>
</html>
You can either add content to the body of this file and remove the META refresh or simply use the META refresh to open a page within your CMS.
Combining Jag's and adt's answers, I still had a problem. When a 404 was handled by the static file handler (as opposed to ASP.NET), I got a blank response. (The status was correctly 404.)
To fix it, I had to add errorMode="Custom" to the <httpErrors> element. If your error page uses ASP.NET, you need to include responseMode="ExecuteURL".
<system.web>
<customErrors mode="On" defaultRedirect="~/Error.aspx" redirectMode="ResponseRewrite" />
</system.web>
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404" />
<error statusCode="404" path="/Error.aspx" responseMode="ExecuteURL" />
</httpErrors>
</system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="ErrorPages/error404.htm" responseMode="Redirect"/>
</httpErrors>
This works for me if I don't use the tilde (~) in the path attribute
For anyone wondering in ASPNET 6.0+ you can now use the following official solution [1]
In Program.cs:
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
In StatusCode.cshtml.cs
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class StatusCodeModel : PageModel
{
public int OriginalStatusCode { get; set; }
public string? OriginalPathAndQuery { get; set; }
[ViewData]
public string Code { get; set; } = "";
public void OnGet(int statusCode)
{
OriginalStatusCode = statusCode;
Code = statusCode.ToString();
var statusCodeReExecuteFeature =
HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
if (statusCodeReExecuteFeature is not null)
{
OriginalPathAndQuery = string.Join(
statusCodeReExecuteFeature.OriginalPathBase,
statusCodeReExecuteFeature.OriginalPath,
statusCodeReExecuteFeature.OriginalQueryString);
}
}
}
In StatusCode.cshtml (here is where you can customize to your sites theme)
#page
#model StatusCodeModel
Its a Custom Page for Status Code: #ViewData["Code"]
[1] https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-6.0#usestatuscodepageswithreexecute
To create & access 404 page in asp.net C# follow the steps:
1)create an 404.html file & add your content
<html>
<head>
<meta charset="utf-8" />
<title>404 - Not Found</title>
<link href="bootstrap/css/bootstrap.css" rel="stylesheet" />
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<style>
body {
color: #797979;
background: #eaeaea;
font-family: 'Ruda', sans-serif;
padding: 0px !important;
margin: 0px !important;
font-size: 13px;
}
.p404 img {
margin-top: 120px;
}
.centered {
text-align: center;
}
.p404 h1 {
font-weight: 900;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-lg-6 col-lg-offset-3 p404 centered">
<img src="assets/img/404.png" alt="">
<h1>DON'T PANIC!!</h1>
<h3>The page you are looking for doesn't exist.</h3>
<br>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<a class="btn btn-success" href="Default.aspx">Back To Home</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
in web config add this code
<system.web>
<customErrors mode="On" redirectMode="ResponseRewrite">
<error statusCode="404" redirect="404.html" />
</customErrors>
</system.web>
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="404.html" responseMode="File"/>
</httpErrors>
</system.webServer>

Resources