I have custom errors turned on in webconfig and redirecting to "/Error/Trouble". This is working as designed. Elmah is logging the error. The error view is being displayed too.
The problem is I want to inspect the thrown error in the Trouble action of my Error controller. When an error is thrown, how do you get access to it after MVC has redirected you to the custom error handler?
I'm throwing an exception if CurrentUser is null:
if (CurrentUser == null)
{
var message = String.Format("{0} is not known. Please contact your administrator.", context.HttpContext.User.Identity.Name);
throw new Exception(message, new Exception("Inner Exception"));
}
I want to be able to access this in my custom error handler ("Error/Trouble"). How do you access the exception?
Here's my trouble action:
public ActionResult Trouble()
{
return View("Error");
}
Here's my view:
#model System.Web.Mvc.HandleErrorInfo
<h2>
Sorry, an error occurred while processing your request.
</h2>
#if (Model != null)
{
<p>#Model.Exception.Message</p>
<p>#Model.Exception.GetType().Name<br />
thrown in #Model.ControllerName #Model.ActionName</p>
<p>Error Details:</p>
<p>#Model.Exception.Message</p>
}
System.Web.Mvc.HandleErrorInfo is the model for my Trouble view and it's empty. Thanks for your help.
I found a workaround:
in Global.asax I do this:
protected void Application_Error()
{
var exception = Server.GetLastError();
HttpContext.Current.Application.Lock();
HttpContext.Current.Application["TheException"] = exception;
HttpContext.Current.Application.UnLock();
}
In Error/Trouble I do this:
var caughtException = (Exception)HttpContext.Application["TheException"];
var message = (caughtException!= null) ? caughtException.Message : "Ooops, something unexpected happened. Please contact your system administrator";
var ex = new Exception(message);
var errorInfo = new HandleErrorInfo(ex, "Application", "Trouble");
return View("Error", errorInfo);
This is working. But it seems like a weird way to go about it. Does anyone have a better solution? Thanks for your help.
Related
I am developing a MVC application which has handles authorization and login information at a base controller class overriding OnActionExecuting event.
At AJAX calls when an exception arises I can handle this via attributes and display error messages with on custom model window.
My custom attribute is as follows :
public class JsonExceptionFilterAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = 500;
filterContext.ExceptionHandled = true;
string msg = HttpUtility.HtmlDecode(filterContext.Exception.Message);
if (filterContext.Exception.GetType() == Type.GetType("System.UnauthorizedAccessException"))
{
msg = "Unauthorized access";
}
filterContext.Result = new JsonResult
{
Data = new
{
errorMessage = msg
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
}
}
But when an Exception occurs while using DataTables.Net the only mesage I get is the DataTables.NET's own error mesaages saying "... please see http://datatables.net/tn/7"
But I want to display my own Exception message as I do in other AJAX calls. Basically I want to display the error message I provide in my custom attribute response.
I have Googled it, advising to use "fnServerData" but I can not find my Exception message in any of the parameters (sSource, aoData, fnCallback, oSettings) I get by this event handler.
How can I possibly get the Exception message that my base controller returns and display it?
Regards.
P.S. : Handling the Exception in the Action and returning it does not apply here, because I do not fall to Action method at all.
In my WebApi code, I raise a HttpResponseException which short-circuits the request pipeline and generates a valid Http response. However, I'm trying to integrate webApi with elmah logging, yet the HttpResponseExeptions aren't showing up.
I have the web.config set-up for elmah and have the following code:
In Global.asx.cs:
static void ConfigureWebApi(HttpConfiguration config)
{
config.Filters.Add(new ServiceLayerExceptionFilter());
config.Filters.Add(new ElmahHandledErrorLoggerFilter());
config.DependencyResolver = new WebApiDependencyResolver(ObjectFactory.Container);
}
Filter:
public class ElmahHandledErrorLoggerFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
base.OnException(actionExecutedContext);
ErrorSignal.FromCurrentContext().Raise(actionExecutedContext.Exception);
}
}
Code where exception is raised:
public Task<FileUpModel> UploadFile()
{
if (Request.Content.IsMimeMultipartContent())
{
var provider = new TolMobileFormDataStreamProvider("C:\images\");
var task = Request.Content.ReadAsMultipartAsync(provider).ContinueWith(
t =>
{
if (t.IsFaulted || t.IsCanceled)
throw new HttpResponseException(HttpStatusCode.InternalServerError);
var fileInfo = provider.FileData.FirstOrDefault();
if (fileInfo == null)
// the exception here isn't logged by Elmah?!
throw new HttpResponseException(HttpStatusCode.InternalServerError);
var uploadModel = new FileUpModel { success = true };
return uploadModel;
});
return task;
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
}
}
Can anyone who has implemented this before let me know what I'm doing wrong?
As mentioned above, the Elmah filter does not catch and log anything when you raise a HttpResponseException. More specifically, if the following syntax is used:
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "It was a bad request");
or
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "HttpResponseException - This request is not properly formatted"));
I wanted to trap and log an error in both cases. The way to do it is to use an "ActionFilterAttribute", override "OnActionExecuted", and check actionExecutedContext.Response.IsSuccessStatusCode.
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
// when actionExecutedContext.Response is null, the error will be caught and logged by the Elmah filter
if ((actionExecutedContext.Response != null) && !actionExecutedContext.Response.IsSuccessStatusCode)
{
try
{
var messages = (System.Web.Http.HttpError)((System.Net.Http.ObjectContent<System.Web.Http.HttpError>)actionExecutedContext.Response.Content).Value;
StringBuilder stringBuilder = new StringBuilder();
foreach (var keyValuePair in messages) {
stringBuilder.AppendLine("Message: Key - " + keyValuePair.Key + ", Value - " + keyValuePair.Value);
}
Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Web API Failed Status Code returned - " + stringBuilder.ToString()));
}
catch (Exception ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Error in OnActionExecuted - " + ex.ToString()));
}
}
}
On a side note, I also overwrote "OnActionExecuting" to validate the model state. This allowed me to remove all of the checks within my actions.
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext.ModelState != null && !actionContext.ModelState.IsValid)
{
StringBuilder stringBuilder = new StringBuilder();
foreach (var obj in actionContext.ModelState.Values)
{
foreach (var error in obj.Errors)
{
if(!string.IsNullOrEmpty(error.ErrorMessage)) {
stringBuilder.AppendLine("Error: " + error.ErrorMessage);
}
}
}
Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Invalid Model State -- " + stringBuilder.ToString()));
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
Of course, you will need to add the filter using "config.Filters.Add".
Web API special cases HttpResponseException thrown in action and converts into HttpResponseMessage and hence you are not seeing your exception filter getting invoked.
This is not true in the case of throwing HttpResponseException from filters. However, ideally one need not throw HttpResponseException from filters as you could short-circuit a request by setting the Response property on the supplied input context.
You need to turn on Elmah for HttpFilters in order to get this to work as you expect for WebApi.
Use Elmah.Contrib.WebApi available as a NuGet Package, it will wire include a class that you can then wire up following the instructions on the Elmah.Contrib.WebApi project site.
If you want to do this yourself, Capturing Unhandled Exceptions in ASP.NET Web API's with ELMAH walks you through what the Elmah.Contrib.WebApi is doing for you.
Additionally, I had to change the way that the error response is thrown for it to be picked by Elmah to:
throw new HttpException((int)HttpStatusCode.NotAcceptable, "This request is not properly formatted");
I would also recommend the use of the Elmah.MVC NuGet Package.
I have set up an mvc app with an _error.cshtml that is set to catch exceptions I throw in the controller.
I also have a few ajax posts on some pages that checks for errors and then it does something else.
On the server, I have a filter on all exceptions and then check if it is an ajax request and return something that can be deserialized on the client. The problem is that if I do not set the post response status code to 500 then ajax will not see this error and I can't show a nice message. If I set the status to 500 I get the default IIS error message stating something happened on the server.
I would like to handle some errors on the page in the ajax results but maintain the generic error handling. Is this an IIS setting to allow custom 500 message per site? The web.config Custom Error On|Off makes no difference in my case.
The filter you have on all exceptions that is checking if its an ajax request, is that a filter made on your own?
I had a slightly similar issue, and I had to make sure the flag TrySkipIisCustomErrors was set as true in order to avoid the standard IIS error.
This flag is located on the Response object of the HttpContext.
This is also done by the standard HandleError filter, pay attention to the last line in its implementation of the OnException method:
public virtual void OnException(ExceptionContext filterContext) {
if (filterContext == null) {
throw new ArgumentNullException("filterContext");
}
if (filterContext.IsChildAction) {
return;
}
// If custom errors are disabled, we need to let the normal ASP.NET exception handler
// execute so that the user can see useful debugging information.
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) {
return;
}
Exception exception = filterContext.Exception;
// If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
// ignore it.
if (new HttpException(null, exception).GetHttpCode() != 500) {
return;
}
if (!ExceptionType.IsInstanceOfType(exception)) {
return;
}
string controllerName = (string)filterContext.RouteData.Values["controller"];
string actionName = (string)filterContext.RouteData.Values["action"];
HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
filterContext.Result = new ViewResult {
ViewName = View,
MasterName = Master,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
TempData = filterContext.Controller.TempData
};
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = 500;
// Certain versions of IIS will sometimes use their own error page when
// they detect a server error. Setting this property indicates that we
// want it to try to render ASP.NET MVC's error page instead.
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
I want to handle all server-side errors with jQuery on client side. For this purpose a create axception handling attrbute for my MVC3 application like this:
public class JsonErrorHandlerAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
string result = jsSerializer.Serialize(new { error = filterContext.Exception.Message });
filterContext.HttpContext.Response.Write(result);
}
}
But with this approach it just returns normal json response with 200 OK result. A don't want to parse it on client side to determine if it has an error or not. So my question is what is the best way to throw an ajax error?
Add this to your function
HttpContext.Response.StatusCode = 500;
This will raise the error event if you use jQuery ajax, there you can react.
I am trying for custom error handling at global level in Application_Error.
Exception ex = Server.GetLastError();
Server.ClearError();
AssortmentDefinitionLogManager.LogException(ex);
Context.RewritePath("/Error/Error");
IHttpHandler httpHandler = new MvcHttpHandler();
httpHandler.ProcessRequest(Context);
But i get this error
Error Message - The view '~/Views/Shared/Error' or its master was not found or no view engine supports the searched locations.
I have also tried this
var urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
var redirectUrl = urlHelper.Action("Error", "Error");
Response.Redirect(redirectUrl);
create a controller called ErrorController and inside this controller create an action method called Error which returns an ActionResult (or ViewResult).
Then create a view under ~/Views/Error/ called error.cshtml.
This will solve your problem.