diff --git a/api.ts b/api.ts index a8fe29102..c984934ab 100644 --- a/api.ts +++ b/api.ts @@ -1,8 +1,9 @@ import { compress as brotli } from 'https://deno.land/x/brotli@v0.1.4/mod.ts' +import { FormDataReader } from 'https://deno.land/x/oak@v6.3.2/multipart.ts' import { gzipEncode } from 'https://deno.land/x/wasm_gzip@v1.0.0/mod.ts' import log from './log.ts' import { ServerRequest } from './std.ts' -import type { APIRequest } from './types.ts' +import type { APIRequest, FormDataBody } from './types.ts' export class Request extends ServerRequest implements APIRequest { #pathname: string @@ -84,6 +85,51 @@ export class Request extends ServerRequest implements APIRequest { await this.send(JSON.stringify(data, replacer, space), 'application/json; charset=utf-8') } + async decodeBody(type: "text"): Promise + async decodeBody(type: "json"): Promise + async decodeBody(type: "form-data"): Promise + async decodeBody(type: string): Promise { + if (type === "text") { + try { + const buff: Uint8Array = await Deno.readAll(this.body); + const encoded = new TextDecoder("utf-8").decode(buff); + return encoded; + } catch (err) { + console.error("Failed to parse the request body.", err); + } + } + + if (type === "json") { + try { + const buff: Uint8Array = await Deno.readAll(this.body); + const encoded = new TextDecoder("utf-8").decode(buff); + const json = JSON.parse(encoded); + return json; + } catch (err) { + console.error("Failed to parse the request body.", err); + } + } + + if (type === "form-data") { + try { + const boundary = this.headers.get("content-type"); + + if (!boundary) throw new Error("Failed to get the content-type") + + const reader = new FormDataReader(boundary, this.body); + const { fields, files } = await reader.read({ maxSize: 1024 * 1024 * 10 }); + + return { + get: (key: string) => fields[key], + getFile: (key: string) => files?.find(i => i.name === key) + } + + } catch (err) { + console.error("Failed to parse the request form-data", err) + } + } + } + async send(data: string | Uint8Array | ArrayBuffer, contentType?: string): Promise { if (this.#resp.done) { log.warn('ServerRequest: repeat respond calls') diff --git a/types.ts b/types.ts index ce4289cbe..4a69c7d32 100644 --- a/types.ts +++ b/types.ts @@ -93,6 +93,10 @@ export interface APIRequest extends ServerRequest { send(data: string | Uint8Array | ArrayBuffer, contentType?: string): Promise /** `json` replies to the request with a json content */ json(data: any): Promise + /** `decodeBody` will return a string, a form-data or any json object */ + decodeBody(type: "text"): Promise + decodeBody(type: "json"): Promise + decodeBody(type: "form-data"): Promise } /** @@ -105,3 +109,22 @@ export interface RouterURL { readonly params: Record readonly query: URLSearchParams } + +/** + * The form data body + */ +export interface FormDataBody { + get(key: string): string + getFile(key: string): FormFile +} + +/** + * The form file data + */ +export interface FormFile { + name: string + content: Uint8Array + contentType: string + filename: string + originalName: string +} \ No newline at end of file