@web-ts-toolkit/express-response-handler 0.0.2 → 0.2.0
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/LICENSE +1 -2
- package/README.md +58 -4
- package/{create-express-response-handler.d.mts → create-handler.d.mts} +3 -2
- package/{create-express-response-handler.d.ts → create-handler.d.ts} +3 -2
- package/{create-express-response-handler.js → create-handler.js} +113 -71
- package/{create-express-response-handler.mjs → create-handler.mjs} +107 -64
- package/error-format.d.mts +5 -2
- package/error-format.d.ts +5 -2
- package/error-format.js +50 -20
- package/error-format.mjs +46 -18
- package/error-formats.d.mts +7 -0
- package/error-formats.d.ts +7 -0
- package/error-formats.js +32 -0
- package/error-formats.mjs +8 -0
- package/http-response.mjs +0 -1
- package/index.d.mts +8 -5
- package/index.d.ts +8 -5
- package/index.js +64 -3
- package/index.mjs +37 -10
- package/package.json +4 -3
- package/public-types.d.mts +9 -4
- package/public-types.d.ts +9 -4
- package/public-types.js +12 -0
- package/public-types.mjs +4 -0
- package/responses/csv.js +4 -5
- package/responses/csv.mjs +2 -4
- package/responses/index.mjs +0 -1
- package/responses/success.mjs +0 -1
- package/chunk-PQJP2ZCI.mjs +0 -8
package/LICENSE
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
Apache License
|
|
3
2
|
Version 2.0, January 2004
|
|
4
3
|
http://www.apache.org/licenses/
|
|
@@ -187,7 +186,7 @@
|
|
|
187
186
|
same "printed page" as the copyright notice for easier
|
|
188
187
|
identification within third-party archives.
|
|
189
188
|
|
|
190
|
-
Copyright
|
|
189
|
+
Copyright Junmin Ahn
|
|
191
190
|
|
|
192
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
192
|
you may not use this file except in compliance with the License.
|
package/README.md
CHANGED
|
@@ -243,9 +243,10 @@ If you want an AIP-193-inspired error envelope, create a handler instance with `
|
|
|
243
243
|
|
|
244
244
|
```ts
|
|
245
245
|
import apiHandler from '@web-ts-toolkit/express-response-handler';
|
|
246
|
+
import { ErrorFormats } from '@web-ts-toolkit/express-response-handler';
|
|
246
247
|
|
|
247
|
-
const structuredHandler = apiHandler.
|
|
248
|
-
errorFormat:
|
|
248
|
+
const structuredHandler = apiHandler.createHandler({
|
|
249
|
+
errorFormat: ErrorFormats.aip193,
|
|
249
250
|
errorDomain: 'api.example.com',
|
|
250
251
|
});
|
|
251
252
|
```
|
|
@@ -296,6 +297,59 @@ app.get(
|
|
|
296
297
|
);
|
|
297
298
|
```
|
|
298
299
|
|
|
300
|
+
If you want RFC 9457 problem details instead, create a handler instance with `errorFormat: 'rfc9457'`:
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
import apiHandler from '@web-ts-toolkit/express-response-handler';
|
|
304
|
+
import { ErrorFormats } from '@web-ts-toolkit/express-response-handler';
|
|
305
|
+
|
|
306
|
+
const problemHandler = apiHandler.createHandler({
|
|
307
|
+
errorFormat: ErrorFormats.rfc9457,
|
|
308
|
+
errorDomain: 'api.example.com',
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
That mode returns `application/problem+json` payloads in this shape:
|
|
313
|
+
|
|
314
|
+
```json
|
|
315
|
+
{
|
|
316
|
+
"type": "https://api.example.com/problems/invalid-project-id",
|
|
317
|
+
"title": "Invalid project id",
|
|
318
|
+
"status": 400,
|
|
319
|
+
"detail": "invalid project id",
|
|
320
|
+
"instance": "/problems/invalid-project-id/123",
|
|
321
|
+
"errors": [
|
|
322
|
+
{
|
|
323
|
+
"detail": "must be a valid project id",
|
|
324
|
+
"pointer": "#/id"
|
|
325
|
+
}
|
|
326
|
+
]
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
You can enrich HTTP errors with problem detail fields:
|
|
331
|
+
|
|
332
|
+
```ts
|
|
333
|
+
import { BadRequestError } from '@web-ts-toolkit/http-errors';
|
|
334
|
+
|
|
335
|
+
app.get(
|
|
336
|
+
'/projects/:id',
|
|
337
|
+
problemHandler.handleResponse(async () => {
|
|
338
|
+
throw new BadRequestError('invalid project id', {
|
|
339
|
+
type: 'https://api.example.com/problems/invalid-project-id',
|
|
340
|
+
title: 'Invalid project id',
|
|
341
|
+
instance: '/problems/invalid-project-id/123',
|
|
342
|
+
errors: [
|
|
343
|
+
{
|
|
344
|
+
detail: 'must be a valid project id',
|
|
345
|
+
pointer: '#/id',
|
|
346
|
+
},
|
|
347
|
+
],
|
|
348
|
+
});
|
|
349
|
+
}),
|
|
350
|
+
);
|
|
351
|
+
```
|
|
352
|
+
|
|
299
353
|
## Isolated Instances
|
|
300
354
|
|
|
301
355
|
The default export is a ready-to-use singleton. If you want separate hook configuration per router or module, create an isolated instance:
|
|
@@ -303,8 +357,8 @@ The default export is a ready-to-use singleton. If you want separate hook config
|
|
|
303
357
|
```ts
|
|
304
358
|
import apiHandler from '@web-ts-toolkit/express-response-handler';
|
|
305
359
|
|
|
306
|
-
const adminHandler = apiHandler.
|
|
307
|
-
const publicHandler = apiHandler.
|
|
360
|
+
const adminHandler = apiHandler.createHandler();
|
|
361
|
+
const publicHandler = apiHandler.createHandler();
|
|
308
362
|
|
|
309
363
|
adminHandler.preError = async function (err) {
|
|
310
364
|
console.error('admin route failed', err);
|
|
@@ -4,7 +4,8 @@ import '@web-ts-toolkit/http-errors';
|
|
|
4
4
|
import './responses/success.mjs';
|
|
5
5
|
import './responses/index.mjs';
|
|
6
6
|
import './responses/csv.mjs';
|
|
7
|
+
import './error-formats.mjs';
|
|
7
8
|
|
|
8
|
-
declare function
|
|
9
|
+
declare function createHandler(options?: ExpressResponseHandlerOptions): ExpressResponseHandler;
|
|
9
10
|
|
|
10
|
-
export {
|
|
11
|
+
export { createHandler };
|
|
@@ -4,7 +4,8 @@ import '@web-ts-toolkit/http-errors';
|
|
|
4
4
|
import './responses/success.js';
|
|
5
5
|
import './responses/index.js';
|
|
6
6
|
import './responses/csv.js';
|
|
7
|
+
import './error-formats.js';
|
|
7
8
|
|
|
8
|
-
declare function
|
|
9
|
+
declare function createHandler(options?: ExpressResponseHandlerOptions): ExpressResponseHandler;
|
|
9
10
|
|
|
10
|
-
export {
|
|
11
|
+
export { createHandler };
|
|
@@ -26,25 +26,59 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
mod
|
|
27
27
|
));
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
var
|
|
30
|
-
__export(
|
|
31
|
-
|
|
29
|
+
var create_handler_exports = {};
|
|
30
|
+
__export(create_handler_exports, {
|
|
31
|
+
createHandler: () => createHandler
|
|
32
32
|
});
|
|
33
|
-
module.exports = __toCommonJS(
|
|
33
|
+
module.exports = __toCommonJS(create_handler_exports);
|
|
34
34
|
var import_assert = __toESM(require("assert"));
|
|
35
|
+
var import_utils = require("@web-ts-toolkit/utils");
|
|
35
36
|
var import_csv = require("./responses/csv");
|
|
36
37
|
var import_responses = require("./responses");
|
|
37
38
|
var import_http_response = require("./http-response");
|
|
38
39
|
var import_error_format = require("./error-format");
|
|
39
|
-
|
|
40
|
-
const isPromise = (value) => Boolean(value) && isFunction(value.then);
|
|
40
|
+
var import_error_formats = require("./error-formats");
|
|
41
41
|
const promisify = (fn) => (value) => Promise.resolve().then(() => fn(value));
|
|
42
|
-
const { isArray } = Array;
|
|
43
42
|
const invokePostHook = (hook, value) => {
|
|
44
43
|
void hook(value).catch(() => void 0);
|
|
45
44
|
};
|
|
45
|
+
const RFC_9457_CONTENT_TYPE = "application/problem+json";
|
|
46
|
+
const shouldSkipResponse = (res, event) => res.headersSent || event.canceled;
|
|
47
|
+
const sendProblemJson = (res, statusCode, payload, contentType) => {
|
|
48
|
+
res.status(statusCode);
|
|
49
|
+
res.set("Content-Type", contentType);
|
|
50
|
+
res.send(payload);
|
|
51
|
+
};
|
|
52
|
+
const sendHttpErrorByFormat = {
|
|
53
|
+
[import_error_formats.ErrorFormats.simple]: (res, error) => {
|
|
54
|
+
const payload = { message: error.message ?? "" };
|
|
55
|
+
if (error.errors !== void 0) {
|
|
56
|
+
payload.errors = error.errors;
|
|
57
|
+
}
|
|
58
|
+
res.status(error.statusCode ?? 500).send(payload);
|
|
59
|
+
},
|
|
60
|
+
[import_error_formats.ErrorFormats.aip193]: (res, error, domain) => {
|
|
61
|
+
res.status(error.statusCode ?? 500).send((0, import_error_format.toStructuredHttpErrorPayload)(error, domain));
|
|
62
|
+
},
|
|
63
|
+
[import_error_formats.ErrorFormats.rfc9457]: (res, error, domain) => {
|
|
64
|
+
sendProblemJson(res, error.statusCode ?? 500, (0, import_error_format.toRfc9457HttpErrorPayload)(error, domain), RFC_9457_CONTENT_TYPE);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const sendGenericErrorByFormat = {
|
|
68
|
+
[import_error_formats.ErrorFormats.simple]: (res, result) => {
|
|
69
|
+
res.status(422).send((0, import_error_format.toSimpleErrorPayload)(result));
|
|
70
|
+
},
|
|
71
|
+
[import_error_formats.ErrorFormats.aip193]: (res, result, domain) => {
|
|
72
|
+
const payload = (0, import_error_format.toStructuredGenericErrorPayload)(result, domain);
|
|
73
|
+
res.status(payload.error.code).send(payload);
|
|
74
|
+
},
|
|
75
|
+
[import_error_formats.ErrorFormats.rfc9457]: (res, result) => {
|
|
76
|
+
const payload = (0, import_error_format.toRfc9457GenericErrorPayload)(result);
|
|
77
|
+
sendProblemJson(res, payload.status ?? 422, payload, RFC_9457_CONTENT_TYPE);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
46
80
|
const assertMiddleware = (fn) => {
|
|
47
|
-
import_assert.default.ok(isFunction(fn), "middleware handler must be a function");
|
|
81
|
+
import_assert.default.ok((0, import_utils.isFunction)(fn), "middleware handler must be a function");
|
|
48
82
|
};
|
|
49
83
|
const normalizeMiddlewareList = (fns) => {
|
|
50
84
|
import_assert.default.ok(fns.length > 0, "at least one middleware handler is required");
|
|
@@ -52,7 +86,7 @@ const normalizeMiddlewareList = (fns) => {
|
|
|
52
86
|
fns.forEach(assertMiddleware);
|
|
53
87
|
return fns;
|
|
54
88
|
}
|
|
55
|
-
if (isArray(fns[0])) {
|
|
89
|
+
if ((0, import_utils.isArray)(fns[0])) {
|
|
56
90
|
import_assert.default.ok(fns[0].length > 0, "at least one middleware handler is required");
|
|
57
91
|
fns[0].forEach(assertMiddleware);
|
|
58
92
|
return fns[0];
|
|
@@ -60,9 +94,10 @@ const normalizeMiddlewareList = (fns) => {
|
|
|
60
94
|
assertMiddleware(fns[0]);
|
|
61
95
|
return [fns[0]];
|
|
62
96
|
};
|
|
63
|
-
function
|
|
64
|
-
const errorFormat = options.errorFormat
|
|
65
|
-
const errorDomain = options.errorDomain
|
|
97
|
+
function createHandler(options = {}) {
|
|
98
|
+
const errorFormat = options.errorFormat ?? import_error_formats.ErrorFormats.simple;
|
|
99
|
+
const errorDomain = options.errorDomain ?? "express-response-handler";
|
|
100
|
+
const rfc9457ContentType = options.rfc9457ContentType ?? RFC_9457_CONTENT_TYPE;
|
|
66
101
|
let errorMessageProvider = import_error_format.defaultErrorMessageProvider;
|
|
67
102
|
let preJson = null;
|
|
68
103
|
let postJson = null;
|
|
@@ -72,8 +107,18 @@ function createExpressResponseHandler(options = {}) {
|
|
|
72
107
|
let postJsonHook = null;
|
|
73
108
|
let preErrorHook = null;
|
|
74
109
|
let postErrorHook = null;
|
|
110
|
+
const updateHook = (fn, name, setState, rebuild) => {
|
|
111
|
+
if (fn === null) {
|
|
112
|
+
setState(null, null);
|
|
113
|
+
rebuild();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
import_assert.default.ok((0, import_utils.isFunction)(fn), `${name} hook must be a function`);
|
|
117
|
+
setState(fn, promisify(fn));
|
|
118
|
+
rebuild();
|
|
119
|
+
};
|
|
75
120
|
const sendBaseJson = function(res, data, event) {
|
|
76
|
-
if (res
|
|
121
|
+
if (shouldSkipResponse(res, event)) {
|
|
77
122
|
return;
|
|
78
123
|
}
|
|
79
124
|
if (data instanceof import_responses.Response) {
|
|
@@ -87,29 +132,30 @@ function createExpressResponseHandler(options = {}) {
|
|
|
87
132
|
res.json(data);
|
|
88
133
|
};
|
|
89
134
|
const sendBaseError = function(res, err, event) {
|
|
90
|
-
if (res
|
|
135
|
+
if (shouldSkipResponse(res, event)) {
|
|
91
136
|
return;
|
|
92
137
|
}
|
|
93
138
|
const error = err;
|
|
94
139
|
if (error.statusCode) {
|
|
95
|
-
if (errorFormat ===
|
|
96
|
-
|
|
140
|
+
if (errorFormat === import_error_formats.ErrorFormats.rfc9457) {
|
|
141
|
+
sendProblemJson(
|
|
142
|
+
res,
|
|
143
|
+
error.statusCode ?? 500,
|
|
144
|
+
(0, import_error_format.toRfc9457HttpErrorPayload)(error, errorDomain),
|
|
145
|
+
rfc9457ContentType
|
|
146
|
+
);
|
|
97
147
|
return;
|
|
98
148
|
}
|
|
99
|
-
|
|
100
|
-
if (error.errors !== void 0) {
|
|
101
|
-
payload.errors = error.errors;
|
|
102
|
-
}
|
|
103
|
-
res.status(error.statusCode).send(payload);
|
|
149
|
+
sendHttpErrorByFormat[errorFormat](res, error, errorDomain);
|
|
104
150
|
return;
|
|
105
151
|
}
|
|
106
152
|
const result = errorMessageProvider(err);
|
|
107
|
-
if (errorFormat ===
|
|
108
|
-
const payload = (0, import_error_format.
|
|
109
|
-
res.status
|
|
153
|
+
if (errorFormat === import_error_formats.ErrorFormats.rfc9457) {
|
|
154
|
+
const payload = (0, import_error_format.toRfc9457GenericErrorPayload)(result);
|
|
155
|
+
sendProblemJson(res, payload.status ?? 422, payload, rfc9457ContentType);
|
|
110
156
|
return;
|
|
111
157
|
}
|
|
112
|
-
res
|
|
158
|
+
sendGenericErrorByFormat[errorFormat](res, result, errorDomain);
|
|
113
159
|
};
|
|
114
160
|
let sendJson = sendBaseJson;
|
|
115
161
|
let sendError = sendBaseError;
|
|
@@ -157,7 +203,7 @@ function createExpressResponseHandler(options = {}) {
|
|
|
157
203
|
};
|
|
158
204
|
};
|
|
159
205
|
const handlePromise = function(res, promise, event) {
|
|
160
|
-
promise.then((data) => {
|
|
206
|
+
Promise.resolve(promise).then((data) => {
|
|
161
207
|
if (event.nextError) {
|
|
162
208
|
sendError(res, event.nextError, event);
|
|
163
209
|
return;
|
|
@@ -166,14 +212,14 @@ function createExpressResponseHandler(options = {}) {
|
|
|
166
212
|
}).catch((err) => sendError(res, err, event));
|
|
167
213
|
};
|
|
168
214
|
const handleResult = function(res, result, event) {
|
|
169
|
-
if (res
|
|
215
|
+
if (shouldSkipResponse(res, event)) {
|
|
170
216
|
return;
|
|
171
217
|
}
|
|
172
218
|
if (event.nextError) {
|
|
173
219
|
sendError(res, event.nextError, event);
|
|
174
220
|
return;
|
|
175
221
|
}
|
|
176
|
-
if (isPromise(result)) {
|
|
222
|
+
if ((0, import_utils.isPromise)(result)) {
|
|
177
223
|
handlePromise(res, result, event);
|
|
178
224
|
return;
|
|
179
225
|
}
|
|
@@ -216,77 +262,73 @@ function createExpressResponseHandler(options = {}) {
|
|
|
216
262
|
handleResult,
|
|
217
263
|
handlePromise,
|
|
218
264
|
HttpResponse: import_http_response.HttpResponse,
|
|
219
|
-
|
|
265
|
+
createHandler,
|
|
220
266
|
get errorMessageProvider() {
|
|
221
267
|
return errorMessageProvider;
|
|
222
268
|
},
|
|
223
269
|
set errorMessageProvider(fn) {
|
|
224
|
-
import_assert.default.ok(isFunction(fn), "error message provider must be a function");
|
|
270
|
+
import_assert.default.ok((0, import_utils.isFunction)(fn), "error message provider must be a function");
|
|
225
271
|
errorMessageProvider = fn;
|
|
226
272
|
},
|
|
227
273
|
get preJson() {
|
|
228
274
|
return preJson;
|
|
229
275
|
},
|
|
230
276
|
set preJson(fn) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
rebuildSendJson();
|
|
277
|
+
updateHook(
|
|
278
|
+
fn,
|
|
279
|
+
"pre-json",
|
|
280
|
+
(syncHook, asyncHook) => {
|
|
281
|
+
preJson = syncHook;
|
|
282
|
+
preJsonHook = asyncHook;
|
|
283
|
+
},
|
|
284
|
+
rebuildSendJson
|
|
285
|
+
);
|
|
241
286
|
},
|
|
242
287
|
get postJson() {
|
|
243
288
|
return postJson;
|
|
244
289
|
},
|
|
245
290
|
set postJson(fn) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
rebuildSendJson();
|
|
291
|
+
updateHook(
|
|
292
|
+
fn,
|
|
293
|
+
"post-json",
|
|
294
|
+
(syncHook, asyncHook) => {
|
|
295
|
+
postJson = syncHook;
|
|
296
|
+
postJsonHook = asyncHook;
|
|
297
|
+
},
|
|
298
|
+
rebuildSendJson
|
|
299
|
+
);
|
|
256
300
|
},
|
|
257
301
|
get preError() {
|
|
258
302
|
return preError;
|
|
259
303
|
},
|
|
260
304
|
set preError(fn) {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
rebuildSendError();
|
|
305
|
+
updateHook(
|
|
306
|
+
fn,
|
|
307
|
+
"pre-error",
|
|
308
|
+
(syncHook, asyncHook) => {
|
|
309
|
+
preError = syncHook;
|
|
310
|
+
preErrorHook = asyncHook;
|
|
311
|
+
},
|
|
312
|
+
rebuildSendError
|
|
313
|
+
);
|
|
271
314
|
},
|
|
272
315
|
get postError() {
|
|
273
316
|
return postError;
|
|
274
317
|
},
|
|
275
318
|
set postError(fn) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
rebuildSendError();
|
|
319
|
+
updateHook(
|
|
320
|
+
fn,
|
|
321
|
+
"post-error",
|
|
322
|
+
(syncHook, asyncHook) => {
|
|
323
|
+
postError = syncHook;
|
|
324
|
+
postErrorHook = asyncHook;
|
|
325
|
+
},
|
|
326
|
+
rebuildSendError
|
|
327
|
+
);
|
|
286
328
|
}
|
|
287
329
|
};
|
|
288
330
|
}
|
|
289
331
|
// Annotate the CommonJS export names for ESM import in node:
|
|
290
332
|
0 && (module.exports = {
|
|
291
|
-
|
|
333
|
+
createHandler
|
|
292
334
|
});
|
|
@@ -1,21 +1,56 @@
|
|
|
1
|
-
import "./chunk-PQJP2ZCI.mjs";
|
|
2
1
|
import assert from "assert";
|
|
2
|
+
import { isArray, isFunction, isPromise } from "@web-ts-toolkit/utils";
|
|
3
3
|
import { CSVResponse } from "./responses/csv";
|
|
4
4
|
import { Response } from "./responses";
|
|
5
5
|
import { HttpResponse } from "./http-response";
|
|
6
6
|
import {
|
|
7
7
|
defaultErrorMessageProvider,
|
|
8
|
+
toRfc9457GenericErrorPayload,
|
|
9
|
+
toRfc9457HttpErrorPayload,
|
|
8
10
|
toSimpleErrorPayload,
|
|
9
11
|
toStructuredGenericErrorPayload,
|
|
10
12
|
toStructuredHttpErrorPayload
|
|
11
13
|
} from "./error-format";
|
|
12
|
-
|
|
13
|
-
const isPromise = (value) => Boolean(value) && isFunction(value.then);
|
|
14
|
+
import { ErrorFormats } from "./error-formats";
|
|
14
15
|
const promisify = (fn) => (value) => Promise.resolve().then(() => fn(value));
|
|
15
|
-
const { isArray } = Array;
|
|
16
16
|
const invokePostHook = (hook, value) => {
|
|
17
17
|
void hook(value).catch(() => void 0);
|
|
18
18
|
};
|
|
19
|
+
const RFC_9457_CONTENT_TYPE = "application/problem+json";
|
|
20
|
+
const shouldSkipResponse = (res, event) => res.headersSent || event.canceled;
|
|
21
|
+
const sendProblemJson = (res, statusCode, payload, contentType) => {
|
|
22
|
+
res.status(statusCode);
|
|
23
|
+
res.set("Content-Type", contentType);
|
|
24
|
+
res.send(payload);
|
|
25
|
+
};
|
|
26
|
+
const sendHttpErrorByFormat = {
|
|
27
|
+
[ErrorFormats.simple]: (res, error) => {
|
|
28
|
+
const payload = { message: error.message ?? "" };
|
|
29
|
+
if (error.errors !== void 0) {
|
|
30
|
+
payload.errors = error.errors;
|
|
31
|
+
}
|
|
32
|
+
res.status(error.statusCode ?? 500).send(payload);
|
|
33
|
+
},
|
|
34
|
+
[ErrorFormats.aip193]: (res, error, domain) => {
|
|
35
|
+
res.status(error.statusCode ?? 500).send(toStructuredHttpErrorPayload(error, domain));
|
|
36
|
+
},
|
|
37
|
+
[ErrorFormats.rfc9457]: (res, error, domain) => {
|
|
38
|
+
sendProblemJson(res, error.statusCode ?? 500, toRfc9457HttpErrorPayload(error, domain), RFC_9457_CONTENT_TYPE);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const sendGenericErrorByFormat = {
|
|
42
|
+
[ErrorFormats.simple]: (res, result) => {
|
|
43
|
+
res.status(422).send(toSimpleErrorPayload(result));
|
|
44
|
+
},
|
|
45
|
+
[ErrorFormats.aip193]: (res, result, domain) => {
|
|
46
|
+
const payload = toStructuredGenericErrorPayload(result, domain);
|
|
47
|
+
res.status(payload.error.code).send(payload);
|
|
48
|
+
},
|
|
49
|
+
[ErrorFormats.rfc9457]: (res, result) => {
|
|
50
|
+
const payload = toRfc9457GenericErrorPayload(result);
|
|
51
|
+
sendProblemJson(res, payload.status ?? 422, payload, RFC_9457_CONTENT_TYPE);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
19
54
|
const assertMiddleware = (fn) => {
|
|
20
55
|
assert.ok(isFunction(fn), "middleware handler must be a function");
|
|
21
56
|
};
|
|
@@ -33,9 +68,10 @@ const normalizeMiddlewareList = (fns) => {
|
|
|
33
68
|
assertMiddleware(fns[0]);
|
|
34
69
|
return [fns[0]];
|
|
35
70
|
};
|
|
36
|
-
function
|
|
37
|
-
const errorFormat = options.errorFormat
|
|
38
|
-
const errorDomain = options.errorDomain
|
|
71
|
+
function createHandler(options = {}) {
|
|
72
|
+
const errorFormat = options.errorFormat ?? ErrorFormats.simple;
|
|
73
|
+
const errorDomain = options.errorDomain ?? "express-response-handler";
|
|
74
|
+
const rfc9457ContentType = options.rfc9457ContentType ?? RFC_9457_CONTENT_TYPE;
|
|
39
75
|
let errorMessageProvider = defaultErrorMessageProvider;
|
|
40
76
|
let preJson = null;
|
|
41
77
|
let postJson = null;
|
|
@@ -45,8 +81,18 @@ function createExpressResponseHandler(options = {}) {
|
|
|
45
81
|
let postJsonHook = null;
|
|
46
82
|
let preErrorHook = null;
|
|
47
83
|
let postErrorHook = null;
|
|
84
|
+
const updateHook = (fn, name, setState, rebuild) => {
|
|
85
|
+
if (fn === null) {
|
|
86
|
+
setState(null, null);
|
|
87
|
+
rebuild();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
assert.ok(isFunction(fn), `${name} hook must be a function`);
|
|
91
|
+
setState(fn, promisify(fn));
|
|
92
|
+
rebuild();
|
|
93
|
+
};
|
|
48
94
|
const sendBaseJson = function(res, data, event) {
|
|
49
|
-
if (res
|
|
95
|
+
if (shouldSkipResponse(res, event)) {
|
|
50
96
|
return;
|
|
51
97
|
}
|
|
52
98
|
if (data instanceof Response) {
|
|
@@ -60,29 +106,30 @@ function createExpressResponseHandler(options = {}) {
|
|
|
60
106
|
res.json(data);
|
|
61
107
|
};
|
|
62
108
|
const sendBaseError = function(res, err, event) {
|
|
63
|
-
if (res
|
|
109
|
+
if (shouldSkipResponse(res, event)) {
|
|
64
110
|
return;
|
|
65
111
|
}
|
|
66
112
|
const error = err;
|
|
67
113
|
if (error.statusCode) {
|
|
68
|
-
if (errorFormat ===
|
|
69
|
-
|
|
114
|
+
if (errorFormat === ErrorFormats.rfc9457) {
|
|
115
|
+
sendProblemJson(
|
|
116
|
+
res,
|
|
117
|
+
error.statusCode ?? 500,
|
|
118
|
+
toRfc9457HttpErrorPayload(error, errorDomain),
|
|
119
|
+
rfc9457ContentType
|
|
120
|
+
);
|
|
70
121
|
return;
|
|
71
122
|
}
|
|
72
|
-
|
|
73
|
-
if (error.errors !== void 0) {
|
|
74
|
-
payload.errors = error.errors;
|
|
75
|
-
}
|
|
76
|
-
res.status(error.statusCode).send(payload);
|
|
123
|
+
sendHttpErrorByFormat[errorFormat](res, error, errorDomain);
|
|
77
124
|
return;
|
|
78
125
|
}
|
|
79
126
|
const result = errorMessageProvider(err);
|
|
80
|
-
if (errorFormat ===
|
|
81
|
-
const payload =
|
|
82
|
-
res.status
|
|
127
|
+
if (errorFormat === ErrorFormats.rfc9457) {
|
|
128
|
+
const payload = toRfc9457GenericErrorPayload(result);
|
|
129
|
+
sendProblemJson(res, payload.status ?? 422, payload, rfc9457ContentType);
|
|
83
130
|
return;
|
|
84
131
|
}
|
|
85
|
-
res
|
|
132
|
+
sendGenericErrorByFormat[errorFormat](res, result, errorDomain);
|
|
86
133
|
};
|
|
87
134
|
let sendJson = sendBaseJson;
|
|
88
135
|
let sendError = sendBaseError;
|
|
@@ -130,7 +177,7 @@ function createExpressResponseHandler(options = {}) {
|
|
|
130
177
|
};
|
|
131
178
|
};
|
|
132
179
|
const handlePromise = function(res, promise, event) {
|
|
133
|
-
promise.then((data) => {
|
|
180
|
+
Promise.resolve(promise).then((data) => {
|
|
134
181
|
if (event.nextError) {
|
|
135
182
|
sendError(res, event.nextError, event);
|
|
136
183
|
return;
|
|
@@ -139,7 +186,7 @@ function createExpressResponseHandler(options = {}) {
|
|
|
139
186
|
}).catch((err) => sendError(res, err, event));
|
|
140
187
|
};
|
|
141
188
|
const handleResult = function(res, result, event) {
|
|
142
|
-
if (res
|
|
189
|
+
if (shouldSkipResponse(res, event)) {
|
|
143
190
|
return;
|
|
144
191
|
}
|
|
145
192
|
if (event.nextError) {
|
|
@@ -189,7 +236,7 @@ function createExpressResponseHandler(options = {}) {
|
|
|
189
236
|
handleResult,
|
|
190
237
|
handlePromise,
|
|
191
238
|
HttpResponse,
|
|
192
|
-
|
|
239
|
+
createHandler,
|
|
193
240
|
get errorMessageProvider() {
|
|
194
241
|
return errorMessageProvider;
|
|
195
242
|
},
|
|
@@ -201,64 +248,60 @@ function createExpressResponseHandler(options = {}) {
|
|
|
201
248
|
return preJson;
|
|
202
249
|
},
|
|
203
250
|
set preJson(fn) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
rebuildSendJson();
|
|
251
|
+
updateHook(
|
|
252
|
+
fn,
|
|
253
|
+
"pre-json",
|
|
254
|
+
(syncHook, asyncHook) => {
|
|
255
|
+
preJson = syncHook;
|
|
256
|
+
preJsonHook = asyncHook;
|
|
257
|
+
},
|
|
258
|
+
rebuildSendJson
|
|
259
|
+
);
|
|
214
260
|
},
|
|
215
261
|
get postJson() {
|
|
216
262
|
return postJson;
|
|
217
263
|
},
|
|
218
264
|
set postJson(fn) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
rebuildSendJson();
|
|
265
|
+
updateHook(
|
|
266
|
+
fn,
|
|
267
|
+
"post-json",
|
|
268
|
+
(syncHook, asyncHook) => {
|
|
269
|
+
postJson = syncHook;
|
|
270
|
+
postJsonHook = asyncHook;
|
|
271
|
+
},
|
|
272
|
+
rebuildSendJson
|
|
273
|
+
);
|
|
229
274
|
},
|
|
230
275
|
get preError() {
|
|
231
276
|
return preError;
|
|
232
277
|
},
|
|
233
278
|
set preError(fn) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
rebuildSendError();
|
|
279
|
+
updateHook(
|
|
280
|
+
fn,
|
|
281
|
+
"pre-error",
|
|
282
|
+
(syncHook, asyncHook) => {
|
|
283
|
+
preError = syncHook;
|
|
284
|
+
preErrorHook = asyncHook;
|
|
285
|
+
},
|
|
286
|
+
rebuildSendError
|
|
287
|
+
);
|
|
244
288
|
},
|
|
245
289
|
get postError() {
|
|
246
290
|
return postError;
|
|
247
291
|
},
|
|
248
292
|
set postError(fn) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
rebuildSendError();
|
|
293
|
+
updateHook(
|
|
294
|
+
fn,
|
|
295
|
+
"post-error",
|
|
296
|
+
(syncHook, asyncHook) => {
|
|
297
|
+
postError = syncHook;
|
|
298
|
+
postErrorHook = asyncHook;
|
|
299
|
+
},
|
|
300
|
+
rebuildSendError
|
|
301
|
+
);
|
|
259
302
|
}
|
|
260
303
|
};
|
|
261
304
|
}
|
|
262
305
|
export {
|
|
263
|
-
|
|
306
|
+
createHandler
|
|
264
307
|
};
|