Skip to content

Commit

Permalink
feat: revert async formatters (#1377)
Browse files Browse the repository at this point in the history
* Remove support for async formatters from Response
* Remove async formatter specific tests
* Make arguments to send and sendRaw optional
* Update documentation
* Remove async formatters from 4TO5GUIDE.md
* Comment application/octet-stream default
  • Loading branch information
William Blankenship committed Jun 27, 2017
1 parent 61c0cb5 commit a2e300f
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 424 deletions.
54 changes: 0 additions & 54 deletions 4TO5GUIDE.md
Expand Up @@ -206,60 +206,6 @@ Connection: keep-alive
{"code":"Gone","message":"gone girl"}
```

## sync vs async formatters

Restify now supports both sync and async formatters. In 4.x, all formatters had
an async signature despite not being async. For example, the text formatter in
4.x might have looked like this:

```js
function formatText(req, res, body, cb) {
return cb(null, body.toString());
}
```

This caused a scenario where formatting could potentially fail, but the handler
chain would continue on. To address this gap, as of 5.x, any formatters that
are async require a callback to be passed into `res.send()`. For example,
imagine this async formatter:

```js
function specialFormat(req, res, body, cb) {
return asyncSerializer.format(body, cb);
}
server.get('/', function(req, res, next) {
res.send('hello world', function(err) {
if (err) {
res.end('some other backup content when formatting fails');
}
return next();
});
});
server.get('/', function(req, res, next) {
// alternatively, you can pass the error to next, which will render the
// error to the client.
res.send('hello world', next);
});
```

This way we are able to block the handler chain from moving on when an async
formatter fails. If you have any custom formatters, migrating from 4.x will
require you to change the formatter to be sync. Imagine the previous text
formatter changed to sync. Notice that the signature no longer takes a
callback. This hints to restify that the formatter is sync:

```js
function formatText(req, res, body) {
return body.toString();
}
```

Thus, if your formatter takes 4 parameters (i.e., takes a callback),
invocations of `res.send()` must take a callback, or else restify will throw.


## Deprecations

The following are still currently supported, but are on life support and may be
Expand Down
1 change: 0 additions & 1 deletion docs/pages/components/response.md
Expand Up @@ -128,7 +128,6 @@ res.status(201);
* `code` {Number} an http status code
* `body` {String | Object | Array | Buffer} the payload to send
* `[headers]` {Object} an optional object of headers (key/val pairs)
* `[callback]` {Function} an optional callback, used in conjunction with async
formatters

You can use `send()` to wrap up all the usual `writeHead()`, `write()`, `end()`
Expand Down
49 changes: 1 addition & 48 deletions docs/pages/guides/server.md
Expand Up @@ -484,7 +484,7 @@ want:
```js
restify.createServer({
formatters: {
'application/foo; q=0.9': function formatFoo(req, res, body, cb) {
'application/foo; q=0.9': function formatFoo(req, res, body) {
if (body instanceof Error)
return body.stack;

Expand All @@ -511,53 +511,6 @@ res.write(body);
res.end();
```

Formatters can also be async, in which case a callback is available to you as
the fourth parameter. If you choose to use an async formatter for a particular
content-type, it is required to pass a callback parameter to `res.send()`. If a
callback is not provided, then restify will throw an error:

```js
var server = restify.createServer({
formatters: {
'application/foo-async': function formatFoo(req, res, body, cb) {

someAsyncMethod(body, function(err, formattedBody) {
if (err) {
return cb(err);
}
return cb(null, formattedBody);
});
}
}
});

server.get('/fooAsync', function(req, res, next) {
res.send(fooData, next); // => callback required!
});
```

In the event of an error with your async formatter, ensure that the error is
passed to the callback. The default behavior in this scenario is for restify to
send an empty 500 response to the client, since restify can make no assumptions
about the correct format for your data. Alternatively, you can listen to a
`FormatterError` event on the server. This event is fired whenever an async
formatter returns an error, allowing you to write a custom response if
necessary. response if necessary. If you listen to this event, you _must_
flush a response, or else the request will hang. It is highly recommended to
avoid using `res.send()` within this handler to avoid any issues that might
have caused the async formatter to fail to begin with. Instead, use the native
node response methods:


```js
server.on('FormatterError', function(req, res, route, err) {
// the original body that caused formatting failure is available provided
// on the error object - err.context.rawBody
res.end('boom! could not format body!');
});
```


## Error handling

It is common to want to handle an error conditions the same way. As an example,
Expand Down

0 comments on commit a2e300f

Please sign in to comment.