UNPKG

6.85 kBJavaScriptView Raw
1"use strict";
2// Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
3// Node module: @loopback/rest
4// This file is licensed under the MIT License.
5// License text available at https://opensource.org/licenses/MIT
6Object.defineProperty(exports, "__esModule", { value: true });
7exports.RequestBodyParser = void 0;
8const tslib_1 = require("tslib");
9const core_1 = require("@loopback/core");
10const openapi_v3_1 = require("@loopback/openapi-v3");
11const debug_1 = tslib_1.__importDefault(require("debug"));
12const type_is_1 = require("type-is");
13const rest_http_error_1 = require("../rest-http-error");
14const body_parser_helpers_1 = require("./body-parser.helpers");
15const types_1 = require("./types");
16const debug = (0, debug_1.default)('loopback:rest:body-parser');
17let RequestBodyParser = class RequestBodyParser {
18 constructor(parsers, ctx) {
19 this.ctx = ctx;
20 this.parsers = sortParsers(parsers !== null && parsers !== void 0 ? parsers : []);
21 if (debug.enabled) {
22 debug('Body parsers: ', this.parsers.map(p => p.name));
23 }
24 }
25 async loadRequestBodyIfNeeded(operationSpec, request) {
26 const { requestBody, customParser } = await this._matchRequestBodySpec(operationSpec, request);
27 if (!operationSpec.requestBody)
28 return requestBody;
29 const matchedMediaType = requestBody.mediaType;
30 try {
31 if (customParser) {
32 // Invoke the custom parser
33 const body = await this._invokeCustomParser(customParser, request);
34 debug('Parsed request body', body);
35 return Object.assign(requestBody, body);
36 }
37 else {
38 const parser = this._findParser(matchedMediaType);
39 if (parser) {
40 const body = await parser.parse(request);
41 debug('Parsed request body', body);
42 return Object.assign(requestBody, body);
43 }
44 }
45 }
46 catch (err) {
47 debug('Request body parsing error', err);
48 throw (0, body_parser_helpers_1.normalizeParsingError)(err);
49 }
50 throw rest_http_error_1.RestHttpErrors.unsupportedMediaType(matchedMediaType);
51 }
52 /**
53 * Match the http request to a given media type of the request body spec
54 */
55 async _matchRequestBodySpec(operationSpec, request) {
56 var _a;
57 const requestBody = {
58 value: undefined,
59 };
60 if (!operationSpec.requestBody)
61 return { requestBody };
62 const contentType = (_a = (0, body_parser_helpers_1.getContentType)(request)) !== null && _a !== void 0 ? _a : 'application/json';
63 debug('Loading request body with content type %j', contentType);
64 // the type of `operationSpec.requestBody` could be `RequestBodyObject`
65 // or `ReferenceObject`, resolving a `$ref` value is not supported yet.
66 if ((0, openapi_v3_1.isReferenceObject)(operationSpec.requestBody)) {
67 throw new Error('$ref requestBody is not supported yet.');
68 }
69 let content = operationSpec.requestBody.content || {};
70 if (!Object.keys(content).length) {
71 content = {
72 // default to allow json and urlencoded
73 'application/json': { schema: { type: 'object' } },
74 'application/x-www-form-urlencoded': { schema: { type: 'object' } },
75 };
76 }
77 // Check of the request content type matches one of the expected media
78 // types in the request body spec
79 let matchedMediaType = false;
80 let customParser = undefined;
81 for (const type in content) {
82 matchedMediaType = (0, type_is_1.is)(contentType, type);
83 if (matchedMediaType) {
84 debug('Matched media type: %s -> %s', type, contentType);
85 requestBody.mediaType = contentType;
86 requestBody.schema = content[type].schema;
87 customParser = content[type]['x-parser'];
88 break;
89 }
90 }
91 if (!matchedMediaType) {
92 // No matching media type found, fail fast
93 throw rest_http_error_1.RestHttpErrors.unsupportedMediaType(contentType, Object.keys(content));
94 }
95 return { requestBody, customParser };
96 }
97 /**
98 * Find a body parser that supports the media type
99 * @param matchedMediaType - Media type
100 */
101 _findParser(matchedMediaType) {
102 for (const parser of this.parsers) {
103 if (!parser.supports(matchedMediaType)) {
104 debug('Body parser %s does not support %s', parser.name, matchedMediaType);
105 continue;
106 }
107 debug('Body parser %s found for %s', parser.name, matchedMediaType);
108 return parser;
109 }
110 }
111 /**
112 * Resolve and invoke a custom parser
113 * @param customParser - The parser name, class or function
114 * @param request - Http request
115 */
116 async _invokeCustomParser(customParser, request) {
117 if (typeof customParser === 'string') {
118 const parser = this.parsers.find(p => p.name === customParser ||
119 p.name === body_parser_helpers_1.builtinParsers.mapping[customParser]);
120 if (parser) {
121 debug('Using custom parser %s', customParser);
122 return parser.parse(request);
123 }
124 }
125 else if (typeof customParser === 'function') {
126 if (isBodyParserClass(customParser)) {
127 debug('Using custom parser class %s', customParser.name);
128 const parser = await (0, core_1.instantiateClass)(customParser, this.ctx);
129 return parser.parse(request);
130 }
131 else {
132 debug('Using custom parser function %s', customParser.name);
133 return customParser(request);
134 }
135 }
136 throw new Error('Custom parser not found: ' + customParser);
137 }
138};
139RequestBodyParser = tslib_1.__decorate([
140 tslib_1.__param(0, (0, core_1.inject)((0, core_1.filterByTag)(types_1.REQUEST_BODY_PARSER_TAG), { optional: true })),
141 tslib_1.__param(1, core_1.inject.context()),
142 tslib_1.__metadata("design:paramtypes", [Array, core_1.Context])
143], RequestBodyParser);
144exports.RequestBodyParser = RequestBodyParser;
145/**
146 * Test if a function is a body parser class or plain function
147 * @param fn
148 */
149function isBodyParserClass(fn) {
150 return fn.toString().startsWith('class ');
151}
152/**
153 * Sort body parsers so that built-in ones are used after extensions
154 * @param parsers
155 */
156function sortParsers(parsers) {
157 return parsers.sort((a, b) => (0, core_1.compareByOrder)(a.name, b.name, body_parser_helpers_1.builtinParsers.names));
158}
159//# sourceMappingURL=body-parser.js.map
\No newline at end of file