ASP.NET MVC properly handling invalid URLs - asp.net-mvc-3

I'm trying to properly handle and return a 404 for this URL: http://localhost:2867/dd./xml (notice the dot before the slash)
In my current implementation I get 4 exceptions/errors in Application_Error. The first exception returned by Server.GetLastError() is System.Web.HttpException while the next three are null.
I made a bare minimum implementation to reproduce this issue. Here's the code in global.asax.cs:
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
Server.ClearError();
var routeData = new RouteData();
routeData.Values.Add("controller", "Error");
routeData.Values.Add("action", "Generic");
routeData.Values.Add("area", "");
IController errorController = new ErrorController();
// this line throws System.Web.HttpException is a view is returned from ErrorController
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
The error controller looks like this:
public class ErrorController : Controller
{
public ActionResult Generic()
{
Response.TrySkipIisCustomErrors = true;
Response.StatusCode = (int)HttpStatusCode.NotFound;
return View();
// returning content rather than a View doesn't fire 'System.Web.HttpException' in Application_Error
//return Content("Some error!");
}
}
There are two issues. One is that for the given URL instead of one error in Application_Error I get 3 or 4, and the other is that when returning a view from the ErrorController an exception is thrown on the Execute call line in Application_Start. If a Content("something") is returned instead this internal (to MVC I assume) exception is not triggered.
In order to see the problem you have to be in debug mode and use the development server. When using IIS or IIS Express the error is not caught for some reason. Also, every now and then these errors go away. To get it back to the beginning you have to clean the solution.
If you'd like to play with it here's the bare minimum solution: http://dl.dropbox.com/u/16605600/InvalidUrl.zip
Thank you for your help!

If you're using IIS7+ putting this in the web.config works:
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" />
<error statusCode="404" responseMode="ExecuteURL" path="/Error/PageNotFound" />
</httpErrors>
</system.webServer>
(Answer from How can I properly handle 404 in ASP.NET MVC?)
Would still be nice to know what is going on in the Application_Error.

You can handle a 404 by changing the customeErrors section in the web.config
There is also a redirectMode attribute which you can use to control the nature of the error page redirection (and avoid the 302) (Read here)
<configuration>
...
<system.web>
<customErrors mode="RemoteOnly"
redirectMode="ResponseRewrite"
defaultRedirect="/ErrorPages/Oops.aspx">
<error statusCode="404" redirect="/ErrorPages/404.aspx" />
</customErrors>
...
http://www.asp.net/hosting/tutorials/displaying-a-custom-error-page-cs
In ASP.net MVC, there is a method you can override to catch all exceptions thrown in a controller. Just override Controller.OnException(...) and you can do custom error handling in there as well. If all of your controllers inherit from a common base controller class, you can put the error handling there.
http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.onexception.aspx

Related

Struts validations problems [duplicate]

