0

I'm working on an application (jquery 3.2.1) where making an ajax call can result in one of the following things happening - providing the server gives a HTTP 200 OK response:

  1. HTML is returned. This is the desired outcome as it is the "content" response to a valid request.

  2. A JSON error is returned - if there are validation errors based on the request data.

The ajax code I have looks like this:

$.ajax({
  url: $('#searchRegulations').attr('action'),
  type: 'post',
  cache: false,
  data: $('#searchRegulations').serialize()
}).done(function (response) {
    if (response) {
      // Display response (point 1)
      $('main .content').hide();
      $('#ajaxContent').html(response).show();
      return false;
    } else {
        // Handle JSON error (point 2)    
    }
});

As I've mentioned in the comments above, if situation 1 occurs (HTML is returned) then I write it to #ajaxContent. That's fine.

But what do I do for situation 2? If I add the following code:

// Handle JSON error (point 2)  
$('main .content').hide();
$('#ajaxContent').html(response).show();

Nothing is written inside #ajaxContent.

Is this because HTML is not being returned?

The Response Headers for each are different:

  1. Content-Type: text/html; charset=UTF-8

  2. Content-Type: application/json; charset=UTF-8

I looked at dataType on the jquery ajax documentation but wasn't sure if this is relevant because I'm actually receiving 2 separate Content-Type's based on the outcome.

The reason JSON is being returned in scenario 2 is because there is some code (not shown) which can write the error message underneath each field, e.g.

`{rf_keywords: {length: "Search keywords must under 1500 characters in length."}}`

So HTML works better for situation 1, and JSON works better for 2.

Please can someone advise?

Manav
  • 553
  • 7
  • 18
Andy
  • 5,142
  • 11
  • 58
  • 131
  • Post an example of your returned: 1. HTML response e.g. parent HTML tag 2. Json response e.g parent object in your json object – Morgs Jul 12 '18 at 11:33
  • Can you change the response? If yes you can change to someting like this {htmlContent: "", json: '{yourjsonobject:here}' and check in response if (response.htmlContent) { //do HTML stuff } if (response.json) { //do json stuff } – Paulo Filipe Dantas Jul 12 '18 at 11:34
  • in case of an error, get the server to change the HTTP status code accordingly - e.g. 400 for a validation error, 404 for resource not found, 403 for forbidden, etc. You'll find the various standard statuses fit most kinds of request errors quite nicely. That way you your JS code will fall into the "fail" callback instead of "done" and you can then easily know that you need to parse the response as JSON instead of HTML. – ADyson Jul 12 '18 at 11:42
  • So is there no way inside `.done()` to see what the response type (`text/html;` or `application/json;`) is? I have to go into a `.fail()` method to do this? – Andy Jul 12 '18 at 12:12
  • 1
    If you want a good design practice, I suggest you choose to return either HTML or JSON for either a Success or Fail operation. I would not recommend you to proceed in that direction where a return type could either be this or that. Just like any Function/Method, it has 1 return type. Follow existing coding standards and patterns if you really want to code along with other developers.... – Morgs Jul 12 '18 at 12:38
  • " is there no way" yeah you could try a parse to JSON, catch an exception and if it fails, then assume it's HTML. Or you can look at the response headers and check the content type header. But returning an error status from the server is more RESTful and gives you an easier way to tell, and a better definition of your endpoint - an ambiguous return type is not really a good design practice. A third alternative is to always return JSON, and make the HTML snippet a property of a JSON object, you can then read this property to get the data. – ADyson Jul 12 '18 at 12:59

2 Answers2

0

As you have observed,you receive a response in json forat in case #2. This is supposed to have already been parsed when your code is handed the response parameter. Turn that into a string again using JSON.stringify and you will be able to display it in html code:

$('#ajaxContent').html(JSON.stringify(response)).show();

Note that this will not be a pretty view as the JSON structure is not translated into an equivalent html subtree.

Have a look at this fiddle to see a full-fledged example.

There are libraries to turn JSON structures into interactive html, eg. JSTree (possibly overkill for your use case).

collapsar
  • 17,010
  • 4
  • 35
  • 61
0

Ok, I'm answering my own question.

The real issue here: is there a way to determine whether the Response type is HTML or JSON, inside .done()

Yes there is. Documented here: jquery how to check response type for ajax call

So my final ajax code looks like this:

$.ajax({
    url: $('#searchRegulations').attr('action'),
    type: 'post',
    cache: false,
    data: $('#searchRegulations').serialize()
 }).done(function (response, status, xhr) {

            // The ajax handler can return either HTML or JSON. So we need to know what's been returned.
            var ct = xhr.getResponseHeader("content-type") || "";

            // HTML response == content to display. No validation error.
            if (ct.indexOf('html') > -1) {
                if (response) {
                    $('main .content').hide();
                    $('#ajaxContent').html(response).show();
                    return false;
                }
            }
            // JSON response. Validation error(s) occurred.
            if (ct.indexOf('json') > -1) {
                $.each(response, function(i, v) {
                    $.each(v, function(k, m) {
                         var msg = '<span class="help-block">'+m+'</span>';
                         $('input[name="' + i + '"]').parent().after(msg);
                         $('input[name="' + i + '"]').closest('.form-group').addClass('has-error');
                    });
                });
            }  
});

Doing this means that you can check whether HTML has been returned - in which case show it inside #ajaxContent. If an error has occurred and JSON is sent back from the ajax script, the error messages can be written underneath the appropriate form fields based on the JSON that is returned.

Andy
  • 5,142
  • 11
  • 58
  • 131
  • If you want a good design practice, I suggest you choose to return either HTML or JSON for either a Success or Fail operation. I would not recommend you to proceed in that direction where a return type could either be this or that. Just like any Function/Method, it has 1 return type. Follow existing coding standards and patterns if you really want to code along with other developers.... – Morgs Jul 12 '18 at 12:38
  • Do you have any resources (links etc) which back up your claim? I disagree. The output from a function may be valid data which needs to be presented/formatted as HTML, or a different type of response for a validation error message. The request is going to the same ajax endpoint, and it has to respond appropriately. In the case of errors, JSON is a more appropriate response than HTML because it can be applied more easily to the appropriate parts of the UI (different form fields in this case). – Andy Jul 12 '18 at 12:56