jQuery Ajax error handling, show custom exception messages
Asked Answered
C

21

809

Is there some way I can show custom exception messages as an alert in my jQuery AJAX error message?

For example, if I want to throw an exception on the server side via Struts by throw new ApplicationException("User name already exists");, I want to catch this message ('user name already exists') in the jQuery AJAX error message.

jQuery("#save").click(function () {
  if (jQuery('#form').jVal()) {
    jQuery.ajax({
      type: "POST",
      url: "saveuser.do",
      dataType: "html",
      data: "userId=" + encodeURIComponent(trim(document.forms[0].userId.value)),
      success: function (response) {
        jQuery("#usergrid").trigger("reloadGrid");
        clear();
        alert("Details saved successfully!!!");
      },
      error: function (xhr, ajaxOptions, thrownError) {
        alert(xhr.status);
        alert(thrownError);
      }
    });
  }
});

On the second alert in the error callback, where I alert thrownError, I am getting undefined and the xhr.status code is 500.

I am not sure where I am going wrong. What can I do to fix this problem?

Corregidor answered 18/12, 2008 at 12:6 Comment(0)
C
386

Make sure you're setting Response.StatusCode to something other than 200. Write your exception's message using Response.Write, then use...

xhr.responseText

..in your javascript.

Copal answered 16/1, 2009 at 14:25 Comment(4)
This is still the correct way of doing this after 2 years and a half... :) I went a little further and actually return my own error JSON object that can handle single or multiple errors, quite good for server-side form validation.Ryun
@Wilson It was as shown in the other high-rated answers here.Copal
Am now in 2014. JSON dominated era. So i use xhr.responseJSON. :DSoubise
xhr.responseJSON is only set if you ensure, that the meta-type is set (e.g. "Content-type: application/json"). That's a problem I just encountered; responseText was set - responseJSON was not.Terminal
S
259

Controller:

public class ClientErrorHandler : FilterAttribute, IExceptionFilter
{
    public void OnException(ExceptionContext filterContext)
    {
        var response = filterContext.RequestContext.HttpContext.Response;
        response.Write(filterContext.Exception.Message);
        response.ContentType = MediaTypeNames.Text.Plain;
        filterContext.ExceptionHandled = true;
    }
}

[ClientErrorHandler]
public class SomeController : Controller
{
    [HttpPost]
    public ActionResult SomeAction()
    {
        throw new Exception("Error message");
    }
}

View script:

