6

I have a MEAN-stack backend where I'd like to respond with

return res.status(400).jsonp({
  message: 'Parsing failed.',
  code: '123'
});

When an angular app uses this JSONP endpoint and encounters this particular error, it receives a 400 but without its payload. When I change the status to 200/300 it comes through fine, with 400/500 it doesn't.

On other routes (POST) I can respond with a 4** status code and payload without any issues.

return res.status(400).send({
  message: 'Codes do not match.',
  code: '234'
});

Any Idea what I'm overlooking?

Tom
  • 73
  • 3
  • did you configured the Express default JSONP callback to JSON_CALLBACK ? – Nivesh Jun 02 '16 at 13:36
  • 1
    @Nivesh correct me if I misunderstood, but if that wouldn't be the case, `res.jsonp({code: '234'})` or `res.status(300).jsonp({code: '234'})` wouldn't work either, would they? – Tom Jun 02 '16 at 13:41
  • 1
    @Nivesh if the callback setting wasn't correct, it would also fail for 200/300 responses... – robertklep Jun 02 '16 at 13:44
  • I'm using JSONP all through my applications, so I'm pretty sure it's working, just not with 400/500 status codes. my angular app requests it like this `return $http.jsonp('/api/code?callback=JSON_CALLBACK').then(` MEAN-stack (with code above) responds `/**/ typeof angular.callbacks._6 === 'function' && angular.callbacks._6({"code":"234"});` As I said, JSONP is working as it should, just don't get the payload with HTTP code other than 2**/3** – Tom Jun 02 '16 at 13:51

1 Answers1

2

It looks like this is a browser-thing: when a remote script is requested (as is the case with JSONP requests), and the response returns a 400 (or higher) HTTP status, any code that may still get returned in the response body isn't evaluated (this actually makes perfect sense).

Angular will only know that the response has been sent, and that it had an error status, but since the callback wasn't called, there is no payload data to pass to your callback.

I tested a standalone HTML page:

<script>
function foo(data) { alert('foo') }
</script>
<script src="/endpoint?callback=foo"></script>

And the following Express handler:

app.get('/endpoint', (req, res) => {
  return res.status(400).jsonp({ hello : 'world' });
});

A 400 status doesn't trigger the alert, a 200 status does. It also doesn't work if the handler returns plain JS (acting like a simple .js file that gets loaded with a <script src=...>).

So for JSONP requests, you should stick to 200 responses and convey any errors in some other way (like setting an error property in the response object).

robertklep
  • 198,204
  • 35
  • 394
  • 381
  • really, let me check this out too. – Nivesh Jun 02 '16 at 14:20
  • 1
    Thanks robert, for the confirmation on what I already had figured out: it's not going to work! :) Seems strange though, should be working according to the docs: http://expressjs.com/en/api.html#res.jsonp . And when using `res.jsonp(400, {})` I see this passing in the logs: `express deprecated res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead` – Tom Jun 02 '16 at 14:24
  • @Tom hmm yeah, strange that the documentation makes that suggestion. I wonder if they ever actually _tested_ it :P – robertklep Jun 02 '16 at 14:27
  • yep, browser is not allowing to evaluate the response data coming along with 400 status. – Nivesh Jun 02 '16 at 14:41