spooder 4.2.16 → 4.2.18
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/README.md +55 -0
- package/package.json +1 -1
- package/src/api.ts +22 -0
package/README.md
CHANGED
|
@@ -47,6 +47,8 @@ The `CLI` component of `spooder` is a global command-line tool for running serve
|
|
|
47
47
|
- [`server.default(handler: DefaultHandler)`](#api-routing-server-default)
|
|
48
48
|
- [`server.error(handler: ErrorHandler)`](#api-routing-server-error)
|
|
49
49
|
- [`server.on_slow_request(callback: SlowRequestCallback, threshold: number)`](#api-routing-server-on-slow-request)
|
|
50
|
+
- [API > Routing > Validation](#api-routing-validation)
|
|
51
|
+
- [`validate_req_json(handler: JSONRequestHandler)`](#api-routing-validate-req-json)
|
|
50
52
|
- [API > Routing > Directory Serving](#api-routing-directory-serving)
|
|
51
53
|
- [`server.dir(path: string, dir: string, handler?: DirHandler, method: HTTP_METHODS)`](#api-routing-server-dir)
|
|
52
54
|
- [API > Routing > Server-Sent Events](#api-routing-server-sent-events)
|
|
@@ -686,6 +688,59 @@ server.on_slow_request(async (req, time, url) => {
|
|
|
686
688
|
> [!NOTE]
|
|
687
689
|
> The callback is not awaited internally, so you can use `async/await` freely without blocking the server/request.
|
|
688
690
|
|
|
691
|
+
<a id="api-routing-validation"></a>
|
|
692
|
+
## API > Routing > Validation
|
|
693
|
+
|
|
694
|
+
<a id="api-routing-validate-req-json"></a>
|
|
695
|
+
### 🔧 `validate_req_json(handler: JSONRequestHandler)`
|
|
696
|
+
|
|
697
|
+
In the scenario that you're expecting an endpoint to receive JSON data, you might set up a handler like this:
|
|
698
|
+
|
|
699
|
+
```ts
|
|
700
|
+
server.route('/api/endpoint', async (req, url) => {
|
|
701
|
+
const json = await req.json();
|
|
702
|
+
// do something with json.
|
|
703
|
+
return 200;
|
|
704
|
+
})
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
The problem with this is that if the request body is not valid JSON, the server will throw an error (potentially triggering canary reports) and return a `500` response.
|
|
708
|
+
|
|
709
|
+
What should instead happen is something like this:
|
|
710
|
+
|
|
711
|
+
```ts
|
|
712
|
+
server.route('/api/endpoint', async (req, url) => {
|
|
713
|
+
// check content-type header
|
|
714
|
+
if (req.headers.get('Content-Type') !== 'application/json')
|
|
715
|
+
return 400;
|
|
716
|
+
|
|
717
|
+
try {
|
|
718
|
+
const json = await req.json();
|
|
719
|
+
if (json === null || typeof json !== 'object' || Array.isArray(json))
|
|
720
|
+
return 400;
|
|
721
|
+
|
|
722
|
+
// do something with json.
|
|
723
|
+
return 200;
|
|
724
|
+
} catch (err) {
|
|
725
|
+
return 400;
|
|
726
|
+
}
|
|
727
|
+
})
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
As you can see this is quite verbose and adds a lot of boilerplate to your handlers. `validate_req_json` can be used to simplify this.
|
|
731
|
+
|
|
732
|
+
```ts
|
|
733
|
+
server.route('/api/endpoint', validate_req_json(async (json, req, url) => {
|
|
734
|
+
// do something with json.
|
|
735
|
+
return 200;
|
|
736
|
+
}));
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
This behaves the same as the code above, where a `400` status code is returned if the `Content-Type` header is not `application/json` or if the request body is not valid JSON, and no error is thrown.
|
|
740
|
+
|
|
741
|
+
> [!NOTE]
|
|
742
|
+
> While arrays and other primitives are valid JSON, `validate_req_json` will only pass objects to the handler, since they are the most common use case for JSON request bodies and it removes the need to validate that in the handler. If you need to use arrays or other primitives, either box them in an object or provide your own validation.
|
|
743
|
+
|
|
689
744
|
<a id="api-routing-directory-serving"></a>
|
|
690
745
|
## API > Routing > Directory Serving
|
|
691
746
|
|
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -398,6 +398,8 @@ type ErrorHandler = (err: Error, req: Request, url: URL) => Resolvable<Response>
|
|
|
398
398
|
type DefaultHandler = (req: Request, status_code: number) => HandlerReturnType;
|
|
399
399
|
type StatusCodeHandler = (req: Request) => HandlerReturnType;
|
|
400
400
|
|
|
401
|
+
type JSONRequestHandler = (req: Request, url: URL, json: JsonObject) => HandlerReturnType;
|
|
402
|
+
|
|
401
403
|
type ServerSentEventClient = {
|
|
402
404
|
message: (message: string) => void;
|
|
403
405
|
event: (event_name: string, message: string) => void;
|
|
@@ -442,6 +444,26 @@ function route_directory(route_path: string, dir: string, handler: DirHandler):
|
|
|
442
444
|
};
|
|
443
445
|
}
|
|
444
446
|
|
|
447
|
+
export function validate_req_json(JSONRequestHandler: JSONRequestHandler): RequestHandler {
|
|
448
|
+
return async (req: Request, url: URL) => {
|
|
449
|
+
try {
|
|
450
|
+
// validate content type header
|
|
451
|
+
if (req.headers.get('Content-Type') !== 'application/json')
|
|
452
|
+
return 400; // Bad Request
|
|
453
|
+
|
|
454
|
+
const json = await req.json();
|
|
455
|
+
|
|
456
|
+
// validate json is a plain object
|
|
457
|
+
if (json === null || typeof json !== 'object' || Array.isArray(json))
|
|
458
|
+
return 400; // Bad Request
|
|
459
|
+
|
|
460
|
+
return JSONRequestHandler(req, url, json);
|
|
461
|
+
} catch (e) {
|
|
462
|
+
return 400; // Bad Request
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
445
467
|
function format_query_parameters(search_params: URLSearchParams): string {
|
|
446
468
|
let result_parts = [];
|
|
447
469
|
|