koajs / koa

Expressive middleware for node.js using ES2017 async functions
https://koajs.com
MIT License
35.22k stars 3.23k forks source link

Koa always overwrite `Content-Type` for JSON responses #1120

Open brunoabreu opened 6 years ago

brunoabreu commented 6 years ago
const Koa = require('koa');
const app = new Koa();
app.use(ctx => {
  ctx.set('Content-Type', 'application/vnd.myapi.v1+json');
  ctx.body = {message: 'hello'};
});
app.listen(3000);

When running this code I aways get Content-Type: application/json; charset=utf-8.

I can make it work if I write the header after setting the body. It only affects json responses. I'm not sure if that's a bug, but I couldn't find any information about that behavior in documentation.

fl0w commented 6 years ago

Yes, setting body is basically a switch statement, where the default case is to assume value is json, which removes content-type and sets it to application/json.

https://github.com/koajs/koa/blob/6baa41178d3930df399fb367dbc3d73890760e02/lib/response.js#L179-L181

I'm not sure about intent but this might be a feature. It's an easy fix by just checking setType (as most/all other cases does), but it's a breaking fix as users may be relying on this behaviour.

jonathanong commented 6 years ago

seems like a bug where it overwrites the content type when the content type is set and has +json in it.

uzil commented 6 years ago

Check if body has value of type object, and if the header contains +json preserve the user given header. Is this the desired implementation of fix for this bug?

nerdyman commented 6 years ago

Is there a merged fix for this? I'm making an API JSON API compliant but having to manually declare type for every response is a hassle.

tiendq commented 6 years ago

what's issue that keep the PR not merged?

TehShrike commented 6 years ago

For reference, the PR in question is #1131

jeppech commented 4 years ago

I'm just gonna note this here, for others to use, as I had a hard time finding a solution.

A temporary "fix" to this, is to set a stringified JSON body, instead of a json object. Then the header will not be overridden, as we'll hit this condition where the setType assertion is made.

ctx.body = JSON.stringify(data)
cjaccino commented 3 years ago

There may be more nuance required than what is above, but Koa shouldn't overwrite a valid media type that has a structured syntax suffix like +json. The practice undermines alignment with other of the IETF's proposed standards, such as rfc7807: Problem Details for HTTP APIs.

A bit on why. When a client sends a message with a structured data payload, it will typically use application/json, application/xml or similar as the Content-Type. The recipient will be able to validate the payload syntactically, but it nothing to understand the message semantically. It has inadequately satisfied REST's Self-Descriptive Messages constraint. The message processor must make assumptions or examine the payload heuristically. Since the majority of applications assume that a URI points to no more than one document or resource representation, this has not been too much of a problem for most.

Use of a more descriptive media types provides a parade of benefits. It can enable a far more expressive web, reduce guesswork in message processing, and provide for more graceful evolution of applications that handle structured data.

Koa is amazing for being so flexible. It should make this possible, too.

For reference,

mrl5 commented 3 years ago

hello, I just want to confirm a workaround for the issue that @brunoabreu already wrote in his first post:

I can make it work if I write the header after setting the body.

I tested it with version 2.11.0