1

I'm trying to send stringified JSON to a local address using jQuery AJAX.

This address is waiting to receive JSON data.

However, once sent, it returns "400 Bad Request".

//response received as: {"success":"true","packages":["https://example.com/link1.pkg","https://example.com/link2.pkg","https://example.com/link3.pkg"]}

var res = JSON.parse(response); 
var packages = [];

$.each(res.packages, function (i, item) { // put links in array
    packages.push(item);
});

if (res.success) {
    $.ajax({
        url: 'http://192.168.2.10:12800/api/install',
        type: 'POST',
        headers: {
            'Access-Control-Allow-Origin': '*', // cors
            'Content-Type': 'application/json'
        },
        contentType: 'application/json; charset=utf-8',
        dataType: 'jsonp',
        data: JSON.stringify({
            'type': 'direct',
            'packages': packages
        }),
        success: function (response) {
            $('#dynamicModal').find('#response').html('<p>Packages sent!</p>');
        },
        error: function (response) {
            $('#dynamicModal').find('#response').html('<p>Error occurred while sending.</p>');
        }
    });
} else {
    $('#dynamicModal').find('#response').html('<p>Error occurred while gathering links.</p>');
}

I've checked so many links on Google, and 90% say the JSON isn't stringified, but mine is. I'm out of ideas and I'm hoping someone could help me out and potentially see the issue.

This is the full console error:

GET http://192.168.2.10:12800/api/install?callback=jQuery3410603622444131205_1578188027542&{%22type%22:%22direct%22,%22packages%22:[%22http://example.com/EXAMPLE_0.pkg%22,%22http://example.com/EXAMPLE_1.pkg%22,%22http://example.com/EXAMPLE_2.pkg%22,%22http://example.com/EXAMPLE_3.pkg%22,%22http://example.com/EXAMPLE_4.pkg%22,%22http://example.com/EXAMPLE_5.pkg%22,%22http://example.com/EXAMPLE_6.pkg%22]}&_=1578188027543 net::ERR_ABORTED 400 (Bad request)

Update

I've found the code where it fails on the local server:

static int event_handler(sb_Event* e) {
    const struct handler_desc* descs = NULL;
    handler_cb* handler = NULL;
    char* in_data;
    size_t in_size;
    size_t count;
    size_t i;
    int ret;

    if (e->type != SB_EV_REQUEST) {
        ret = SB_RES_OK;
        goto done;
    }

    if (strcasecmp(e->method, "GET") == 0) {
        descs = s_get_handlers;
        count = ARRAY_SIZE(s_get_handlers);
    } else if (strcasecmp(e->method, "POST") == 0) {
        descs = s_post_handlers;
        count = ARRAY_SIZE(s_post_handlers);
    }
    if (!descs) {
bad_request:
        kick_error(e->stream, 400, "Bad request", "Unsupported method");
        ret = SB_RES_OK;
        goto done;
    }

    for (i = 0; i < count; ++i) {
        if (descs[i].need_partial_match) {
            if (strstr(e->path, descs[i].path) == e->path) {
                handler = descs[i].handler;
                break;
            }
        } else {
            if (strcmp(e->path, descs[i].path) == 0) {
                handler = descs[i].handler;
                break;
            }
        }
    }
    if (!handler) {
        goto bad_request;
    }

    in_data = sb_get_content_data(e->stream, &in_size);

    (*handler)(e->stream, e->method, e->path, in_data, in_size);

    ret = SB_RES_OK;

done:
    return ret;
}

To me this looks like it's not able to get s_get_handlers or s_post_handlers?

Appel Flap
  • 261
  • 3
  • 23
  • Normally you would not stringify the `data` field, have you tried just supplying a raw json object for that field? Also have you noticed that you specified POST but the console error says GET? Looks like the server-side may be expecting to only handle that URL with GET requests. – Nathan Hawks Jan 05 '20 at 02:05
  • @NathanHawks I've unstringified the `data` field and changed POST to GET but no changes to the result. Can the GET be the return result? – Appel Flap Jan 05 '20 at 02:29
  • No, the return result will be neither POST nor GET. Ok, so i wasn't suggesting you change the AJAX request to a GET. Rather I think you should find the code on the server-side that expects a GET, and change *that* to a POST. Whatever's happening, a 400 means you sent a misshapen request. That boils down to: either there are vars you are sending that aren't expected, or vars are missing, or the method (GET/POST) is wrong. – Nathan Hawks Jan 05 '20 at 02:38
  • @NathanHawks Hi Nathan, I've added the relevant code of the server where it fails. – Appel Flap Jan 05 '20 at 17:47
  • 1
    Your client-side code is full of nonsense, you can't make a POST request *or* set request headers when you use JSON, and when you can set request headers, `Access-Control-Allow-Origin` isn't one of them: It's a *response* header. – Quentin Jan 05 '20 at 17:53
  • @Quentin Could you provide a snippet how you would've done this? – Appel Flap Jan 05 '20 at 18:04
  • @Quentin the incompatibility is between JSONP and POST request. I didn't realized XDDDD – user1039663 Jan 06 '20 at 23:26
  • @AppelFlap You can check it on the network tab --> you will probably see that the requests to 192.168.2.10:12800 are always GET requests. Also I added to my answer more info on how to make it: you should stop using JSONP, so you will have CORS problem, so you need to add Access-Control-Allow-Origin header to the 192.168.2.10:12800 server response, I added also more documentation. But I've to say that sucessfully enabling CORS exceptions is a pain! – user1039663 Jan 06 '20 at 23:26
  • @AppelFlap you must search where s_get_handlers is set, to know what they are... – user1039663 Jan 06 '20 at 23:31

