|
3 | 3 | // This file is licensed under the MIT License. |
4 | 4 | // License text available at https://opensource.org/licenses/MIT |
5 | 5 |
|
6 | | -import {ServerRequest} from 'http'; |
7 | | -import * as HttpErrors from 'http-errors'; |
| 6 | +import {REQUEST_BODY_INDEX} from '@loopback/openapi-v3'; |
8 | 7 | import { |
| 8 | + isReferenceObject, |
9 | 9 | OperationObject, |
10 | 10 | ParameterObject, |
11 | | - isReferenceObject, |
12 | 11 | SchemasObject, |
13 | 12 | } from '@loopback/openapi-v3-types'; |
14 | | -import {REQUEST_BODY_INDEX} from '@loopback/openapi-v3'; |
| 13 | +import * as debugModule from 'debug'; |
| 14 | +import {ServerRequest} from 'http'; |
| 15 | +import * as HttpErrors from 'http-errors'; |
| 16 | +import * as parseUrl from 'parseurl'; |
| 17 | +import {parse as parseQuery} from 'qs'; |
15 | 18 | import {promisify} from 'util'; |
16 | | -import {OperationArgs, Request, PathParameterValues} from './types'; |
17 | | -import {ResolvedRoute} from './router/routing-table'; |
18 | 19 | import {coerceParameter} from './coercion/coerce-parameter'; |
19 | | -import {validateRequestBody} from './validation/request-body.validator'; |
20 | 20 | import {RestHttpErrors} from './index'; |
| 21 | +import {ResolvedRoute} from './router/routing-table'; |
| 22 | +import {OperationArgs, PathParameterValues, Request} from './types'; |
| 23 | +import {validateRequestBody} from './validation/request-body.validator'; |
| 24 | + |
21 | 25 | type HttpError = HttpErrors.HttpError; |
22 | | -import * as debugModule from 'debug'; |
| 26 | + |
23 | 27 | const debug = debugModule('loopback:rest:parser'); |
24 | 28 |
|
| 29 | +export const QUERY_NOT_PARSED = {}; |
| 30 | +Object.freeze(QUERY_NOT_PARSED); |
| 31 | + |
25 | 32 | // tslint:disable-next-line:no-any |
26 | 33 | type MaybeBody = any | undefined; |
27 | 34 |
|
@@ -134,22 +141,31 @@ function getParamFromRequest( |
134 | 141 | request: Request, |
135 | 142 | pathParams: PathParameterValues, |
136 | 143 | ) { |
137 | | - let result; |
138 | 144 | switch (spec.in) { |
139 | 145 | case 'query': |
140 | | - result = request.query[spec.name]; |
141 | | - break; |
| 146 | + ensureRequestQueryWasParsed(request); |
| 147 | + return request.query[spec.name]; |
142 | 148 | case 'path': |
143 | | - result = pathParams[spec.name]; |
144 | | - break; |
| 149 | + return pathParams[spec.name]; |
145 | 150 | case 'header': |
146 | 151 | // @jannyhou TBD: check edge cases |
147 | | - result = request.headers[spec.name.toLowerCase()]; |
| 152 | + return request.headers[spec.name.toLowerCase()]; |
148 | 153 | break; |
149 | 154 | // TODO(jannyhou) to support `cookie`, |
150 | 155 | // see issue https://github.com/strongloop/loopback-next/issues/997 |
151 | 156 | default: |
152 | 157 | throw RestHttpErrors.invalidParamLocation(spec.in); |
153 | 158 | } |
154 | | - return result; |
| 159 | +} |
| 160 | + |
| 161 | +function ensureRequestQueryWasParsed(request: Request) { |
| 162 | + if (request.query && request.query !== QUERY_NOT_PARSED) return; |
| 163 | + |
| 164 | + const input = parseUrl(request)!.query; |
| 165 | + if (input && typeof input === 'string') { |
| 166 | + request.query = parseQuery(input); |
| 167 | + } else { |
| 168 | + request.query = {}; |
| 169 | + } |
| 170 | + debug('Parsed request query: ', request.query); |
155 | 171 | } |
0 commit comments