I have following defined in
struts-config.xml:
<struts-config>
<form-beans>
<form-bean name="LoginForm" type="com.actionform.LoginForm"/>
</form-beans>
<action-mappings>
<!-- action for login -->
<action input="/views/login.jsp" name="LoginForm" path="/Login" scope="session" type="com.actions.LoginAction"
parameter="method" validate="true">
<forward name="success" path="/views/Frameset.html" />
</action>
</action-mappings>
<message-resources parameter="/WEB-INF/ApplicationResources"/>
<!-- ========================= Validator plugin ================================= -->
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property
property="pathnames"
value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
</plug-in>
</struts-config>
The login form:
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
if (userName == null || userName.length() < 1) {
System.out.println("in validate ---");
errors.add("userName", new ActionMessage("error.userName.required"));
// TODO: add 'error.name.required' key to your resources
}
if (password == null || password.length() < 1) {
errors.add("password", new ActionMessage("error.password.required"));
// TODO: add 'error.name.required' key to your resources
}
return errors;
}
login.jsp:
<html:form action="/Login?method=loginUser">
<html:errors/>
<html:text name="LoginForm" property="userName" />
<html:messages id="err_userName" property="userName">
<bean:write name="err_userName" />
</html:messages>
</html:form>
Property file:
error.userName.required = User Name is required.
error.password.required = Password is required.
Where am I doing wrong? I am getting the following error
javax.servlet.jsp.JspException: Cannot find bean error in any scope
I just want to display the error in the same JSP.
After you obtain the ActionMessages/ActionErrors object which contains the messages or errors you want to display in your input page (using <html:messages> tags or <html:errors> tags), you must call one of the following methods from your Action object to place the result of your validation in scope:
addMessages(HttpServletRequest request, ActionMessages messages)
or
addErrors(HttpServletRequest request, ActionMessages errors)
Are you doing that?
I'm really do not care how about struts handles an exception. Using old raw code from 1.2 generally when overriding a RequestProcesor, I should probably replace the two methods - process and processException. First thing is happy about catching exception from the request after processValidation has been made. The fragment of code could look like
Exception exception = null;
if (needValidation)
try {
if (! processValidate(request, response, form, mapping)) {
return;
}
exception = (Exception)request.getAttribute(Globals.EXCEPTION_KEY);
} catch (InvalidCancelException ex) {
exception = ex;
}
ActionForward forward;
// Check out if exception occurred
if (exception != null){
forward = processException(request, response, exception, form, mapping);
The second is pretty easy if you have configured the errors forward. The errors forward is usually one of the global forwards that easily found from the mapping. Once it found, it likes to display your error message on the page. I think those would probably enough for processing an exception
exception.printStackTrace();
log.error(exception);
request.setAttribute("error", exception.getMessage());
return mapping.findForward("error");
It has been done because validate method from ActionForm or ValidatorForm doesn't throw any exceptions and I couldn't properly override this method without throwing some. Once thrown, who will care about it?!

"Down for Maintenance" page for Asp.net MVC web app

I have the following requirements
Set a value in web.config and enable maintenance mode
All non-ajax requests should be shown a custom error page, with the http status code set to 503. The url of page should be retained.
All ajax requests should be responded with http status code 503
I should have an opportunity to do some basic logging to a file. Log the url and the user Identity if he happened to be logged into the app
I am using ELMAH to track/log all unhandled exceptions. The mechanism for implementing maintenance mode shouldn't need me to not use ELMAH
I have "runAllManagedModulesForAllRequests" set to true. this was originally done for use with RequestReduce. we no longer use it, but I am hesitant to reset its value to false. I am not sure if any other library needs it.
Once I realized there is nothing built in which supports the above requirements, I felt I had the following two options in front of me (App_offile.html won't work for me).
an HttpModule
an MVC ActionFilter
I dropped the MVC ActionFilter as I couldn't figure out how to guarantee it to run before any authentication/authorization filters. I have a custom authentication filter which hits the db. The idea behind the maintenance mode is the db might be offline, yet the web-app shouldn't show a 500 custom error page, but a 503 custom error page.
I wrote the following httpmodule and added in my web.config. It works for ajax requests. It kinda works for non-ajax requests. All requests get redirected to the 503 error page. The side-effect is all requests for static content also result in a 503. My error page thus is shown unstyled :(
// the http module
public class MaintenanceModeModule : IHttpModule
{
private static bool _isUnderMaintenance;
static MaintenanceModeModule()
{
var valueStr = (ConfigurationManager.AppSettings["UnderMaintenance"] ?? (false).ToString());
bool underMaintenance;
bool.TryParse(valueStr, out underMaintenance);
_isUnderMaintenance = underMaintenance;
}
public void Init(HttpApplication application)
{
application.BeginRequest += OnBeginRequest;
}
private void OnBeginRequest(object sender, EventArgs e)
{
var application = (HttpApplication) sender;
var request = application.Request;
var response = application.Response;
if (_isUnderMaintenance == false)
{
return;
}
application.Context.Items["under_maintenance"] = true; // used later
if (request.Url.PathAndQuery == "/503") // the url of the action that renders the custom error page
{
return;
}
const int statusCode = (int) HttpStatusCode.ServiceUnavailable;
const string statusMessage = "Temporarily down for maintenance";
var requestWrapper = new HttpRequestWrapper(request);
if (requestWrapper.IsAjaxRequest())
{
response.Clear();
response.ClearContent();
response.ClearHeaders();
response.StatusCode = statusCode;
response.TrySkipIisCustomErrors = true;
response.StatusDescription = statusMessage;
response.End();
return;
}
// doesn't work, shows the Yellow Screen of Death (YSoD)
// application.Context.Server.Transfer("~/503", preserveForm: true);
// doesn't work, shows the Yellow Screen of Death (YSoD)
// throw new HttpException(statusCode, statusMessage);
response.Redirect("~/503");
}
public void Dispose()
{
}
}
...
// web.config
// only the relevant portions of each section is shown
<appSettings>
<add key="UnderMaintenance" value="true" />
</appSettings>
<customErrors mode="On"> <!-- Custom errors are on, even then I was seeing YSoDs during my attempts -->
<error statusCode="404" redirect="404" />
<error statusCode="503" redirect="503" />
</customErrors>
<system.webServer>
<httpErrors existingResponse="PassThrough">
</httpErrors>
<modules runAllManagedModulesForAllRequests="true">
<add name="MaintenanceMode" type="WebApp.Code.MvcInfrastructure.MaintenanceModeModule" />
</modules>
</system.webServer>
...
// route config
routes.MapRoute("503Error", "503", new { controller = "Error", action = "UnderMaintenance" });
...
// error controller
// the authentication filter skips authentication if the allowanonymous attribute is present
[AllowAnonymous]
public class ErrorController : CustomBaseController
{
public ErrorController(AppConfig appConfig)
: base(appConfig)
{
}
public ActionResult UnderMaintenance()
{
// behind the scenes reads the value from HttpContext.Items.
// This was set during the execution of the httpmodule
if (AppConfig.UnderMaintenance == false)
{
return new RedirectResult("~/");
}
Response.StatusCode = (int) HttpStatusCode.ServiceUnavailable;
Response.TrySkipIisCustomErrors = true;
// the actual content of the view is not relevant now
return View("Error503");
}
}
The problems with this approach,
Each non-ajax request is responded with a 302 and then a 503
The URL requested by the browser is not retained
It returns a 503 for all static assets as well
The code I wrote and web.config settings I enabled are all cobbled together from various sources. I am not fully sure what those settings do or what the recommended way is. Please feel free to answer with a completely different method, as long as it can meet the requirements stated.

keep getting The view "Error" not found when using Elmah and asp.net mvc 4

I am using Elmah 1.2 as the logging framework for my asp.net mvc 4 application.
in the web.config file, I set customErrors mode to on.
<customErrors mode="On" defaultRedirect="/Error">
<error statusCode="404" redirect="/Error/NotFound" />
</customErrors>
I also created a custom HandleErrorAttribute, copied the code from this link.
http://joel.net/logging-errors-with-elmah-in-asp.net-mvc-3--part-4--handleerrorattribute
In my Home controller, i just throw an exception to test the logging framework.
public ActionResult About()
{
throw new Exception("this is a buggggggggggggg");
ViewBag.Message = "Your app description page.";
return View();
}
"this is a buggggggggggggg" is logged in the database, great, it works. then there's another error also logged, and I didnt expect that to happen.
The view 'Error' or its master was not found or no view engine supports the searched locations. The following locations were searched: ~/Views/Home/Error.aspx ~/Views/Home/Error.ascx ~/Views/Shared/Error.aspx ~/Views/Shared/Error.ascx ~/Views/Home/Error.cshtml ~/Views/Home/Error.vbhtml ~/Views/Shared/Error.cshtml ~/Views/Shared/Error.vbhtml
Update:
follow Tim's suggestion, then it causes another issue.
If I create a Error.cshtml in the shared folder. when unhandled exception happens, it will show this Error.cshtml file, not "/Error" page. I have customErrors enabled. They should all get redirected to "/Error" page.
We created an empty MVC5 app and added ELMAH to it. We also were receiving the extra error you described even though we did not add the HandleErrorAttribute. After some research I found the nuget package Elmah.MVC which adds some additional configuration settings. In the appSettings section of web.config you will find these 2 lines:
<appSettings>
<add key="elmah.mvc.disableHandler" value="false" />
<add key="elmah.mvc.disableHandleErrorFilter" value="false" />
</appSettings>
These 2 keys default to "false". I changed their values to "true" and the extra logged exception went away.
I am developing an application using ASP.NET MVC 5 RC and I use Elmah too for error logging. I am using too a custom error handling attribute to redirect errors to a custom action on a custom controller, but mine doesn't look like the one shown in the link you provided.
However I had the same problem: Elmah was properly logging the error, but was also adding a "Error view not found" entry. I solved this by adding the following line to the OnException method on the attribute:
filterContext.ExceptionHandled = true;
For completeness, this is the complete code for the custom error handling attribute I am using:
public class CustomHandleErrorAttribute: HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
if(filterContext.HttpContext.Request.IsAjaxRequest()) {
filterContext.HttpContext.Response.StatusCode =
(int)HttpStatusCode.InternalServerError;
filterContext.Result = new ContentResult() {
Content = "Server error",
ContentType = "text/plain"
};
}
else {
filterContext.Result = new RedirectToRouteResult(
"Default",
new System.Web.Routing.RouteValueDictionary(new
{
controller = "Error",
action = "ApplicationError"
}));
}
}
}

Removing headers from the response

I need to cloak certain headers generated by ASP.NET and IIS and returned in the responses from a ASP.NET WebAPI service. The headers I need to cloak are:
Server
X-AspNet-Version
X-AspNetMvc-Version
X-Powered-By
The service was earlier hosted in WCF, and the cloaking was done in an HttpModule by subscribing to PreSendRequestHeaders and manipulating HttpContext.Current.Response.Headers. With ASP.NET WebAPI everything is now task based, so HttpContext.Current is null. I tried to insert a message handler and manipulate the returned HttpResponseMessage, but the headers were not present on that stage. X-Powered-By can be removed in the IIS settings, but what is the suggested way to remove the rest of them?
The problem is each one is added at a different point:
Server: added by IIS. Not exactly sure if it can be turned off although you seem to have been to remove it using HttpModule .
X-AspNet-Version: added by System.Web.dll at the time of Flush in HttpResponse class
X-AspNetMvc-Version: Added by MvcHandler in System.Web.dll. It can be overridden so this one should be OK.
X-Powered-By by IIS but can be turned off as you said.
I think your best bet is still using HttpModules.
For the benefit of those who land here through a google/bing search::
Here's the summary of steps:
Step 1: Create a class that derives from IHttpModule (and IDisposable to clean up when we're done):
public class MyCustomModule : IHttpModule, IDisposable
{
private HttpApplication _httpApplication
private static readonly List<string> HeadersToCloak = new List<string>
{
"Server",
"X-AspNet-Version",
"X-AspNetMvc-Version",
"X-Powered-By"
};
..
}
Step 2: Get a reference to the intrinsic context in the IHttpModule.Init method, and assign an event handler to the PreSendRequestHeaders event:
public void Init(HttpApplication context)
{
_httpApplication = context;
context.PreSendRequestHeaders += OnPreSendRequestHeaders;
}
Step 3: Now the headers can be removed like so:
private void OnPreSendRequestHeaders(object sender, EventArgs e)
{
if (null == _httpApplication)
{
return;
}
if (_httpApplication.Context != null)
{
var response = _httpApplication.Response;
HeadersToCloak.ForEach(header => response.Headers.Remove(header));
}
}
Step 4: Now register this module in your root web.config under the system.webserver (if running IIS 7.0 integrated mode more details here):
<configuration>
<system.webServer>
<modules>
<add name="MyCustomModule" type="<namespace>.MyCustomModule "/>
</modules>
</system.webServer>
</configuration>
Hope this helps!
If you're using IIS7 / Azure then have a look at this:
Removing/Hiding/Disabling excessive HTTP response headers in Azure/IIS7 without UrlScan
It shows the best way to disable these headers without using HttpModules.
if you like to remove version go to web.config file
and add these lines
<system.web>
<compilation debug="true" targetFramework="4.5.2" />
<!--enableVersionHeader remove the header-->
<httpRuntime targetFramework="4.5.2" enableVersionHeader = "false"/>
also, add these
<httpProtocol>
<customHeaders>
<!--enableVersionHeader remove the header-->
<remove name ="X-Powered-By"/>
</customHeaders>
</httpProtocol>

Errors with custom HandleErrorAttribute does not work

I've read many articles and several post (including here in stackoverflow) but do not know what I'm doing wrong.
Here my code:
Global.asax.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
ErrorControler.cs
public class ErrorController : Controller
{
public ActionResult Error404()
{
return View();
}
public ActionResult Error500()
{
return View();
}
}
Web.config
<customErrors mode="On" defaultRedirect="~/Error/Error500">
<error statusCode="404" redirect="~/Error/Error404"/>
</customErrors>
MyController.cs
public ActionResult Index()
{
using (var db = new DataContext())
{
int a = 2, b = 0;
var r = a / b;
return View(r);
}
}
Error500.cshtml
#model System.Web.Mvc.HandleErrorInfo
#{
ViewBag.Title = "Erro";
}
<h2>#ViewBag.Title</h2>
<div id="error-info">
<p>Ocorreu um erro inexperado na página <a class="error-url" href="#Response["aspxerrorpath"]" title="Origem do erro">#Response["aspxerrorpath"]</a></p>
<p>Se o erro persistir, por-favor, entre em contato com os administradores do site.</p>
<div class="error-details">
<p>#Model.Exception.Message</p>
</div>
</div>
When I try to access the path /MyController the following message appears:
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: /Error/Error500
I would like that happen when an error on any controller, if the http status code has not been informed in web.config, it redirects to the default view Error500
In this article, for example, it handles DbException errors, but would like to handle any type of error.
Errors of type 404 (page not found) works perfectly. The user is redirected to the page Error404.cshtml
If you want this to happen remove/comment the following line from your Global.asax:
filters.Add(new HandleErrorAttribute());
You basically have to choose whether you want ASP.NET to handle your errors (the <customErrors> section in your web.config) or ASp.NET MVC (the global HandleErrorAttribute action filter, which by the way requires you to turn on custom errors in web.config)
Or checkout an alternative method for handling errors in ASP.NET MVC (with this approach you still have to remove the line I showed from your Global.asax).

Resources