1 Answers1

0

Most probably solution: add processData: false to your arguments, this should make that the string passed to data is not processed and should be sent as POST raw body:

if (res.success) {
  $.ajax({
    url: 'http://192.168.2.10:12800/api/install',
    ...
    processData: false,
    data: JSON.stringify({
    ...

But as long as I know you cannot send JSONP POST requests because of how JSONP requests are made: with dynamic created tags. Those requests will be always GET requests, so you cannot send raw bodies. Check it on your's developer network tab (see down). If you want to send a POST request then you have to use a non-JSONP request and setup CORS correctly on your 192.168.2.10:12800 server. That's set on that server the response header Access-Control-Allow-Origin: http://siteA.com where siteA.com is the domain where the caller page is. See more on How does Access-Control-Allow-Origin header work?

I have to mention that maybe there is a trick used by JQuery, but I think JSONP is not compatible with sending POST requests, because is requested by creating a tag.

You can check for this solution and for the others how requests change using the network tab from the developer's bar from your browser (you know, open with F12). For example activating processData: false should change the url and the request body.

Less probable solution: change the data to specify at least one parameter, depending on your API, so the data stringfied are send under one named argument respecting the most used standarized protocol://host/path/resource?param=value&param=value&param=value url format.

For example if you want to send your data under a parameter named json you should use:

if (res.success) {
  $.ajax({
    url: 'http://192.168.2.10:12800/api/install',
    ...
    data: {
      json: JSON.stringify({
        'type': 'direct',
        'packages': packages
      })
    },
    ...

Reason: if you analize your URL from the error, splitting it into parts:

http://192.168.2.10:12800/api/install

first argument:

?callback=jQuery3410603622444131205_1578188027542

second (malformed, does not have parameter name):

&{%22type%22:%22direct%22,%22packages%22:[%22http://example.com/EXAMPLE_0.pkg%22,%22http://example.com/EXAMPLE_1.pkg%22,%22http://example.com/EXAMPLE_2.pkg%22,%22http://example.com/EXAMPLE_3.pkg%22,%22http://example.com/EXAMPLE_4.pkg%22,%22http://example.com/EXAMPLE_5.pkg%22,%22http://example.com/EXAMPLE_6.pkg%22]}

final argument:

&_=1578188027543

The first is the JQuery dynamic callback, and last one is the anti-cache, seems ok, but second part seems malformed. Of course you can use your own url scheme, but standard ones does not use that form of json data without parameter name. So probably you want to send the json data in POST raw body or send a named parameter with the json stringified as value.

Related questions: Send POST data via raw json with postman and Always failed when POST data with json (body: raw)

user1039663
  • 1,230
  • 1
  • 9
  • 15
  • I've tried both of your snippets but sadly they don't seem to solve the problem. Sending data un-stringified however seems to fix the JSON-in-URL problem. I'm starting to wonder if its even possible to ping a local server from a webserver... – Appel Flap Jan 05 '20 at 13:41
  • Probably will fail if you load from an https context a resource that is http (not secure), but not because of the ips or addresses. Anyway the problem will be shown in the network tab from the developer's tools. – user1039663 Jan 06 '20 at 21:49
  • Mmmm... I re-readed my own answer... perhaps I didn't explained sufficiently the processData: false impact... activating this option should make that data is sent direcly in the POST body, so it should dissapear from the url itself. However, it depends on your API: different APIs are designed to receive different type of requests. – user1039663 Jan 06 '20 at 22:42
  • Hi, I realized that making a POST request with JSONP method does not have any sense, and except JQUery has a new trick, it should not be possible. So you cannot use both JSONP and POST. – user1039663 Jan 06 '20 at 23:20