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.
Related
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 have a custom attribute that I am using for Ajax requests to prevent the default FormsAuthentication workflow from happening (since it doesn't make sense for an Ajax request).
This is the custom authorize attribute:
public class AjaxAuthorize : AuthorizeAttribute {
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
UrlHelper urlHelper;
if (filterContext.HttpContext.Request.IsAjaxRequest()) {
urlHelper = new UrlHelper(filterContext.RequestContext);
filterContext.HttpContext.Response.StatusCode = 401;
//Don't let IIS7 be mean.
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
//Return JSON which tells the client where the login page exists if the user wants to authenticate.
filterContext.HttpContext.Response.Write(new JavaScriptSerializer().Serialize(
new {
LoginUrl = string.Format("{0}?ReturnURL={1}", FormsAuthentication.LoginUrl, urlHelper.Encode(filterContext.HttpContext.Request.Url.PathAndQuery))
}
));
filterContext.HttpContext.Response.End();
} else {
base.HandleUnauthorizedRequest(filterContext);
}
}
}
I have my ActionResult decorated with the custom authorize attribute like so:
[HttpPut]
[ValidateInput(false)]
[AjaxAuthorize]
public ActionResult Comment(string id, string comment) {
//stuff happens here.
}
An exception is thrown inside of my action result because I try to access information about a user that would only exist if you are logged in. But, I don't understand why this code is even being executed because I have my authorization attribute protecting the action.
protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
filterContext.Result = new HttpUnauthorizedResult();
}
This is default implementation of HandleUnauthorizedRequest. Probably you need to assign your custom ActionResult that contains JSON you need and valid status code. I'm not sure direct processing request object from HTTP context would work.
I ended up returning an HTTP 418 (I have been wanting to use this for awhile) so that the Forms Authentication module didn't intercept the request. Then in my script I look for that status code and handle it as an unauthorized request and do the proper redirect.
Try calling base.HandleUnauthorizedRequest(filterContext); inside the IF block. Or just remove the ELSE block and always call base.HandleUnauthorizedRequest(filterContext);.
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.
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 have a project using Struts2 on the server side and I am trying to make it work with jqGrid (using JSON format). I have several tables made with jqGrid and I am using the add/edit/delete buttons from navGrid.
The main problem I have is with server validation error messages. I have created custom validators and they work with jsp pages, using s:fielderror, but I don't know how to make them work for add/edit popups from jqGrid. I am aware that jqGrid provides the users with custom validation on client, but this has its limitations(think about testing whether the email of a user is unique, you definitely must use the database for that, or if some fields depend on each other and must be tested together, like if isManager is true, then the managerCode must be not empty and vice versa...).
When I use the client validation, there is a message in the add/edit window whenever an error occurs. Can I somehow display my server validation error messages in the window in the same way?
I managed to solve the issue. I will explain how using a simple custom validator for age field, which must be > 18 for an Employee. It is supposed next that the validator was already declared in validators.xml and mapped on the action and that the message in case of ValidationException is "An employee should be older than 18.".
Using Firebug, I figured out that the id of the error area in the form is FormError. It is possible to configure a callback function errorTextFormat in jqgrid, in order to get a response from the server and process it. In the jqgrid configuration, one could write
errorTextFormat : errorFormat,
with
var errorFormat = function(response) {
var text = response.responseText;
$('#FormError').text(text); //sets the text in the error area to the validation //message from the server
return text;
};
The problem is now that the server will send implicitly a response containing the whole exception stack trace. To deal with it, I decided to create a new result type.
public class MyResult implements Result {
/**
*
*/
private static final long serialVersionUID = -6814596446076941639L;
private int errorCode = 500;
public void execute(ActionInvocation invocation) throws Exception {
ActionContext actionContext = invocation.getInvocationContext();
HttpServletResponse response = (HttpServletResponse) actionContext
.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse");
Exception exception = (Exception) actionContext
.getValueStack().findValue("exception");
response.setStatus(getErrorCode());
try {
PrintWriter out = response.getWriter();
out.print(exception.getMessage());
} catch (IOException e) {
throw e;
}
}
/**
* #return the errorCode
*/
public int getErrorCode() {
return errorCode;
}
/**
* #param errorCode the errorCode to set
*/
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
}
It must also be configured in struts.xml as follows:
<package name="default" abstract="true" extends="struts-default">
...
<result-types>
<result-type name="validationError"
class="exercises.ex5.result.MyResult">
</result-type>
</result-types>
...
<action name="myaction">
...
<result name="validationException" type="validationError"></result>
<exception-mapping result="validationException"
exception="java.lang.Exception"></exception-mapping>
</action>
...
</package>
These are the steps I followed to get a validation error message in the add/edit window and now it works.