mirror of
https://github.com/honojs/hono.git
synced 2024-11-21 18:18:57 +01:00
refactor: body parser (#2045)
This commit is contained in:
parent
039cac90ea
commit
c02b83bccd
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -8,5 +8,6 @@
|
|||||||
],
|
],
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": "explicit"
|
"source.fixAll.eslint": "explicit"
|
||||||
}
|
},
|
||||||
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
}
|
}
|
@ -22,49 +22,80 @@ export type ParseBodyOptions = {
|
|||||||
all?: boolean
|
all?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const isArrayField = (value: unknown): value is (string | File)[] => {
|
|
||||||
return Array.isArray(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const parseBody = async <T extends BodyData = BodyData>(
|
export const parseBody = async <T extends BodyData = BodyData>(
|
||||||
request: HonoRequest | Request,
|
request: HonoRequest | Request,
|
||||||
options: ParseBodyOptions = {
|
options: ParseBodyOptions = { all: false }
|
||||||
all: false,
|
|
||||||
}
|
|
||||||
): Promise<T> => {
|
): Promise<T> => {
|
||||||
let body: BodyData = {}
|
|
||||||
const contentType = request.headers.get('Content-Type')
|
const contentType = request.headers.get('Content-Type')
|
||||||
|
|
||||||
if (
|
if (isFormDataContent(contentType)) {
|
||||||
contentType &&
|
return parseFormData<T>(request, options)
|
||||||
(contentType.startsWith('multipart/form-data') ||
|
|
||||||
contentType.startsWith('application/x-www-form-urlencoded'))
|
|
||||||
) {
|
|
||||||
const formData = await request.formData()
|
|
||||||
if (formData) {
|
|
||||||
const form: BodyData = {}
|
|
||||||
formData.forEach((value, key) => {
|
|
||||||
const shouldParseAllValues = options.all || key.slice(-2) === '[]'
|
|
||||||
|
|
||||||
if (!shouldParseAllValues) {
|
|
||||||
form[key] = value // override if same key
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (form[key] && isArrayField(form[key])) {
|
|
||||||
;(form[key] as (string | File)[]).push(value) // append if same key
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (form[key]) {
|
|
||||||
form[key] = [form[key] as string | File, value] // convert to array if multiple values
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
form[key] = value
|
|
||||||
})
|
|
||||||
body = form
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return body as T
|
|
||||||
|
return {} as T
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFormDataContent(contentType: string | null): boolean {
|
||||||
|
if (contentType === null) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
contentType.startsWith('multipart/form-data') ||
|
||||||
|
contentType.startsWith('application/x-www-form-urlencoded')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function parseFormData<T extends BodyData = BodyData>(
|
||||||
|
request: HonoRequest | Request,
|
||||||
|
options: ParseBodyOptions
|
||||||
|
): Promise<T> {
|
||||||
|
const formData = await (request as Request).formData()
|
||||||
|
|
||||||
|
if (formData) {
|
||||||
|
return convertFormDataToBodyData<T>(formData, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {} as T
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertFormDataToBodyData<T extends BodyData = BodyData>(
|
||||||
|
formData: FormData,
|
||||||
|
options: ParseBodyOptions
|
||||||
|
): T {
|
||||||
|
const form: BodyData = {}
|
||||||
|
|
||||||
|
formData.forEach((value, key) => {
|
||||||
|
const shouldParseAllValues = options.all || key.endsWith('[]')
|
||||||
|
|
||||||
|
if (!shouldParseAllValues) {
|
||||||
|
form[key] = value
|
||||||
|
} else {
|
||||||
|
handleParsingAllValues(form, key, value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return form as T
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleParsingAllValues = (form: BodyData, key: string, value: FormDataEntryValue): void => {
|
||||||
|
if (form[key] && isArrayField(form[key])) {
|
||||||
|
appendToExistingArray(form[key] as (string | File)[], value)
|
||||||
|
} else if (form[key]) {
|
||||||
|
convertToNewArray(form, key, value)
|
||||||
|
} else {
|
||||||
|
form[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isArrayField(field: unknown): field is (string | File)[] {
|
||||||
|
return Array.isArray(field)
|
||||||
|
}
|
||||||
|
|
||||||
|
const appendToExistingArray = (arr: (string | File)[], value: FormDataEntryValue): void => {
|
||||||
|
arr.push(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const convertToNewArray = (form: BodyData, key: string, value: FormDataEntryValue): void => {
|
||||||
|
form[key] = [form[key] as string | File, value]
|
||||||
}
|
}
|
||||||
|
@ -22,49 +22,80 @@ export type ParseBodyOptions = {
|
|||||||
all?: boolean
|
all?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const isArrayField = (value: unknown): value is (string | File)[] => {
|
|
||||||
return Array.isArray(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const parseBody = async <T extends BodyData = BodyData>(
|
export const parseBody = async <T extends BodyData = BodyData>(
|
||||||
request: HonoRequest | Request,
|
request: HonoRequest | Request,
|
||||||
options: ParseBodyOptions = {
|
options: ParseBodyOptions = { all: false }
|
||||||
all: false,
|
|
||||||
}
|
|
||||||
): Promise<T> => {
|
): Promise<T> => {
|
||||||
let body: BodyData = {}
|
|
||||||
const contentType = request.headers.get('Content-Type')
|
const contentType = request.headers.get('Content-Type')
|
||||||
|
|
||||||
if (
|
if (isFormDataContent(contentType)) {
|
||||||
contentType &&
|
return parseFormData<T>(request, options)
|
||||||
(contentType.startsWith('multipart/form-data') ||
|
|
||||||
contentType.startsWith('application/x-www-form-urlencoded'))
|
|
||||||
) {
|
|
||||||
const formData = await request.formData()
|
|
||||||
if (formData) {
|
|
||||||
const form: BodyData = {}
|
|
||||||
formData.forEach((value, key) => {
|
|
||||||
const shouldParseAllValues = options.all || key.slice(-2) === '[]'
|
|
||||||
|
|
||||||
if (!shouldParseAllValues) {
|
|
||||||
form[key] = value // override if same key
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (form[key] && isArrayField(form[key])) {
|
|
||||||
;(form[key] as (string | File)[]).push(value) // append if same key
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (form[key]) {
|
|
||||||
form[key] = [form[key] as string | File, value] // convert to array if multiple values
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
form[key] = value
|
|
||||||
})
|
|
||||||
body = form
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return body as T
|
|
||||||
|
return {} as T
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFormDataContent(contentType: string | null): boolean {
|
||||||
|
if (contentType === null) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
contentType.startsWith('multipart/form-data') ||
|
||||||
|
contentType.startsWith('application/x-www-form-urlencoded')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function parseFormData<T extends BodyData = BodyData>(
|
||||||
|
request: HonoRequest | Request,
|
||||||
|
options: ParseBodyOptions
|
||||||
|
): Promise<T> {
|
||||||
|
const formData = await (request as Request).formData()
|
||||||
|
|
||||||
|
if (formData) {
|
||||||
|
return convertFormDataToBodyData<T>(formData, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {} as T
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertFormDataToBodyData<T extends BodyData = BodyData>(
|
||||||
|
formData: FormData,
|
||||||
|
options: ParseBodyOptions
|
||||||
|
): T {
|
||||||
|
const form: BodyData = {}
|
||||||
|
|
||||||
|
formData.forEach((value, key) => {
|
||||||
|
const shouldParseAllValues = options.all || key.endsWith('[]')
|
||||||
|
|
||||||
|
if (!shouldParseAllValues) {
|
||||||
|
form[key] = value
|
||||||
|
} else {
|
||||||
|
handleParsingAllValues(form, key, value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return form as T
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleParsingAllValues = (form: BodyData, key: string, value: FormDataEntryValue): void => {
|
||||||
|
if (form[key] && isArrayField(form[key])) {
|
||||||
|
appendToExistingArray(form[key] as (string | File)[], value)
|
||||||
|
} else if (form[key]) {
|
||||||
|
convertToNewArray(form, key, value)
|
||||||
|
} else {
|
||||||
|
form[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isArrayField(field: unknown): field is (string | File)[] {
|
||||||
|
return Array.isArray(field)
|
||||||
|
}
|
||||||
|
|
||||||
|
const appendToExistingArray = (arr: (string | File)[], value: FormDataEntryValue): void => {
|
||||||
|
arr.push(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const convertToNewArray = (form: BodyData, key: string, value: FormDataEntryValue): void => {
|
||||||
|
form[key] = [form[key] as string | File, value]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user