I am sick of the asp.net, just one question that
//EmpService.cs
Emplee emp = loadDataFromDB(emp_id);
if (emp == null) //Fail to get employee data
{
this.custom404();
}
Based on the MVC framework, how do we handle the custom404 and finally return a custom 404 Not Found page to the user? Like how to define controllers, how to define views. A concrete example with codes would be better. Thanks!
In the web.config file place these code inside <system.web></system.web> and create Error controller and three actions Error404 etc. and respective view pages. if iis returns status 500 then the Error404 page will be shown. You can create your own error pages.
<customErrors mode="On" defaultRedirect="Error">
<error statusCode="404" redirect="~/Error/Error404" />
</customErrors>
I'm trying to understand HandleErrorAttribute in MVC3. (I also followed old article from ScottGu) I added the <customErrors mode="On" /> to the web.config file. All errors redirect to the \Views\Shared\Error.cshtml view. If I keep the HandleErrorAttribute or remove from the controller, there is no difference in the behavior.
Code of the controller
public class HomeController : Controller
{
[HandleError]
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
throw new Exception();
return View();
}
}
Also, I show in some articles and SO post, that with <error redirect="..."/>, request can be redirected to the required view.
Qestions
What is the use of HandleErrorAttribute?
What is the advantage of using it over <customErrors.. ?
What can we achieve that is not achievable by <customErrors.. ?
1) The HandleErrorAttribute (MSDN) is a FilterAttribute that is used to handle controller actions that throw an error. I would suggest reading the documentation on the MSDN page as it describes exactly what it does and the constructors that it can take. Additionally in your webconfig you must have the customErrors section set to.
<system.web>
<customErrors mode="On" defaultRedirect="Error" />
</system.web>
2) Now the custom errors section is used to allow the Asp.Net application to control the behavior of the page when an error (Exception) is raised. (MSDN) When the custom Errors is set to On or RemoteOnly when an application exception happens the application will use the rules defined in the Web.config to either display the error message or redirect to a page.
3) Using the HandleErrorAttribute you can provide different redirections \ views based on the exception types raised.
I would recommend you view this SO topic for more information (read Elijah Manor's post). ASP.NET MVC HandleError
Cheers.
Background:
On a public facing ASP.NET MVC4 application, sometimes I receive inbound requests to a bad URL. The referrer is from outside so my app which is out of my control (I'm not generating the bad URL in my app). So MVC correctly raises an exception AND the user sees the custom error page. The global.asax is coded to email errors to me.
Problem. Although the URL is bad, the error I'm receiving is unexpected.
Ex:
- User navigates (from an external URL) to /Blog/View
- The Blog controller does not have a View action
- The user is presented with the Error500 custom error web page
- The error I receive by email is:
The view 'Error' or its master was not found or no view engine
supports the searched locations. The following locations were
searched: ~/Views/Blog/Error.aspx ~/Views/Blog/Error.ascx
~/Views/Shared/Error.aspx ~/Views/Shared/Error.ascx
~/Views/Blog/Error.cshtml ~/Views/Blog/Error.vbhtml
~/Views/Shared/Error.cshtml ~/Views/Shared/Error.vbhtml
I don't understand why ASP.NET MVC4 is looking for a view named "Error", and why MVC does not search for the view as specified (Error500) in the web.config. Here are the applicable source files:
The Web.Config:
<customErrors mode="RemoteOnly" defaultRedirect="~/Error/Error500">
<error statusCode="404" redirect="~/Error/Error404" />
</customErrors>
The ErrorController file:
public class ErrorController : Controller
{
public ActionResult Error500()
{
return View();
}
public ActionResult Error404()
{
return View();
}
}
The Error404.cshtml file (located in the /Views/Error folder):
#{
ViewBag.Title = "Oops...";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>That's interesting</h1>
<p>The page you were looking for could not be found.</p>
The Error500.cshtml file (located in the /Views/Error folder):
#{
ViewBag.Title = "Oops...";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>We're sorry about that</h1>
<p>Something unexpected just happened. Our IT staff has been notified...time to code a hot-fix!</p>
Are you sure that the user is hitting a URL that does not exist? It seems from a small repro that I just made that this works as desired if the action method does not exist. When an error is thrown inside an action method that was resolved by the routing and you are using the HandleErrorAttribute then you will get the error that you mention. What does your routing look like?
As an aside emailing your self on error is not a sustainable route to receiving errors about your application! You should look into an error logging service to handle this for you. I recommend Bugsnag. (Disclaimer: I work at Bugsnag :))
I have an ASP.Net MVC 2 web application where I have HomeController. Index method generates unauthorized exception. I have customErrors=on. Now I want to show a view Error.aspx in shared folder with Exception.Message. But I get Resource not found /Error in browser.
Note: I had gone through all the related links on StackOverflow.
any help?
Step by step:
Create a new ASP.NET MVC project using the default visual Studio wizard
Modify the HomeController so that it looks like this:
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
throw new Exception("oops");
}
}
Add the following to the <system.web> section of web.config:
<customErrors mode="On" />
Run the application
As expected the ~/Views/Shared/Error.aspx is shown
I am using the Authorize attribute like this:
[Authorize (Roles="Admin, User")]
Public ActionResult Index(int id)
{
// blah
}
When a user is not in the specified roles, I get an error page (resource not found). So I put the HandleError attribute in also.
[Authorize (Roles="Admin, User"), HandleError]
Public ActionResult Index(int id)
{
// blah
}
Now it goes to the Login page, if the user is not in the specified roles.
How do I get it to go to an Unauthorized page instead of the login page, when a user does not meet one of the required roles? And if a different error occurs, how do I distinguish that error from an Unauthorized error and handle it differently?
Add something like this to your web.config:
<customErrors mode="On" defaultRedirect="~/Login">
<error statusCode="401" redirect="~/Unauthorized" />
<error statusCode="404" redirect="~/PageNotFound" />
</customErrors>
You should obviously create the /PageNotFound and /Unauthorized routes, actions and views.
EDIT: I'm sorry, I apparently didn't understand the problem thoroughly.
The problem is that when the AuthorizeAttribute filter is executed, it decides that the user does not fit the requirements (he/she may be logged in, but is not in a correct role). It therefore sets the response status code to 401. This is intercepted by the FormsAuthentication module which will then perform the redirect.
I see two alternatives:
Disable the defaultRedirect.
Create your own IAuthorizationFilter. Derive from AuthorizeAttribute and override HandleUnauthorizedRequest. In this method, if the user is authenticated do a redirect to /Unauthorized
I don't like either: the defaultRedirect functionality is nice and not something you want to implement yourself. The second approach results in the user being served a visually correct "You are not authorized"-page, but the HTTP status codes will not be the desired 401.
I don't know enough about HttpModules to say whether this can be circumvented with a a tolerable hack.
EDIT 2:
How about implementing your own IAuthorizationFilter in the following way: download the MVC2 code from CodePlex and "borrow" the code for AuthorizeAttribute. Change the OnAuthorization method to look like
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
if (AuthorizeCore(filterContext.HttpContext))
{
HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
}
// Is user logged in?
else if(filterContext.HttpContext.User.Identity.IsAuthenticated)
{
// Redirect to custom Unauthorized page
filterContext.Result = new RedirectResult(unauthorizedUrl);
}
else {
// Handle in the usual way
HandleUnauthorizedRequest(filterContext);
}
}
where unauthorizedUrl is either a property on the filter or read from Web.config.
You could also inherit from AuthorizeAttribute and override OnAuthorization, but you would end up writing a couple of private methods which are already in AuthorizeAttribute.
You could do this in two ways:
Specify the error the HandleError-attribute, and give a view that should be shown:
[HandleError(ExceptionType = typeof(UnAuthorizedException),
View = "UnauthorizedError")]
You can specify several different ExceptionTypes and views
Create a custom ActionFilter, check there for credentials, and redirect to a controller if the user is unauthorized.: http://web.archive.org/web/20090322055514/http://msdn.microsoft.com/en-us/library/dd381609.aspx
Perhaps a 403 status code is more appropriate based on your question (the user is identified, but their account is not privileged enough). 401 is for the case where you do not know what priveleges the user has.
And HttpUnauthorizedResult (this comes as reuslt of the AuthorizeAtrribute) just sets StatusCode to 401. So probably you can setup 401 page in IIS or custom error pages in web.config. Of course, you also have to ensure that access to your custom error page is not requires authorization.
Just override the HandleUnauthorizedRequest method of AuthorizeAttribute. If this method is called, but the user IS authenticated, then you can redirect to your "not authorized" page.
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { Area = "", Controller = "Error", Action = "Unauthorized" }));
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
}