Skip to content

Commit d5bc53e

Browse files
committed
feat(rest): Improve result serialization for http
Map number/boolean to json by default Add support for buffer and stream.Readable
1 parent 15d04fa commit d5bc53e

File tree

2 files changed

+46
-10
lines changed

2 files changed

+46
-10
lines changed

packages/rest/src/writer.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import {ServerResponse as Response} from 'http';
77
import {OperationRetval} from './internal-types';
88
import {HttpError} from 'http-errors';
9+
import {Readable} from 'stream';
10+
911
/**
1012
* Writes the result from Application controller method
1113
* into the HTTP response
@@ -20,13 +22,26 @@ export function writeResultToResponse(
2022
result: OperationRetval,
2123
): void {
2224
if (result) {
25+
if (result instanceof Readable || typeof result.pipe === 'function') {
26+
response.setHeader('Content-Type', 'application/octet-stream');
27+
// Stream
28+
result.pipe(response);
29+
return;
30+
}
2331
switch (typeof result) {
2432
case 'object':
25-
// TODO(ritch) remove this, should be configurable
26-
// See https://github.com/strongloop/loopback-next/issues/436
27-
response.setHeader('Content-Type', 'application/json');
28-
// TODO(bajtos) handle errors - JSON.stringify can throw
29-
result = JSON.stringify(result);
33+
case 'boolean':
34+
case 'number':
35+
if (Buffer.isBuffer(result)) {
36+
// Buffer for binary data
37+
response.setHeader('Content-Type', 'application/octet-stream');
38+
} else {
39+
// TODO(ritch) remove this, should be configurable
40+
// See https://github.com/strongloop/loopback-next/issues/436
41+
response.setHeader('Content-Type', 'application/json');
42+
// TODO(bajtos) handle errors - JSON.stringify can throw
43+
result = JSON.stringify(result);
44+
}
3045
break;
3146
default:
3247
response.setHeader('Content-Type', 'text/plain');

packages/rest/test/unit/writer.test.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// License text available at https://opensource.org/licenses/MIT
55

66
import {ServerResponse, writeResultToResponse} from '../..';
7-
7+
import {Duplex} from 'stream';
88
import {expect, mockResponse, ShotObservedResponse} from '@loopback/testlab';
99

1010
describe('writer', () => {
@@ -31,22 +31,43 @@ describe('writer', () => {
3131
expect(result.payload).to.equal('{"name":"Joe"}');
3232
});
3333

34-
it('writes boolean result to response as text', async () => {
34+
it('writes boolean result to response as json', async () => {
3535
writeResultToResponse(response, true);
3636
const result = await observedResponse;
3737

38-
expect(result.headers['content-type']).to.eql('text/plain');
38+
expect(result.headers['content-type']).to.eql('application/json');
3939
expect(result.payload).to.equal('true');
4040
});
4141

42-
it('writes number result to response as text', async () => {
42+
it('writes number result to response as json', async () => {
4343
writeResultToResponse(response, 2);
4444
const result = await observedResponse;
4545

46-
expect(result.headers['content-type']).to.eql('text/plain');
46+
expect(result.headers['content-type']).to.eql('application/json');
4747
expect(result.payload).to.equal('2');
4848
});
4949

50+
it('writes buffer result to response as binary', async () => {
51+
const buf = Buffer.from('ABC123');
52+
writeResultToResponse(response, buf);
53+
const result = await observedResponse;
54+
55+
expect(result.headers['content-type']).to.eql('application/octet-stream');
56+
expect(result.payload).to.equal('ABC123');
57+
});
58+
59+
it('writes stream result to response as binary', async () => {
60+
const buf = Buffer.from('ABC123');
61+
const stream = new Duplex();
62+
stream.push(buf);
63+
stream.push(null);
64+
writeResultToResponse(response, stream);
65+
const result = await observedResponse;
66+
67+
expect(result.headers['content-type']).to.eql('application/octet-stream');
68+
expect(result.payload).to.equal('ABC123');
69+
});
70+
5071
function setupResponseMock() {
5172
const responseMock = mockResponse();
5273
response = responseMock.response;

0 commit comments

Comments
 (0)