Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Send exception message in Ajax.BeginForm MVC 4 scenario

I have a partial view that contains an ajax form. The view simply adds and updates user information.

The controller sends the same partial view back as ActionResult.

What I want to do is to display an error message if the transaction was not successful. But it should still send back the partial view, only this time with a message.

How would this be achieved?

Code:

ManageUsers.cshtml

<div id="details">
@{
    Html.RenderPartial("AddModifyUserPartialView");
}
</div>

@{
    Html.RenderPartial("ListUsersPartialView");
 }

AddModifyUserPartialView.cshtml

@using (Ajax.BeginForm("AddModifyUser", "Account", FormMethod.Post, 
         new AjaxOptions() 
         { 
             UpdateTargetId = "details", 
             OnFailure= "handleError", 
             OnSuccess="handleSuccess" 
         }, 
         new { id = "useragentform", 
               enctype = "multipart/form-data" }))
{
  //form fields here
  <input type="submit" id="savebutton" name="savebutton" value="Add New User" />
}

Also in the partial view:

function handleError(ajaxContext) {

    var response = ajaxContext.get_response();
    var statusCode = response.get_statusCode();

    alert(statusCode);
}

Account Controller

try
{
     SecurityManager.AddUpdateUserAgent(ua);
}
catch (Exception ex)
{
     //how do I send the message back along with the partial view???
}
return PartialView("AddModifyUserPartialView");
like image 687
Raza Ali Avatar asked Sep 12 '13 12:09

Raza Ali


3 Answers

Two parts to solving this, create a new exception, let's call it StatusException, with your message, and throw it when you've caught the normal exception:

try
{
   SecurityManager.AddUpdateUserAgent(ua);
}
catch (Exception ex)
{
   throw new StatusException("Your error message here")
} 
return PartialView("AddModifyUserPartialView");

Override Controller::OnException and handle the exception by setting it to handled, setting the error code to 500, setting the HttpContext.Response.StatusDescription to your StatusException message. For example:

    protected override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.Exception == null) return;

        Type exceptionType = filterContext.Exception.GetType();

        if (exceptionType == typeof(StatusException))
        {

            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.Clear();
            filterContext.HttpContext.Response.ContentEncoding = Encoding.UTF8;
            filterContext.HttpContext.Response.HeaderEncoding = Encoding.UTF8;
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
            filterContext.HttpContext.Response.StatusCode = 500;
            filterContext.HttpContext.Response.StatusDescription = filterContext.Exception.Message;
        }
     }

Then, in your OnFailure handler for Ajax.BeginForm, display out the error parameter:

function handleError(data){
    //display data.errorThrown, data.statusCode, etc...
}

By setting the error code to 500 in the OnException override, AjaxForm will detect an error and jump into your handler. We set the StatusDescription in the override as well, so that message will be available in the handleError callback.

like image 168
Furynation Avatar answered Oct 24 '22 19:10

Furynation


The answer above is fine. A simple alternative might be:

if (gotAnError)
{
    Response.StatusCode = (int) System.Net.HttpStatusCode.BadRequest;  // Or another code
    return Json( new { message = "You did something wrong." } );
}

for the (partial) controller, and

function ShowPartialLoadErrors(result) {
    var obj = $.parseJSON(response.responseText);
    alert(obj.message);
}

for the view.

like image 45
Gerard Avatar answered Oct 24 '22 20:10

Gerard


One could argue that since your request is expecting only a partial view, your action should always return a partial view. If you encounter an error, pass to your view a View Model that is in an error state and let your view render appropriately.

AddUpdateUserVM vm = new AddUpdateUserVM();
try
{
    SecurityManager.AddUpdateUserAgent(ua);
}
catch (Exception ex)
{
     //log exception
     vm.HasError = true;
     vm.ErrorMessage = ex.Message;
}
return PartialView("AddModifyUserPartialView", vm);

View:

@if(Model.HasError)
{
    <div>@Model.ErrorMessage</div> @* Or whatever you want to display *@
}

@using (Ajax.BeginForm("AddModifyUser", "Account", FormMethod.Post, 
     new AjaxOptions() 
     { 
         UpdateTargetId = "details", 
         OnFailure= "handleError", 
         OnSuccess="handleSuccess" 
     }, 
     new { id = "useragentform", 
           enctype = "multipart/form-data" }))
{
  //form fields here
  <input type="submit" id="savebutton" name="savebutton" value="Add New User" />
}

The AjaxOptions' OnFailure function would then only occur if the server can't be reached or responds with some status code other than 200.Personally, I prefer not to use Ajax.BeginForm and to write my own jQuery ajax requests and let the server-side action method return a JSON object containing either an error or the requested view's markup. It gives you complete control over what you return from the server and what you do with it on the client side.

like image 2
xr280xr Avatar answered Oct 24 '22 20:10

xr280xr