$.ajax({
    type: "post", url: "/SomeController/SomeAction",
    success: function (data, text) {
        //...
    },
    error: function (request, status, error) {
        alert(request.responseText);
    }
});
Secor answered 10/5, 2010 at 7:16 Comment(10)
This isn't a "correct" answer to the question but it most certainly shows a higher level solution to the problem... Nice!Tericaterina
I'm doing something similar. It works fine if everything's done on the development box. If I try connecting from a different box on the network, the xhr.responseText contains the generic error page html and not my custom message, see #3883252Midday
I believe you should also add response.StatusCode = 500; line to OnException method.Heroism
I adapted this - since I wanted the 500 statuscode, but to have the exception message in the status description (rather than "Internal server error") - response.StatusCode = (int)HttpStatusCode.InternalServerError; and response.StatusDescription = filterContext.Exception.Message;Lapboard
Using IIS7 and IOS (iPhone / iPad / iPod), I got it working by using the script above and the following lines: responce.TrySkipIisCustomErrors = true; and responce.StatusCode = (int)HttpStatusCode.InternalServerError;Khachaturian
@AlexanderProkofyev The response is already set to 500 since it is executing the OnException method.Adjective
The response is not set to 500 in my instance (I'm using exactly this code) and so the error is not being caught on the client side....Holloman
If you're using IIS7 or above you may need to add: response.TrySkipIisCustomErrors = true;Luby
a minor nitpick, but request in error: function (request, status, error) { should actually be named response. The argument is the response you got, not the request you made.Roquelaure
IMHO, trying to put information into error messages is a largely futile effort. It'll work in some cases, but proxies, firewalls and possibly even malware defenders will probably strip these. Further, intermediaries may return a 500 error to the client which never originated from your server - so your client will return "undefined" or raise an exception or something to the user because the error is not as expected.Hamamelidaceous
L
103

ServerSide:

     doPost(HttpServletRequest request, HttpServletResponse response){ 
            try{ //logic
            }catch(ApplicationException exception){ 
               response.setStatus(400);
               response.getWriter().write(exception.getMessage());
               //just added semicolon to end of line

           }
 }

ClientSide:

 jQuery.ajax({// just showing error property
           error: function(jqXHR,error, errorThrown) {  
               if(jqXHR.status&&jqXHR.status==400){
                    alert(jqXHR.responseText); 
               }else{
                   alert("Something went wrong");
               }
          }
    }); 

Generic Ajax Error Handling

If I need to do some generic error handling for all the ajax requests. I will set the ajaxError handler and display the error on a div named errorcontainer on the top of html content.

$("div#errorcontainer")
    .ajaxError(
        function(e, x, settings, exception) {
            var message;
            var statusErrorMap = {
                '400' : "Server understood the request, but request content was invalid.",
                '401' : "Unauthorized access.",
                '403' : "Forbidden resource can't be accessed.",
                '500' : "Internal server error.",
                '503' : "Service unavailable."
            };
            if (x.status) {
                message =statusErrorMap[x.status];
                                if(!message){
                                      message="Unknown Error \n.";
                                  }
            }else if(exception=='parsererror'){
                message="Error.\nParsing JSON Request failed.";
            }else if(exception=='timeout'){
                message="Request Time out.";
            }else if(exception=='abort'){
                message="Request was aborted by the server";
            }else {
                message="Unknown Error \n.";
            }
            $(this).css("display","inline");
            $(this).html(message);
                 });
Loo answered 25/2, 2012 at 17:9 Comment(0)
G
86

You need to convert the responseText to JSON. Using JQuery:

jsonValue = jQuery.parseJSON( jqXHR.responseText );
console.log(jsonValue.Message);
Giordano answered 13/4, 2011 at 15:49 Comment(5)
+1 'cause this is at present the only CORRECT answer to this question! You can call "jsonValue.Message" to get the exception message.Doriadorian
Actually its not the correct answer because the question does not ask about JSON and the example request specifically asks for HTML as the response.Divertimento
+1 Correct. Note, It's common to send a JSON encoded object thru the jqXHR.responseText (string). You you can then use the jsonValue Object how you need to. Use Firebug console to review the response using console.log(jsonValue).Slade
This gives me 'Uncaught SyntaxError: Unexpected number'Training
The parsed JSON object is made available through the responseJSON property of the jqXHR object. So there's no need to parse the responseText property. You can just do: console.log( jqXHR.responseJSON.Message)Arytenoid
C
43

If making a call to asp.net, this will return the error message title:

I didn't write all of formatErrorMessage myself but i find it very useful.

function formatErrorMessage(jqXHR, exception) {

    if (jqXHR.status === 0) {
        return ('Not connected.\nPlease verify your network connection.');
    } else if (jqXHR.status == 404) {
        return ('The requested page not found. [404]');
    } else if (jqXHR.status == 500) {
        return ('Internal Server Error [500].');
    } else if (exception === 'parsererror') {
        return ('Requested JSON parse failed.');
    } else if (exception === 'timeout') {
        return ('Time out error.');
    } else if (exception === 'abort') {
        return ('Ajax request aborted.');
    } else {
        return ('Uncaught Error.\n' + jqXHR.responseText);
    }
}


var jqxhr = $.post(addresshere, function() {
  alert("success");
})
.done(function() { alert("second success"); })
.fail(function(xhr, err) { 

    var responseTitle= $(xhr.responseText).filter('title').get(0);
    alert($(responseTitle).text() + "\n" + formatErrorMessage(xhr, err) ); 
})
Cassel answered 13/2, 2013 at 12:30 Comment(0)
S
30

If someone is here as in 2016 for the answer, use .fail() for error handling as .error() is deprecated as of jQuery 3.0

$.ajax( "example.php" )
  .done(function() {
    alert( "success" );
  })
  .fail(function(jqXHR, textStatus, errorThrown) {
    //handle error here
  })

I hope it helps

Scottiescottish answered 13/12, 2016 at 17:41 Comment(1)
jqXHR.error() is deprecated (actually removed) in jQuery 3.0, but the error and success callbacks to $.ajax() are not deprecated, as far as I know.Watersick
M
25

This is what I did and it works so far in a MVC 5 application.

Controller's return type is ContentResult.

public ContentResult DoSomething()
{
    if(somethingIsTrue)
    {
        Response.StatusCode = 500 //Anything other than 2XX HTTP status codes should work
        Response.Write("My Message");
        return new ContentResult();
    }

    //Do something in here//
    string json = "whatever json goes here";

    return new ContentResult{Content = json, ContentType = "application/json"};
}

And on client side this is what ajax function looks like

$.ajax({
    type: "POST",
    url: URL,
    data: DATA,
    dataType: "json",
    success: function (json) {
        //Do something with the returned json object.
    },
    error: function (xhr, status, errorThrown) {
        //Here the status code can be retrieved like;
        xhr.status;

        //The message added to Response object in Controller can be retrieved as following.
        xhr.responseText;
    }
});
Melanson answered 18/5, 2016 at 22:23 Comment(0)
R
18

A general/reusable solution

This answer is provided for future reference to all those that bump into this problem. Solution consists of two things:

  1. Custom exception ModelStateException that gets thrown when validation fails on the server (model state reports validation errors when we use data annotations and use strong typed controller action parameters)
  2. Custom controller action error filter HandleModelStateExceptionAttribute that catches custom exception and returns HTTP error status with model state error in the body

This provides the optimal infrastructure for jQuery Ajax calls to use their full potential with success and error handlers.

Client side code

$.ajax({
    type: "POST",
    url: "some/url",
    success: function(data, status, xhr) {
        // handle success
    },
    error: function(xhr, status, error) {
        // handle error
    }
});

Server side code

[HandleModelStateException]
public ActionResult Create(User user)
{
    if (!this.ModelState.IsValid)
    {
        throw new ModelStateException(this.ModelState);
    }

    // create new user because validation was successful
}

The whole problem is detailed in this blog post where you can find all the code to run this in your application.

Ranique answered 5/5, 2011 at 8:27 Comment(0)
B
17

 error:function (xhr, ajaxOptions, thrownError) {
        alert(xhr.status);
        alert(thrownError);
      }
in code error ajax request for catch error connect between client to server if you want show error message of your application send in success scope

such as

success: function(data){
   //   data is object  send  form server 
   //   property of data 
   //   status  type boolean 
   //   msg     type string
   //   result  type string
  if(data.status){ // true  not error 
         $('#api_text').val(data.result);
  }
  else 
  {
      $('#error_text').val(data.msg);
  }

}
Bandeau answered 16/3, 2018 at 3:33 Comment(0)
S
16

I found this to be nice because I could parse out the message I was sending from the server and display a friendly message to the user without the stacktrace...

error: function (response) {
      var r = jQuery.parseJSON(response.responseText);
      alert("Message: " + r.Message);
      alert("StackTrace: " + r.StackTrace);
      alert("ExceptionType: " + r.ExceptionType);
}
Selfpronouncing answered 1/12, 2011 at 21:33 Comment(0)
T
10

This function basically generates unique random API key's and in case if it doesn't then pop-up dialog box with error message appears

In View Page:

<div class="form-group required">
    <label class="col-sm-2 control-label" for="input-storename"><?php echo $entry_storename; ?></label>
    <div class="col-sm-6">
        <input type="text" class="apivalue"  id="api_text" readonly name="API" value="<?php echo strtoupper(substr(md5(rand().microtime()), 0, 12)); ?>" class="form-control" />                                                                    
        <button type="button" class="changeKey1" value="Refresh">Re-Generate</button>
    </div>
</div>

<script>
$(document).ready(function(){
    $('.changeKey1').click(function(){
          debugger;
        $.ajax({
                url  :"index.php?route=account/apiaccess/regenerate",
                type :'POST',
                dataType: "json",
                async:false,
                contentType: "application/json; charset=utf-8",
                success: function(data){
                  var result =  data.sync_id.toUpperCase();
                        if(result){
                          $('#api_text').val(result);
                        }
                  debugger;
                  },
                error: function(xhr, ajaxOptions, thrownError) {
                  alert(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
                }

        });
    });
  });
</script>

From Controller:

public function regenerate(){
    $json = array();
    $api_key = substr(md5(rand(0,100).microtime()), 0, 12);
    $json['sync_id'] = $api_key; 
    $json['message'] = 'Successfully API Generated';
    $this->response->addHeader('Content-Type: application/json');
    $this->response->setOutput(json_encode($json));
}

The optional callback parameter specifies a callback function to run when the load() method is completed. The callback function can have different parameters:

Type: Function( jqXHR jqXHR, String textStatus, String errorThrown )

A function to be called if the request fails. The function receives three arguments: The jqXHR (in jQuery 1.4.x, XMLHttpRequest) object, a string describing the type of error that occurred and an optional exception object, if one occurred. Possible values for the second argument (besides null) are "timeout", "error", "abort", and "parsererror". When an HTTP error occurs, errorThrown receives the textual portion of the HTTP status, such as "Not Found" or "Internal Server Error." As of jQuery 1.5, the error setting can accept an array of functions. Each function will be called in turn. Note: This handler is not called for cross-domain script and cross-domain JSONP requests.

Tennes answered 21/2, 2018 at 13:4 Comment(0)
H
7

This is probably caused by the JSON field names not having quotation marks.

Change the JSON structure from:

{welcome:"Welcome"}

to:

{"welcome":"Welcome"}
Hexylresorcinol answered 14/8, 2010 at 13:1 Comment(2)
This shouldn't matter unless the key is a reserved word in JS. I don't see this being the issue here.Moreta
JSON.stringify({welcome:"Welcome"}) --> {"welcome":"Welcome"}Subside
W
6

You have a JSON object of the exception thrown, in the xhr object. Just use

alert(xhr.responseJSON.Message);

The JSON object expose two other properties: 'ExceptionType' and 'StackTrace'

Westernize answered 23/2, 2017 at 10:33 Comment(0)
I
5

I believe the Ajax response handler uses the HTTP status code to check if there was an error.

So if you just throw a Java exception on your server side code but then the HTTP response doesn't have a 500 status code jQuery (or in this case probably the XMLHttpRequest object) will just assume that everything was fine.

I'm saying this because I had a similar problem in ASP.NET where I was throwing something like a ArgumentException("Don't know what to do...") but the error handler wasn't firing.

I then set the Response.StatusCode to either 500 or 200 whether I had an error or not.

Inedible answered 19/12, 2008 at 14:11 Comment(0)
S
5

jQuery.parseJSON is useful for success and error.

$.ajax({
    url: "controller/action",
    type: 'POST',
    success: function (data, textStatus, jqXHR) {
        var obj = jQuery.parseJSON(jqXHR.responseText);
        notify(data.toString());
        notify(textStatus.toString());
    },
    error: function (data, textStatus, jqXHR) { notify(textStatus); }
});
Sean answered 11/8, 2011 at 22:18 Comment(0)
S
4
$("#save").click(function(){
    $("#save").ajaxError(function(event,xhr,settings,error){
        $(this).html{'error: ' (xhr ?xhr.status : '')+ ' ' + (error ? error:'unknown') + 'page: '+settings.url);
    });
});
Skycap answered 6/3, 2014 at 18:7 Comment(0)
O
3

Throw a new exception on server using:

Response.StatusCode = 500

Response.StatusDescription = ex.Message()

I believe that the StatusDescription is returned to the Ajax call...

Example:

        Try

            Dim file As String = Request.QueryString("file")

            If String.IsNullOrEmpty(file) Then Throw New Exception("File does not exist")

            Dim sTmpFolder As String = "Temp\" & Session.SessionID.ToString()

            sTmpFolder = IO.Path.Combine(Request.PhysicalApplicationPath(), sTmpFolder)

            file = IO.Path.Combine(sTmpFolder, file)

            If IO.File.Exists(file) Then

                IO.File.Delete(file)

            End If

        Catch ex As Exception

            Response.StatusCode = 500

            Response.StatusDescription = ex.Message()

        End Try
Obrian answered 18/12, 2008 at 12:6 Comment(0)
C
3

Although it has been many years since this question is asked, I still don't find xhr.responseText as the answer I was looking for. It returned me string in the following format:

"{"error":true,"message":"The user name or password is incorrect"}"

which I definitely don't want to show to the users. What I was looking for is something like below:

alert(xhr.responseJSON.message);

xhr.responseJSON.message gives me the exact message from the Json Object which can be shown to the users.

Chilly answered 24/6, 2016 at 2:5 Comment(0)
A
1
$("#fmlogin").submit(function(){
   $("#fmlogin").ajaxError(function(event,xhr,settings,error){
       $("#loading").fadeOut('fast');       
       $("#showdata").fadeIn('slow');   
       $("#showdata").html('Error please, try again later or reload the Page. Reason: ' + xhr.status);
       setTimeout(function() {$("#showdata").fadeOut({"opacity":"0"})} , 5500 + 1000); // delays 1 sec after the previous one
    });
});

If there is any form is submit with validate

simply use the rest of the code

$("#fmlogin").validate({...

... ... });

Autocade answered 9/3, 2014 at 17:31 Comment(0)
G
0

First we need to set <serviceDebug includeExceptionDetailInFaults="True" /> in web.config:

<serviceBehaviors> 
 <behavior name=""> 
  <serviceMetadata httpGetEnabled="true" /> 
    **<serviceDebug includeExceptionDetailInFaults="true" />** 
 </behavior> 
</serviceBehaviors>

In addition to that at jquery level in error part you need to parse error response that contains exception like:

.error(function (response, q, t) { 
  var r = jQuery.parseJSON(response.responseText); 
}); 

Then using r.Message you can actully show exception text.

Check complete code: http://www.codegateway.com/2012/04/jquery-ajax-handle-exception-thrown-by.html

Goldbrick answered 9/5, 2012 at 8:41 Comment(0)
R
0

In my case, I just removed HTTP VERB from controller.

    **//[HttpPost]**   ---- just removed this verb
    public JsonResult CascadeDpGetProduct(long categoryId)
    {
       
        List<ProductModel> list = new List<ProductModel>();
        list = dp.DpProductBasedOnCategoryandQty(categoryId);
        return Json(new SelectList(list, "Value", "Text", JsonRequestBehavior.AllowGet));
    }
Ratafia answered 6/10, 2020 at 5:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.