Koajs, as an excellent NodeJS Web framework, automatically determines the appropriate Content-Type based on what we write. So don’t we care what the content-Type is? If we don’t, returning to the browser can be messy.

Here’s a simple example:

const Koa = require("koa"); const app = new Koa(); const koaRouter = require("koa-router"); const router = new koaRouter(); The router. The get ("/", (CTX) = > {CTX. Body = "using the routing component"; let url = ctx.url; let request = ctx.request; let response = ctx.response; ctx.body = ctx.request.url == ctx.url; ctx.body = ctx.query; // Ctx. body = "text/plain"; //ctx. body = "text/plain"; ctx.body = ctx.querystring; console.log(ctx.querystring); }); The router. Post ("/", (CTX) = > {CTX. Body = "this is a post request"; }) app.use(router.routes()); app.use(router.allowedMethods()); app.listen(3000);Copy the code

When we look at the output, we see that the content-type of the output to the browser is application/json. We finally set the input to a string, which is this code: ctx.body= ctx.queryString; But why isn’t the output what we expect?

Let’s open koAJS source code to see what it is, or as the old saying goes, if you want to learn koA or JS in depth, look at the source is the only way.

Source code analysis:

Open response.js in koajs source code and look for the assignment to type.

set body(val) { const original = this._body; this._body = val; // no content if (null == val) { if (! statuses.empty[this.status]) this.status = 204; if (val === null) this._explicitNullBody = true; this.remove('Content-Type'); this.remove('Content-Length'); this.remove('Transfer-Encoding'); return; } // set the status if (! this._explicitStatus) this.status = 200; // set the content-type only if not yet set const setType = ! this.has('Content-Type'); // string if ('string' === typeof val) { if (setType) this.type = /^\s*</.test(val) ? 'html' : 'text'; this.length = Buffer.byteLength(val); return; } // buffer if (Buffer.isBuffer(val)) { if (setType) this.type = 'bin'; this.length = val.length; return; } // stream if (val instanceof Stream) { onFinish(this.res, destroy.bind(null, val)); if (original ! = val) { val.once('error', err => this.ctx.onerror(err)); // overwriting if (null ! = original) this.remove('Content-Length'); } if (setType) this.type = 'bin'; return; } // json this.remove('Content-Length'); this.type = 'json'; },Copy the code

We see that in the body set method, type is set differently depending on the data type, but there is one more important point, namely

// set the content-type only if not yet set const setType = ! this.has('Content-Type');Copy the code

This is set only when type is empty. This is set automatically only once.

Now let’s see, what type is set when?

Top down, let’s analyze the source code line by line.

const original=this._body; Gets the initial value for body in the current context

this._body=val; Assign the current argument to the _body variable

if(null==val){ if(! statuses.empty[this.status])this.status=204; If the current status code is not empty, set it to 204. If (val===null) this._explicitNullBody=true; if(val===null) this._explicitNullBody=true; Explicitly set the empty body property to true this.remove(" content-type "); This. remove(" content-lenfth "); Remove content-Length return this.remove(" transfer-encoding "); Remove transfer-encoding returns return; }Copy the code

The net effect is that the response has fewer attributes.

if(! this._explicitStatus) this.status=200; Set to false only if content is empty, and status is set to 204, and 200 in all other cases

const setType=! this.has(“Content-Type”) ; Check if the Content-Type attribute already exists, in case the Type is updated only once later

if("string"==typeof val){ if(setType) this.type = /^\s*</.test(val) ? 'html' : 'text'; If content-type is not set and the input value is a string set to HTML if the string contains </, otherwise text this.length= buffer.bytelength (val); return; }Copy the code

If the input data is a string, it can only be set to HTML or text.

if(Buffer.isBuffer(val)){
if(setType)this.type="bin";
this.length=val.length;
return;
}
Copy the code

If the input data is Buffer, the type is bin and the content length is the length of Buffer.

if (val instanceof Stream) { onFinish(this.res, destroy.bind(null, val)); if (original ! = val) { val.once('error', err => this.ctx.onerror(err)); // overwriting if (null ! = original) this.remove('Content-Length'); } if stream, setType to bin if (setType) this.type = 'bin'; return; }Copy the code

this.remove(“Content-Length”);

this.type=”json”;

If all of the above conditions are not met, remove ContentLength and set Type to JSON.

Conclusion:

⒈ If it is a string, the type is automatically set to text or HTML. If it is stream or buffer, the type is automatically set to bin. If none is met, it is set to JSON

2. The value is automatically set only when the content-type is not set. That is, it is set only when the value is assigned for the first time.

3. If the value is set to true or false, it is set to JSON. If you want to output a string containing an HTML fragment, you must manually set it to text. The use of stream is probably at download time.

4. Several useful attributes: Transfer-encoding,

5. If null is entered, 204 status code is returned.

6. We set the type to JSON, and output to the browser is not json, but application/ JSON. The mapping is in the NPM package of MIME-type.