Skip to content
This repository has been archived by the owner on Aug 31, 2024. It is now read-only.

Latest commit

 

History

History
180 lines (144 loc) · 5.05 KB

body-parsing.md

File metadata and controls

180 lines (144 loc) · 5.05 KB

Body Parsing

Unlike Koa, Koala includes body parsing. Thanks to generators, body parsing is much easier in Koa than in Express and other frameworks. Body parsing is not automatic and must be yielded.

Expect: 100-continue

Expect: 100-continue is automatically supported as long as you use app.listen(). Otherwise, create your server like this:

const fn = app.callback();
const server = http.createServer(); // or whatever server you use
server.on('request', fn); // regular requests
server.on('checkContinue', function (req, res) {
  // requests with `Expect: 100-continue`
  req.checkContinue = true;
  fn(req, res);
});

Nested Query String Support

By default, node's querystring is used, which does not support nested parameters. To enable nested parameters, install set options.qs = true at initialization.

const app = koala({
  qs: true
})

Body Limits

In general, you'd want to keep body limits low to avoid any potential attacks on your server as well as keep memory usage as low as possible. For example, you probably don't need a limit much higher than 1kb for login forms.

You can also now set the limit on a per-request basis. Your limit on routes where users post articles would probably be much higher than just the login route. This is previously impossible with middleware.

CSRF

Koala uses koa-csrf for CSRF tokens. See sessions for more information on CSRF.

To check a body for a CSRF token, you must do this.assertCSRF(body), otherwise, only headers will be checked. This allows you to selectively assert a token exists, for example, don't bother asserting when the user supplies an API token.

const body = yield* this.request.json([limit]);
if (!body) this.throw(400, 'no body supplied!');
this.assertCSRF(body); // will throw if CSRF verification fails

// do something with the body

Content Negotiation

These request body methods are designed to be used with this.request.is(). Here's an example:

switch (this.request.is('json', 'urlencoded', 'multipart', 'image/*')) {
case 'json':
  const body = yield* this.request.json();
  break;
case 'urlencoded':
  const body = yield* this.request.urlencoded();
  break
case 'multipart':
  const parts = this.request.parts();
  let part;
  while (part = yield parts) {
    if (part.length) {
      const key = part[0];
      const value = part[1];
      // check the CSRF token
      if (key === '_csrf') this.assertCSRF(value);
    } else {
      yield this.save(part, '/tmp/file');
    }
  }
  break;
case 'image/jpeg':
case 'image/png':
case 'image/gif':
  // a supported image, so let's download it to disk
  const destination = '/tmp/image';
  yield this.save(this.req, destination);
  break
default:
  this.throw(415, 'i do not know what to do with this request type')
}

this.response.writeContinue()

If Expect: 100-continue was sent to the client, this will automatically response with a "100-continue". Use this right before parsing the body. Automatically called by all following body parsers, but you would still have to call it if you're doing something like:

app.use(function* (next) {
  if (this.request.is('image/*')) {
    this.response.writeContinue();
    yield this.save(this.req, '/tmp/image')
  }
})

const body = yield* this.request.json([limit])

Get the JSON body of the request, if any. limit defaults to 100kb.

const body = yield* this.request.urlencoded([limit])

Get the traditional form body of the request, if any, limit defaults to 100kb.

const text = yield* this.request.text([limit])

Get the body of the request as a single text string. limit defaults to 100kb. You could use this to create your own request body parser of some sort.

const buffer = yield* this.request.buffer([limit])

Get the body of the request as a single Buffer instance. limit defaults to 1mb.

const parts = this.request.parts([options])

Use this to parse multipart bodies. Uses co-busboy and thus busboy. This API is a little different. parts must be yielded to get the next part. See co-busboy's docs on its usage.

app.use(function* (next) {
  if (!this.request.is('multipart')) return yield* next;

  const parts = this.request.parts();
  let part;
  while (part = yield parts) {
    if (part.length) {
      // fields are returned as arrays
      const key = part[0];
      const value = part[1];
      // check the CSRF token
      if (key === '_csrf') this.assertCSRF(value);
    } else {
      // files are returned as readable streams
      // let's just save them to disk
      yield this.save(part, '/tmp/file');
    }
  }
})

Files are not automatically stored to disk for you. Use yield this.save() to save files to disk.

yield this.save(stream, destination)

Utility to save a stream to disk. Designed to be used in conjunction with this.request.parts() or maybe this.req. See save-to for more information.