Custom Errors Work Locally Not Once Deployed On IIS - c#

I have the following:
Web.config
<system.web>
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<customErrors mode="On" defaultRedirect="~/Error/ShowError">
<error redirect="~/Error/ShowError/400" statusCode="400" />
<error redirect="~/Error/ShowError/401" statusCode="401" />
<error redirect="~/Error/ShowError/403" statusCode="403" />
<error redirect="~/Error/ShowError/404" statusCode="404" />
</customErrors>
</system.web>
ErrorController
[AllowAnonymous]
public class ErrorController : Controller
{
public ViewResult ShowError(int id)
{
Response.StatusCode = id;
switch (id)
{
case 400:
return View("~/Views/Error/400.cshtml");
case 401:
return View("~/Views/Error/401.cshtml");
case 403:
return View("~/Views/Error/403.cshtml");
case 404:
return View("~/Views/Error/404.cshtml");
default:
return View("~/Views/Error/404.cshtml");
}
}
}
FilterConfig
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//filters.Add(new HandleErrorAttribute());
}
Locally everything works fine, I get a custom error page and am happy, however as soon as I deploy the site to my web server, I no longer get my custom error messages and only the generic:
Do I need to add anything specific for the IIS configuration in my online environment?
I've compared the local Web.config with the deployed Web.config and there isn't anything different (that I can see).

Your error is overwritten by IIS custom error.
Try:
Response.TrySkipIisCustomErrors = true

Related

Setting up authorization and a "Access-denied" page

I'm working on an ASP.Net MVC application. I want to deny access to all users who are unauthenticated or not in an AD group. Only this AD group should have access. The exception to this is the "You Shall Not Pass!" page. Anyone can access that.
In the project root, I have this in my web.config (the rest of the file is trimmed for brevity):
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<customErrors mode="On">
<error statusCode="401" redirect="/ui/Other/YouShallNotPass.html" />
</customErrors>
<compilation debug="true" targetFramework="4.5.2" />
<httpRuntime targetFramework="4.5.2" />
<authentication mode="Windows" />
<authorization>
<allow roles="allowedrole"/>
<deny users="*"/>
</authorization>
</system.web>
<system.webServer>
<httpErrors>
<remove statusCode="401" />
<error statusCode="401"
subStatusCode="2"
responseMode="ExecuteURL"
path="/ui/Other/YouShallNotPass.html" />
</httpErrors>
</system.webServer>
</configuration>
I have a second web.config sitting next to ui/Other/YouShallNotPass.html. I expect this to allow anyone to access this page, authenticated or otherwise:
<configuration>
<system.web>
<authorization>
<allow users="?"/>
</authorization>
</system.web>
</configuration>
I'm able to test this by setting the AD group to one that doesn't exist. I'm not part of the non-existent group, so I should expect to see the YouShallNotPass.html page.
It's not working as expected. I'm getting the following error in my browser:
Error message 401.2.: Unauthorized: Logon failed due to server configuration. Verify that you have permission to view this directory or page based on the credentials you supplied and the authentication methods enabled on the Web server.
If I request YouShallNotPass.html directly, I can access it after being prompted for a user/pass.
What am I doing incorrectly? Why won't it serve the 401 page when the user isn't authorized?
Check out this question because it explains why your solution won't work.
So once you move to annotate all your secure controller actions with [Authorize] you can add a custom ExceptionFilter
like this
public class HandleUnauthorizedAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
base.OnException(filterContext);
if (filterContext.Exception.GetType() != typeof (SecurityException)) return;
var controllerName = (string) filterContext.RouteData.Values["controller"];
var actionName = (string) filterContext.RouteData.Values["action"];
var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
filterContext.Result = new ViewResult
{
ViewName = "Unauthorized",
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
TempData = filterContext.Controller.TempData
};
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = 403;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
}
Then wiring it up here
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleUnauthorizedAttribute());
}
}
Managed to work around this by adding the following to my global.asax:
protected void Application_EndRequest(object sender, EventArgs e)
{
if (Response.StatusCode != 401)
return;
Response.ClearContent();
Response.WriteFile("~/ui/Other/YouShallNotPass.html");
Response.ContentType = "text/html";
}
I would have preferred using the web.config to do this, though.

RoleManager crashes application asp.net MVC WebApi

my RoleManager keeps failing to connect to the database
my application's roles work fine in my ASP.net MVC webApi application without it, but i can't get the roles of a user without adding RoleManager to the WebConfig
this is a part of the webconfig containing the RoleManager definition
<system.web>
<roleManager enabled="true">
<providers>
<add name="newprovider"
type="System.Web.Security.SqlMembershipProvider"
minRequiredNonalphanumericCharacters="0"
connectionStringName="DefaultConnection"/>
</providers>
</roleManager>
<customErrors defaultRedirect="Error.aspx" mode="On">
<error statusCode="401" redirect="~" />
<error statusCode="403" redirect="Forbidden.aspx" />
</customErrors>
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5.2" />
<httpRuntime targetFramework="4.5.2" />
<httpModules>
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
</httpModules>
</system.web>
and this is the code I have in my RolesController
[Authorize]
public class RolesController : ApiController
{
// GET: api/Roles
public IEnumerable<string> Get()
{
string[] roleNames = Roles.GetRolesForUser();
return roleNames;
}
}
when i have the RoleManager part in the WebConfig my application doesn't even show the index page, it shows an error:
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.
EDIT: now shows error on Line 20: type="System.Web.Security.SqlMembershipProvider"

Custom error pages in ASP.NET MVC 5

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.

how to redirect to default error page after ELMAH implemented?

I have implemented ELMAH with help of this post (#Ivan Zlatev answer). It is working fine. But now I need to redirect default error page with error message.
How can I do this?. I was reading with ELMAH, we can not implement custom error page. Is it true?
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ElmahHandledErrorLoggerFilter());
filters.Add(new HandleErrorAttribute());
}
public class ElmahHandledErrorLoggerFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
// Log only handled exceptions, because all other will be caught by ELMAH anyway.
if (context.ExceptionHandled)
ErrorSignal.FromCurrentContext().Raise(context.Exception);
}
}
Elmah is for logging errors, Creating custom error page is a different step. You can do it using your web.config
<configuration>
<system.web>
<customErrors mode="On" defaultRedirect="error.aspx">
<error statusCode="404" redirect="404.aspx" />
<error statusCode="500" redirect="500.aspx" />
</customErrors>
</system.web>
</configuration>
And then you will have to implement the error.aspx 400.aspx 500.aspx pages.

Can't redirect to custom error pages

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.

Categories

Resources