fastify 3.27.4 → 4.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.taprc +3 -0
- package/README.md +7 -7
- package/build/build-error-serializer.js +27 -0
- package/build/build-validation.js +47 -35
- package/docs/Guides/Database.md +320 -0
- package/docs/Guides/Ecosystem.md +9 -0
- package/docs/Guides/Getting-Started.md +7 -7
- package/docs/Guides/Plugins-Guide.md +1 -1
- package/docs/Guides/Serverless.md +3 -3
- package/docs/Guides/Testing.md +2 -2
- package/docs/Migration-Guide-V4.md +12 -0
- package/docs/Reference/ContentTypeParser.md +4 -0
- package/docs/Reference/Decorators.md +2 -2
- package/docs/Reference/Encapsulation.md +2 -2
- package/docs/Reference/Errors.md +51 -6
- package/docs/Reference/HTTP2.md +3 -3
- package/docs/Reference/Hooks.md +4 -7
- package/docs/Reference/LTS.md +5 -4
- package/docs/Reference/Plugins.md +3 -3
- package/docs/Reference/Reply.md +73 -22
- package/docs/Reference/Request.md +1 -3
- package/docs/Reference/Routes.md +22 -15
- package/docs/Reference/Server.md +69 -119
- package/docs/Reference/TypeScript.md +20 -22
- package/docs/Reference/Validation-and-Serialization.md +30 -55
- package/docs/Type-Providers.md +257 -0
- package/examples/asyncawait.js +1 -1
- package/examples/benchmark/hooks-benchmark-async-await.js +1 -1
- package/examples/benchmark/hooks-benchmark.js +1 -1
- package/examples/benchmark/simple.js +1 -1
- package/examples/hooks.js +2 -2
- package/examples/http2.js +1 -1
- package/examples/https.js +1 -1
- package/examples/parser.js +13 -3
- package/examples/route-prefix.js +1 -1
- package/examples/shared-schema.js +1 -1
- package/examples/simple-stream.js +18 -0
- package/examples/simple.js +1 -1
- package/examples/simple.mjs +1 -1
- package/examples/typescript-server.ts +1 -1
- package/examples/use-plugin.js +1 -1
- package/fastify.d.ts +34 -22
- package/fastify.js +40 -36
- package/lib/configValidator.js +902 -1023
- package/lib/contentTypeParser.js +6 -16
- package/lib/context.js +36 -10
- package/lib/decorate.js +3 -1
- package/lib/error-handler.js +158 -0
- package/lib/error-serializer.js +257 -0
- package/lib/errors.js +51 -9
- package/lib/fourOhFour.js +31 -20
- package/lib/handleRequest.js +10 -13
- package/lib/hooks.js +14 -9
- package/lib/pluginOverride.js +0 -3
- package/lib/pluginUtils.js +3 -2
- package/lib/reply.js +121 -175
- package/lib/request.js +13 -10
- package/lib/route.js +131 -138
- package/lib/schema-controller.js +2 -2
- package/lib/schemas.js +27 -1
- package/lib/server.js +242 -116
- package/lib/symbols.js +5 -3
- package/lib/validation.js +11 -9
- package/lib/warnings.js +4 -12
- package/lib/wrapThenable.js +4 -11
- package/package.json +37 -39
- package/test/404s.test.js +258 -125
- package/test/500s.test.js +3 -3
- package/test/als.test.js +1 -1
- package/test/async-await.test.js +20 -76
- package/test/bodyLimit.test.js +1 -1
- package/test/build-certificate.js +6 -7
- package/test/case-insensitive.test.js +4 -4
- package/test/close-pipelining.test.js +2 -2
- package/test/close.test.js +11 -11
- package/test/content-parser.test.js +32 -0
- package/test/context-config.test.js +52 -0
- package/test/custom-http-server.test.js +14 -7
- package/test/custom-parser-async.test.js +1 -66
- package/test/custom-parser.test.js +92 -159
- package/test/custom-querystring-parser.test.js +3 -3
- package/test/decorator.test.js +11 -13
- package/test/delete.test.js +6 -6
- package/test/encapsulated-error-handler.test.js +50 -0
- package/test/esm/index.test.js +0 -14
- package/test/fastify-instance.test.js +4 -4
- package/test/fluent-schema.test.js +4 -4
- package/test/genReqId.test.js +1 -1
- package/test/get.test.js +4 -4
- package/test/handler-context.test.js +2 -2
- package/test/head.test.js +1 -1
- package/test/helper.js +19 -4
- package/test/hooks-async.test.js +15 -48
- package/test/hooks.on-ready.test.js +10 -5
- package/test/hooks.test.js +78 -119
- package/test/http2/closing.test.js +10 -16
- package/test/http2/constraint.test.js +1 -1
- package/test/http2/head.test.js +1 -1
- package/test/http2/plain.test.js +1 -1
- package/test/http2/secure-with-fallback.test.js +1 -1
- package/test/http2/secure.test.js +1 -1
- package/test/http2/unknown-http-method.test.js +4 -10
- package/test/https/custom-https-server.test.js +12 -6
- package/test/https/https.test.js +1 -1
- package/test/input-validation.js +3 -3
- package/test/internals/handleRequest.test.js +6 -43
- package/test/internals/initialConfig.test.js +41 -12
- package/test/internals/logger.test.js +2 -2
- package/test/internals/reply.test.js +317 -48
- package/test/internals/request.test.js +13 -7
- package/test/internals/server.test.js +88 -0
- package/test/listen.deprecated.test.js +202 -0
- package/test/listen.test.js +140 -145
- package/test/logger.test.js +82 -42
- package/test/maxRequestsPerSocket.test.js +8 -6
- package/test/middleware.test.js +2 -25
- package/test/nullable-validation.test.js +53 -16
- package/test/output-validation.test.js +1 -1
- package/test/plugin.test.js +47 -21
- package/test/pretty-print.test.js +22 -10
- package/test/promises.test.js +1 -1
- package/test/proto-poisoning.test.js +6 -6
- package/test/register.test.js +3 -3
- package/test/reply-error.test.js +126 -15
- package/test/reply-trailers.test.js +270 -0
- package/test/request-error.test.js +3 -6
- package/test/route-hooks.test.js +18 -18
- package/test/route-prefix.test.js +2 -1
- package/test/route.test.js +206 -22
- package/test/router-options.test.js +2 -2
- package/test/schema-examples.test.js +11 -5
- package/test/schema-feature.test.js +25 -20
- package/test/schema-serialization.test.js +9 -9
- package/test/schema-special-usage.test.js +5 -153
- package/test/schema-validation.test.js +9 -9
- package/test/skip-reply-send.test.js +2 -2
- package/test/stream.test.js +82 -23
- package/test/throw.test.js +8 -5
- package/test/trust-proxy.test.js +6 -6
- package/test/type-provider.test.js +20 -0
- package/test/types/fastify.test-d.ts +10 -18
- package/test/types/hooks.test-d.ts +61 -5
- package/test/types/import.js +2 -0
- package/test/types/import.ts +1 -0
- package/test/types/instance.test-d.ts +68 -17
- package/test/types/logger.test-d.ts +44 -15
- package/test/types/reply.test-d.ts +2 -1
- package/test/types/request.test-d.ts +71 -1
- package/test/types/route.test-d.ts +8 -2
- package/test/types/schema.test-d.ts +2 -39
- package/test/types/type-provider.test-d.ts +424 -0
- package/test/url-rewriting.test.js +3 -3
- package/test/validation-error-handling.test.js +8 -8
- package/test/versioned-routes.test.js +30 -18
- package/test/wrapThenable.test.js +7 -6
- package/types/content-type-parser.d.ts +17 -8
- package/types/hooks.d.ts +182 -85
- package/types/instance.d.ts +286 -118
- package/types/logger.d.ts +18 -104
- package/types/plugin.d.ts +10 -4
- package/types/reply.d.ts +18 -12
- package/types/request.d.ts +13 -8
- package/types/route.d.ts +62 -34
- package/types/schema.d.ts +1 -1
- package/types/type-provider.d.ts +99 -0
- package/types/utils.d.ts +1 -1
- package/lib/schema-compilers.js +0 -12
- package/test/emit-warning.test.js +0 -166
package/lib/route.js
CHANGED
|
@@ -19,6 +19,7 @@ const {
|
|
|
19
19
|
FST_ERR_SCH_VALIDATION_BUILD,
|
|
20
20
|
FST_ERR_SCH_SERIALIZATION_BUILD,
|
|
21
21
|
FST_ERR_DEFAULT_ROUTE_INVALID_TYPE,
|
|
22
|
+
FST_ERR_DUPLICATED_ROUTE,
|
|
22
23
|
FST_ERR_INVALID_URL
|
|
23
24
|
} = require('./errors')
|
|
24
25
|
|
|
@@ -27,19 +28,17 @@ const {
|
|
|
27
28
|
kLogLevel,
|
|
28
29
|
kLogSerializers,
|
|
29
30
|
kHooks,
|
|
30
|
-
kHooksDeprecatedPreParsing,
|
|
31
31
|
kSchemaController,
|
|
32
32
|
kOptions,
|
|
33
|
-
kContentTypeParser,
|
|
34
|
-
kReply,
|
|
35
33
|
kReplySerializerDefault,
|
|
36
34
|
kReplyIsError,
|
|
37
|
-
kRequest,
|
|
38
35
|
kRequestPayloadStream,
|
|
39
36
|
kDisableRequestLogging,
|
|
40
37
|
kSchemaErrorFormatter,
|
|
41
|
-
kErrorHandler
|
|
38
|
+
kErrorHandler,
|
|
39
|
+
kHasBeenDecorated
|
|
42
40
|
} = require('./symbols.js')
|
|
41
|
+
const { buildErrorHandler } = require('./error-handler')
|
|
43
42
|
|
|
44
43
|
function buildRouting (options) {
|
|
45
44
|
const { keepAliveConnections } = options
|
|
@@ -136,22 +135,17 @@ function buildRouting (options) {
|
|
|
136
135
|
|
|
137
136
|
throwIfAlreadyStarted('Cannot add route when fastify instance is already started!')
|
|
138
137
|
|
|
138
|
+
const path = opts.url || opts.path || ''
|
|
139
|
+
|
|
139
140
|
if (Array.isArray(opts.method)) {
|
|
140
141
|
// eslint-disable-next-line no-var
|
|
141
142
|
for (var i = 0; i < opts.method.length; ++i) {
|
|
142
|
-
|
|
143
|
-
if (supportedMethods.indexOf(method) === -1) {
|
|
144
|
-
throw new Error(`${method} method is not supported!`)
|
|
145
|
-
}
|
|
143
|
+
validateMethodAndSchemaBodyOption(opts.method[i], path, opts.schema)
|
|
146
144
|
}
|
|
147
145
|
} else {
|
|
148
|
-
|
|
149
|
-
throw new Error(`${opts.method} method is not supported!`)
|
|
150
|
-
}
|
|
146
|
+
validateMethodAndSchemaBodyOption(opts.method, path, opts.schema)
|
|
151
147
|
}
|
|
152
148
|
|
|
153
|
-
const path = opts.url || opts.path
|
|
154
|
-
|
|
155
149
|
if (!opts.handler) {
|
|
156
150
|
throw new Error(`Missing handler function for ${opts.method}:${path} route.`)
|
|
157
151
|
}
|
|
@@ -164,42 +158,33 @@ function buildRouting (options) {
|
|
|
164
158
|
|
|
165
159
|
const prefix = this[kRoutePrefix]
|
|
166
160
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
} else if (path && path[0] === '/' && prefix.endsWith('/')) {
|
|
185
|
-
// Ensure that '/prefix/' + '/route' gets registered as '/prefix/route'
|
|
186
|
-
afterRouteAdded.call(this, { path: path.slice(1) }, notHandledErr, done)
|
|
187
|
-
} else {
|
|
188
|
-
afterRouteAdded.call(this, { path }, notHandledErr, done)
|
|
161
|
+
if (path === '/' && prefix.length > 0 && opts.method !== 'HEAD') {
|
|
162
|
+
switch (opts.prefixTrailingSlash) {
|
|
163
|
+
case 'slash':
|
|
164
|
+
addNewRoute.call(this, path)
|
|
165
|
+
break
|
|
166
|
+
case 'no-slash':
|
|
167
|
+
addNewRoute.call(this, '')
|
|
168
|
+
break
|
|
169
|
+
case 'both':
|
|
170
|
+
default:
|
|
171
|
+
addNewRoute.call(this, '')
|
|
172
|
+
// If ignoreTrailingSlash is set to true we need to add only the '' route to prevent adding an incomplete one.
|
|
173
|
+
if (ignoreTrailingSlash !== true) {
|
|
174
|
+
addNewRoute.call(this, path, true)
|
|
175
|
+
}
|
|
189
176
|
}
|
|
190
|
-
})
|
|
177
|
+
} else if (path[0] === '/' && prefix.endsWith('/')) {
|
|
178
|
+
// Ensure that '/prefix/' + '/route' gets registered as '/prefix/route'
|
|
179
|
+
addNewRoute.call(this, path.slice(1))
|
|
180
|
+
} else {
|
|
181
|
+
addNewRoute.call(this, path)
|
|
182
|
+
}
|
|
191
183
|
|
|
192
184
|
// chainable api
|
|
193
185
|
return this
|
|
194
186
|
|
|
195
|
-
|
|
196
|
-
* This function sets up a new route, its log serializers, and triggers route hooks.
|
|
197
|
-
*
|
|
198
|
-
* @param {object} opts contains route `path` and `prefixing` flag which indicates if this is an auto-prefixed route, e.g. `fastify.register(routes, { prefix: '/foo' })`
|
|
199
|
-
* @param {*} notHandledErr error object to be passed back to the original invoker
|
|
200
|
-
* @param {*} done callback
|
|
201
|
-
*/
|
|
202
|
-
function afterRouteAdded ({ path, prefixing = false }, notHandledErr, done) {
|
|
187
|
+
function addNewRoute (path, prefixing = false) {
|
|
203
188
|
const url = prefix + path
|
|
204
189
|
|
|
205
190
|
opts.url = url
|
|
@@ -217,117 +202,122 @@ function buildRouting (options) {
|
|
|
217
202
|
}
|
|
218
203
|
|
|
219
204
|
if (prefixing === false) {
|
|
220
|
-
|
|
205
|
+
// run 'onRoute' hooks
|
|
221
206
|
for (const hook of this[kHooks].onRoute) {
|
|
222
|
-
|
|
223
|
-
hook.call(this, opts)
|
|
224
|
-
} catch (error) {
|
|
225
|
-
done(error)
|
|
226
|
-
return
|
|
227
|
-
}
|
|
207
|
+
hook.call(this, opts)
|
|
228
208
|
}
|
|
229
209
|
}
|
|
230
210
|
|
|
211
|
+
const constraints = opts.constraints || {}
|
|
231
212
|
const config = {
|
|
232
213
|
...opts.config,
|
|
233
214
|
url,
|
|
234
215
|
method: opts.method
|
|
235
216
|
}
|
|
236
|
-
|
|
217
|
+
|
|
218
|
+
const context = new Context({
|
|
219
|
+
schema: opts.schema,
|
|
220
|
+
handler: opts.handler.bind(this),
|
|
221
|
+
config,
|
|
222
|
+
errorHandler: opts.errorHandler,
|
|
223
|
+
bodyLimit: opts.bodyLimit,
|
|
224
|
+
logLevel: opts.logLevel,
|
|
225
|
+
logSerializers: opts.logSerializers,
|
|
226
|
+
attachValidation: opts.attachValidation,
|
|
227
|
+
schemaErrorFormatter: opts.schemaErrorFormatter,
|
|
228
|
+
replySerializer: this[kReplySerializerDefault],
|
|
229
|
+
server: this
|
|
230
|
+
})
|
|
231
|
+
|
|
237
232
|
if (opts.version) {
|
|
238
233
|
warning.emit('FSTDEP008')
|
|
239
234
|
constraints.version = opts.version
|
|
240
235
|
}
|
|
241
236
|
|
|
242
|
-
const
|
|
243
|
-
opts.schema,
|
|
244
|
-
opts.handler.bind(this),
|
|
245
|
-
this[kReply],
|
|
246
|
-
this[kRequest],
|
|
247
|
-
this[kContentTypeParser],
|
|
248
|
-
config,
|
|
249
|
-
opts.errorHandler || this[kErrorHandler],
|
|
250
|
-
opts.bodyLimit,
|
|
251
|
-
opts.logLevel,
|
|
252
|
-
opts.logSerializers,
|
|
253
|
-
opts.attachValidation,
|
|
254
|
-
this[kReplySerializerDefault],
|
|
255
|
-
opts.schemaErrorFormatter || this[kSchemaErrorFormatter]
|
|
256
|
-
)
|
|
237
|
+
const headRouteExists = opts.method === 'HEAD' && router.find(opts.method, opts.url, constraints) != null
|
|
257
238
|
|
|
258
|
-
|
|
239
|
+
// Check if the current route is not for a sibling HEAD one
|
|
240
|
+
if (!headRouteExists) {
|
|
241
|
+
try {
|
|
242
|
+
router.on(opts.method, opts.url, { constraints }, routeHandler, context)
|
|
243
|
+
} catch (error) {
|
|
244
|
+
const isDuplicatedRoute = error.message.includes(`Method '${opts.method}' already declared for route '${opts.url}'`)
|
|
245
|
+
if (isDuplicatedRoute) {
|
|
246
|
+
throw new FST_ERR_DUPLICATED_ROUTE(opts.method, opts.url)
|
|
247
|
+
}
|
|
259
248
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
} catch (err) {
|
|
263
|
-
done(err)
|
|
264
|
-
return
|
|
249
|
+
throw error
|
|
250
|
+
}
|
|
265
251
|
}
|
|
266
252
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
253
|
+
this.after((notHandledErr, done) => {
|
|
254
|
+
// Send context async
|
|
255
|
+
context.errorHandler = opts.errorHandler ? buildErrorHandler(this[kErrorHandler], opts.errorHandler) : this[kErrorHandler]
|
|
256
|
+
context._parserOptions.limit = opts.bodyLimit || null
|
|
257
|
+
context.logLevel = opts.logLevel
|
|
258
|
+
context.logSerializers = opts.logSerializers
|
|
259
|
+
context.attachValidation = opts.attachValidation
|
|
260
|
+
context[kReplySerializerDefault] = this[kReplySerializerDefault]
|
|
261
|
+
context.schemaErrorFormatter = opts.schemaErrorFormatter || this[kSchemaErrorFormatter] || context.schemaErrorFormatter
|
|
262
|
+
|
|
263
|
+
// Run hooks and more
|
|
264
|
+
avvio.once('preReady', () => {
|
|
265
|
+
for (const hook of lifecycleHooks) {
|
|
266
|
+
const toSet = this[kHooks][hook]
|
|
267
|
+
.concat(opts[hook] || [])
|
|
268
|
+
.map(h => h.bind(this))
|
|
269
|
+
context[hook] = toSet.length ? toSet : null
|
|
270
|
+
}
|
|
270
271
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
272
|
+
// Optimization: avoid encapsulation if no decoration has been done.
|
|
273
|
+
while (!context.Request[kHasBeenDecorated] && context.Request.parent) {
|
|
274
|
+
context.Request = context.Request.parent
|
|
275
|
+
}
|
|
276
|
+
while (!context.Reply[kHasBeenDecorated] && context.Reply.parent) {
|
|
277
|
+
context.Reply = context.Reply.parent
|
|
278
|
+
}
|
|
277
279
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
avvio.once('preReady', () => {
|
|
282
|
-
for (const hook of lifecycleHooks) {
|
|
283
|
-
const toSet = this[kHooks][hook]
|
|
284
|
-
.concat(opts[hook] || [])
|
|
285
|
-
.map(h => {
|
|
286
|
-
const bound = h.bind(this)
|
|
287
|
-
|
|
288
|
-
// Track hooks deprecation markers
|
|
289
|
-
if (hook === 'preParsing') {
|
|
290
|
-
// Check for deprecation syntax
|
|
291
|
-
if (h.length === (h.constructor.name === 'AsyncFunction' ? 2 : 3)) {
|
|
292
|
-
warning.emit('FSTDEP004')
|
|
293
|
-
bound[kHooksDeprecatedPreParsing] = true
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return bound
|
|
298
|
-
})
|
|
299
|
-
context[hook] = toSet.length ? toSet : null
|
|
300
|
-
}
|
|
280
|
+
// Must store the 404 Context in 'preReady' because it is only guaranteed to
|
|
281
|
+
// be available after all of the plugins and routes have been loaded.
|
|
282
|
+
fourOhFour.setContext(this, context)
|
|
301
283
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
fourOhFour.setContext(this, context)
|
|
284
|
+
if (opts.schema) {
|
|
285
|
+
context.schema = normalizeSchema(context.schema, this.initialConfig)
|
|
305
286
|
|
|
306
|
-
|
|
307
|
-
|
|
287
|
+
const schemaController = this[kSchemaController]
|
|
288
|
+
if (!opts.validatorCompiler && (opts.schema.body || opts.schema.headers || opts.schema.querystring || opts.schema.params)) {
|
|
289
|
+
schemaController.setupValidator(this[kOptions])
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
292
|
+
compileSchemasForValidation(context, opts.validatorCompiler || schemaController.validatorCompiler)
|
|
293
|
+
} catch (error) {
|
|
294
|
+
throw new FST_ERR_SCH_VALIDATION_BUILD(opts.method, url, error.message)
|
|
295
|
+
}
|
|
308
296
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
297
|
+
if (opts.schema.response && !opts.serializerCompiler) {
|
|
298
|
+
schemaController.setupSerializer(this[kOptions])
|
|
299
|
+
}
|
|
300
|
+
try {
|
|
301
|
+
compileSchemasForSerialization(context, opts.serializerCompiler || schemaController.serializerCompiler)
|
|
302
|
+
} catch (error) {
|
|
303
|
+
throw new FST_ERR_SCH_SERIALIZATION_BUILD(opts.method, url, error.message)
|
|
304
|
+
}
|
|
317
305
|
}
|
|
306
|
+
})
|
|
318
307
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
308
|
+
const { exposeHeadRoute } = opts
|
|
309
|
+
const hasRouteExposeHeadRouteFlag = exposeHeadRoute != null
|
|
310
|
+
const shouldExposeHead = hasRouteExposeHeadRouteFlag ? exposeHeadRoute : globalExposeHeadRoutes
|
|
311
|
+
|
|
312
|
+
if (shouldExposeHead && options.method === 'GET' && !headRouteExists) {
|
|
313
|
+
const onSendHandlers = parseHeadOnSendHandlers(opts.onSend)
|
|
314
|
+
prepareRoute.call(this, 'HEAD', path, { ...opts, onSend: onSendHandlers })
|
|
315
|
+
} else if (headRouteExists && exposeHeadRoute) {
|
|
316
|
+
warning.emit('FSTDEP007')
|
|
327
317
|
}
|
|
328
|
-
})
|
|
329
318
|
|
|
330
|
-
|
|
319
|
+
done(notHandledErr)
|
|
320
|
+
})
|
|
331
321
|
}
|
|
332
322
|
}
|
|
333
323
|
|
|
@@ -429,6 +419,16 @@ function handleTimeout () {
|
|
|
429
419
|
)
|
|
430
420
|
}
|
|
431
421
|
|
|
422
|
+
function validateMethodAndSchemaBodyOption (method, path, schema) {
|
|
423
|
+
if (supportedMethods.indexOf(method) === -1) {
|
|
424
|
+
throw new Error(`${method} method is not supported!`)
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if ((method === 'GET' || method === 'HEAD') && schema && schema.body) {
|
|
428
|
+
throw new Error(`Body validation schema for ${method}:${path} route is not supported!`)
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
432
|
function validateBodyLimitOption (bodyLimit) {
|
|
433
433
|
if (bodyLimit === undefined) return
|
|
434
434
|
if (!Number.isInteger(bodyLimit) || bodyLimit <= 0) {
|
|
@@ -439,6 +439,7 @@ function validateBodyLimitOption (bodyLimit) {
|
|
|
439
439
|
function runPreParsing (err, request, reply) {
|
|
440
440
|
if (reply.sent === true) return
|
|
441
441
|
if (err != null) {
|
|
442
|
+
reply[kReplyIsError] = true
|
|
442
443
|
reply.send(err)
|
|
443
444
|
return
|
|
444
445
|
}
|
|
@@ -465,10 +466,6 @@ function preParsingHookRunner (functions, request, reply, cb) {
|
|
|
465
466
|
}
|
|
466
467
|
|
|
467
468
|
if (err || i === functions.length) {
|
|
468
|
-
if (err && !(err instanceof Error)) {
|
|
469
|
-
reply[kReplyIsError] = true
|
|
470
|
-
}
|
|
471
|
-
|
|
472
469
|
cb(err, request, reply)
|
|
473
470
|
return
|
|
474
471
|
}
|
|
@@ -476,11 +473,7 @@ function preParsingHookRunner (functions, request, reply, cb) {
|
|
|
476
473
|
const fn = functions[i++]
|
|
477
474
|
let result
|
|
478
475
|
try {
|
|
479
|
-
|
|
480
|
-
result = fn(request, reply, next)
|
|
481
|
-
} else {
|
|
482
|
-
result = fn(request, reply, request[kRequestPayloadStream], next)
|
|
483
|
-
}
|
|
476
|
+
result = fn(request, reply, request[kRequestPayloadStream], next)
|
|
484
477
|
} catch (error) {
|
|
485
478
|
next(error)
|
|
486
479
|
return
|
package/lib/schema-controller.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { buildSchemas } = require('./schemas')
|
|
4
|
-
const
|
|
4
|
+
const SerializerSelector = require('@fastify/fast-json-stringify-compiler')
|
|
5
5
|
const ValidatorSelector = require('@fastify/ajv-compiler')
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -17,7 +17,7 @@ function buildSchemaController (parentSchemaCtrl, opts) {
|
|
|
17
17
|
|
|
18
18
|
let compilersFactory = {
|
|
19
19
|
buildValidator: ValidatorSelector(),
|
|
20
|
-
buildSerializer:
|
|
20
|
+
buildSerializer: SerializerSelector()
|
|
21
21
|
}
|
|
22
22
|
if (opts && opts.compilersFactory) {
|
|
23
23
|
compilersFactory = Object.assign(compilersFactory, opts.compilersFactory)
|
package/lib/schemas.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const fastClone = require('rfdc')({ circles: false, proto: true })
|
|
4
|
-
const { kSchemaVisited } = require('./symbols')
|
|
4
|
+
const { kSchemaVisited, kSchemaResponse } = require('./symbols')
|
|
5
5
|
const kFluentSchema = Symbol.for('fluent-schema-object')
|
|
6
6
|
|
|
7
7
|
const {
|
|
@@ -121,7 +121,33 @@ function getSchemaAnyway (schema, jsonShorthand) {
|
|
|
121
121
|
return schema
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Search for the right JSON schema compiled function in the request context
|
|
126
|
+
* setup by the route configuration `schema.response`.
|
|
127
|
+
* It will look for the exact match (eg 200) or generic (eg 2xx)
|
|
128
|
+
*
|
|
129
|
+
* @param {object} context the request context
|
|
130
|
+
* @param {number} statusCode the http status code
|
|
131
|
+
* @returns {function|boolean} the right JSON Schema function to serialize
|
|
132
|
+
* the reply or false if it is not set
|
|
133
|
+
*/
|
|
134
|
+
function getSchemaSerializer (context, statusCode) {
|
|
135
|
+
const responseSchemaDef = context[kSchemaResponse]
|
|
136
|
+
if (!responseSchemaDef) {
|
|
137
|
+
return false
|
|
138
|
+
}
|
|
139
|
+
if (responseSchemaDef[statusCode]) {
|
|
140
|
+
return responseSchemaDef[statusCode]
|
|
141
|
+
}
|
|
142
|
+
const fallbackStatusCode = (statusCode + '')[0] + 'xx'
|
|
143
|
+
if (responseSchemaDef[fallbackStatusCode]) {
|
|
144
|
+
return responseSchemaDef[fallbackStatusCode]
|
|
145
|
+
}
|
|
146
|
+
return false
|
|
147
|
+
}
|
|
148
|
+
|
|
124
149
|
module.exports = {
|
|
125
150
|
buildSchemas (initStore) { return new Schemas(initStore) },
|
|
151
|
+
getSchemaSerializer,
|
|
126
152
|
normalizeSchema
|
|
127
153
|
}
|