From 5a1214da3748d35cb46df85dea8b8db4c0dafc35 Mon Sep 17 00:00:00 2001 From: mrmodise Date: Fri, 30 Oct 2020 18:06:32 +0200 Subject: [PATCH] docs: move parsing-requests recipes to how-to guides Signed-off-by: mrmodise --- docs/site/Parsing-requests-guide.md | 235 ++++++++++++++++++++++++++++ docs/site/Parsing-requests.md | 228 +-------------------------- docs/site/sidebars/lb4_sidebar.yml | 3 + 3 files changed, 240 insertions(+), 226 deletions(-) create mode 100644 docs/site/Parsing-requests-guide.md diff --git a/docs/site/Parsing-requests-guide.md b/docs/site/Parsing-requests-guide.md new file mode 100644 index 000000000000..2cacc096d3cc --- /dev/null +++ b/docs/site/Parsing-requests-guide.md @@ -0,0 +1,235 @@ +--- +lang: en +title: 'Guide to Parsing requests' +keywords: LoopBack 4.0, LoopBack 4, Node.js, TypeScript, OpenAPI +sidebar: lb4_sidebar +permalink: /doc/en/lb4/Parsing-requests-guide.html +--- + +## Custom Error Messages + +You can also specify custom error messages for JSON schema validation rules, as +explained in +[Custom Validation Rules and Error Messages](Model.md#custom-validation-rules-and-error-messages). + +A full list of options & usage scenarios could be found in the +[documentation of AJV errors](https://github.com/epoberezkin/ajv-errors). + +One request body specification could contain multiple content types. Our +supported content types are `json`, `urlencoded`, and `text`. The client should +set `Content-Type` http header to `application/json`, +`application/x-www-form-urlencoded`, or `text/plain`. Its value is matched +against the list of media types defined in the `requestBody.content` object of +the OpenAPI operation spec. If no matching media types is found or the type is +not supported yet, an `UnsupportedMediaTypeError` (http statusCode 415) will be +reported. + +Please note that `urlencoded` media type does not support data typing. For +example, `key=3` is parsed as `{key: '3'}`. The raw result is then coerced by +AJV based on the matching content schema. The coercion rules are described in +[AJV type coercion rules](https://github.com/epoberezkin/ajv/blob/master/COERCION.md). + +The [qs](https://github.com/ljharb/qs) is used to parse complex strings. For +example, given the following request body definition: + +```ts +const requestBodyObject = { + description: 'data', + content: { + 'application/x-www-form-urlencoded': { + schema: { + type: 'object', + properties: { + name: {type: 'string'}, + location: { + type: 'object', + properties: { + lat: {type: 'number'}, + lng: {type: 'number'}, + }, + }, + tags: { + type: 'array', + items: {type: 'string'}, + }, + }, + }, + }, + }, +}; +``` + +The encoded value +`'name=IBM%20HQ&location[lat]=0.741895&location[lng]=-73.989308&tags[0]=IT&tags[1]=NY'` +is parsed and coerced as: + +```ts +{ + name: 'IBM HQ', + location: {lat: 0.741895, lng: -73.989308}, + tags: ['IT', 'NY'], +} +``` + +The request body parser options (such as `limit`) can now be configured by +binding the value to `RestBindings.REQUEST_BODY_PARSER_OPTIONS` +('rest.requestBodyParserOptions'). For example, + +```ts +server.bind(RestBindings.REQUEST_BODY_PARSER_OPTIONS).to({ + limit: '4MB', +}); +``` + +The options can be media type specific, for example: + +```ts +server.bind(RestBindings.REQUEST_BODY_PARSER_OPTIONS).to({ + json: {limit: '4MB'}, + text: {limit: '1MB'}, +}); +``` + +The list of options can be found in the +[body-parser](https://github.com/expressjs/body-parser/#options) module. + +By default, the `limit` is `1MB`. Any request with a body length exceeding the +limit will be rejected with http status code 413 (request entity too large). + +A few tips worth mentioning: + +- If a model property's type refers to another model, make sure it is also + decorated with `@model` decorator. + +- If you're using API first development approach, you can also provide the + request body specification in decorators like `route()` and + [`api()`](Decorators.md#api-decorator), this requires you to provide a + completed request body specification. + +## Extend Request Body Parsing + +See [Extending request body parsing](./Extending-request-body-parsing.md) for +more details. + +## Specify Custom Parser by Controller Methods + +In some cases, a controller method wants to handle request body parsing by +itself, such as, to accept `multipart/form-data` for file uploads or stream-line +a large json document. To bypass body parsing, the `'x-parser'` extension can be +set to `'stream'` for a media type of the request body content. For example, + +```ts +class FileUploadController { + async upload( + @requestBody({ + description: 'multipart/form-data value.', + required: true, + content: { + 'multipart/form-data': { + // Skip body parsing + 'x-parser': 'stream', + schema: {type: 'object'}, + }, + }, + }) + request: Request, + @inject(RestBindings.Http.RESPONSE) response: Response, + ): Promise { + const storage = multer.memoryStorage(); + const upload = multer({storage}); + return new Promise((resolve, reject) => { + upload.any()(request, response, err => { + if (err) reject(err); + else { + resolve({ + files: request.files, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fields: (request as any).fields, + }); + } + }); + }); + } +} +``` + +The `x-parser` value can be one of the following: + +1. Name of the parser, such as `json`, `raw`, or `stream` + +- `stream`: keeps the http request body as a stream without parsing +- `raw`: parses the http request body as a `Buffer` + +```ts +{ + 'x-parser': 'stream' +} +``` + +2. A body parser class + +```ts +{ + 'x-parser': JsonBodyParser +} +``` + +3. A body parser function, for example: + +```ts +function parseJson(request: Request): Promise { + return new JsonBodyParser().parse(request); +} + +{ + 'x-parser': parseJson +} +``` + +## Localizing Errors + +A body data may break multiple validation rules, like missing required fields, +data in a wrong type, data that exceeds the maximum length, etc...The validation +errors are returned in batch mode, and user can find all of them in +`error.details`, which describes errors in a machine-readable way. + +Each element in the `error.details` array reports one error. It contains 4 +attributes: + +- `path`: The path to the invalid field. +- `code`: A single word code represents the error's type. +- `message`: A human readable description of the error. +- `info`: Some additional details that the 3 attributes above don't cover. + +In most cases `path` shows which field in the body data is invalid. For example, +if an object schema's `id` field should be a string, while the data in body has +it as a number: `{id: 1, name: 'Foo'}`. Then the error entry is: + +```ts +{ + path: '/id', + code: 'type', + message: 'should be string', + info: {type: 'string'}, +} +``` + +And in this case the error code is `type`. A reference of all the possible code +could be found in +[ajv validation error keywords(codes)](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md). + +In some exception scenarios, like a required field is missing, the `path` is +empty, but the field location is easy to find in `message` and `info`. For +example, `id` is a required field while it's missing in a request body: +`{name: 'Foo'}`, the error entry will be: + +```ts +{ + // `path` is empty + path: '', + code: 'required', + message: "should have required property 'id'", + // you can parse the missing field from `info.missingProperty` + info: {missingProperty: 'id'}, +}, +``` diff --git a/docs/site/Parsing-requests.md b/docs/site/Parsing-requests.md index fa73c7ec8081..79c2ba7abb02 100644 --- a/docs/site/Parsing-requests.md +++ b/docs/site/Parsing-requests.md @@ -206,230 +206,6 @@ class Product extends Entity { A full list of validation keywords could be found in the [documentation of AJV validation keywords](https://github.com/epoberezkin/ajv#validation-keywords). -##### Custom Error Messages +## Common tasks -You can also specify custom error messages for JSON schema validation rules, as -explained in -[Custom Validation Rules and Error Messages](Model.md#custom-validation-rules-and-error-messages). - -A full list of options & usage scenarios could be found in the -[documentation of AJV errors](https://github.com/epoberezkin/ajv-errors). - -One request body specification could contain multiple content types. Our -supported content types are `json`, `urlencoded`, and `text`. The client should -set `Content-Type` http header to `application/json`, -`application/x-www-form-urlencoded`, or `text/plain`. Its value is matched -against the list of media types defined in the `requestBody.content` object of -the OpenAPI operation spec. If no matching media types is found or the type is -not supported yet, an `UnsupportedMediaTypeError` (http statusCode 415) will be -reported. - -Please note that `urlencoded` media type does not support data typing. For -example, `key=3` is parsed as `{key: '3'}`. The raw result is then coerced by -AJV based on the matching content schema. The coercion rules are described in -[AJV type coercion rules](https://github.com/epoberezkin/ajv/blob/master/COERCION.md). - -The [qs](https://github.com/ljharb/qs) is used to parse complex strings. For -example, given the following request body definition: - -```ts -const requestBodyObject = { - description: 'data', - content: { - 'application/x-www-form-urlencoded': { - schema: { - type: 'object', - properties: { - name: {type: 'string'}, - location: { - type: 'object', - properties: { - lat: {type: 'number'}, - lng: {type: 'number'}, - }, - }, - tags: { - type: 'array', - items: {type: 'string'}, - }, - }, - }, - }, - }, -}; -``` - -The encoded value -`'name=IBM%20HQ&location[lat]=0.741895&location[lng]=-73.989308&tags[0]=IT&tags[1]=NY'` -is parsed and coerced as: - -```ts -{ - name: 'IBM HQ', - location: {lat: 0.741895, lng: -73.989308}, - tags: ['IT', 'NY'], -} -``` - -The request body parser options (such as `limit`) can now be configured by -binding the value to `RestBindings.REQUEST_BODY_PARSER_OPTIONS` -('rest.requestBodyParserOptions'). For example, - -```ts -server.bind(RestBindings.REQUEST_BODY_PARSER_OPTIONS).to({ - limit: '4MB', -}); -``` - -The options can be media type specific, for example: - -```ts -server.bind(RestBindings.REQUEST_BODY_PARSER_OPTIONS).to({ - json: {limit: '4MB'}, - text: {limit: '1MB'}, -}); -``` - -The list of options can be found in the -[body-parser](https://github.com/expressjs/body-parser/#options) module. - -By default, the `limit` is `1MB`. Any request with a body length exceeding the -limit will be rejected with http status code 413 (request entity too large). - -A few tips worth mentioning: - -- If a model property's type refers to another model, make sure it is also - decorated with `@model` decorator. - -- If you're using API first development approach, you can also provide the - request body specification in decorators like `route()` and - [`api()`](Decorators.md#api-decorator), this requires you to provide a - completed request body specification. - -#### Extend Request Body Parsing - -See [Extending request body parsing](./Extending-request-body-parsing.md) for -more details. - -#### Specify Custom Parser by Controller Methods - -In some cases, a controller method wants to handle request body parsing by -itself, such as, to accept `multipart/form-data` for file uploads or stream-line -a large json document. To bypass body parsing, the `'x-parser'` extension can be -set to `'stream'` for a media type of the request body content. For example, - -```ts -class FileUploadController { - async upload( - @requestBody({ - description: 'multipart/form-data value.', - required: true, - content: { - 'multipart/form-data': { - // Skip body parsing - 'x-parser': 'stream', - schema: {type: 'object'}, - }, - }, - }) - request: Request, - @inject(RestBindings.Http.RESPONSE) response: Response, - ): Promise { - const storage = multer.memoryStorage(); - const upload = multer({storage}); - return new Promise((resolve, reject) => { - upload.any()(request, response, err => { - if (err) reject(err); - else { - resolve({ - files: request.files, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - fields: (request as any).fields, - }); - } - }); - }); - } -} -``` - -The `x-parser` value can be one of the following: - -1. Name of the parser, such as `json`, `raw`, or `stream` - -- `stream`: keeps the http request body as a stream without parsing -- `raw`: parses the http request body as a `Buffer` - -```ts -{ - 'x-parser': 'stream' -} -``` - -2. A body parser class - -```ts -{ - 'x-parser': JsonBodyParser -} -``` - -3. A body parser function, for example: - -```ts -function parseJson(request: Request): Promise { - return new JsonBodyParser().parse(request); -} - -{ - 'x-parser': parseJson -} -``` - -#### Localizing Errors - -A body data may break multiple validation rules, like missing required fields, -data in a wrong type, data that exceeds the maximum length, etc...The validation -errors are returned in batch mode, and user can find all of them in -`error.details`, which describes errors in a machine-readable way. - -Each element in the `error.details` array reports one error. It contains 4 -attributes: - -- `path`: The path to the invalid field. -- `code`: A single word code represents the error's type. -- `message`: A human readable description of the error. -- `info`: Some additional details that the 3 attributes above don't cover. - -In most cases `path` shows which field in the body data is invalid. For example, -if an object schema's `id` field should be a string, while the data in body has -it as a number: `{id: 1, name: 'Foo'}`. Then the error entry is: - -```ts -{ - path: '/id', - code: 'type', - message: 'should be string', - info: {type: 'string'}, -} -``` - -And in this case the error code is `type`. A reference of all the possible code -could be found in -[ajv validation error keywords(codes)](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md). - -In some exception scenarios, like a required field is missing, the `path` is -empty, but the field location is easy to find in `message` and `info`. For -example, `id` is a required field while it's missing in a request body: -`{name: 'Foo'}`, the error entry will be: - -```ts -{ - // `path` is empty - path: '', - code: 'required', - message: "should have required property 'id'", - // you can parse the missing field from `info.missingProperty` - info: {missingProperty: 'id'}, -}, -``` +- [Guide to parsing requests](Parsing-requests-guide.md) diff --git a/docs/site/sidebars/lb4_sidebar.yml b/docs/site/sidebars/lb4_sidebar.yml index b74b358e07f8..4eea7825503e 100644 --- a/docs/site/sidebars/lb4_sidebar.yml +++ b/docs/site/sidebars/lb4_sidebar.yml @@ -346,6 +346,9 @@ children: - title: 'Call Other Services' url: Calling-other-APIs-and-web-services.html output: 'web, pdf' + - title: 'Parsing requests' + output: 'web, pdf' + url: Parsing-requests-guide.html - title: 'Validating Data' url: Validation.html