Use forms with or without client-side JavaScript--no code duplication required!
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

90 行
2.6 KiB

  1. import { IncomingMessage } from 'http';
  2. import { getFormValues } from '@theoryofnekomata/formxtra';
  3. import {
  4. ENCTYPE_APPLICATION_JSON,
  5. ENCTYPE_MULTIPART_FORM_DATA,
  6. ENCTYPE_X_WWW_FORM_URLENCODED,
  7. } from '../common';
  8. import { getBody, parseMultipartFormData } from './body';
  9. export type EncTypeSerializer = (data: unknown) => string;
  10. export type EncTypeSerializerMap = Record<string, EncTypeSerializer>;
  11. export type SerializerOptions = Parameters<typeof getFormValues>[1];
  12. export interface SerializeBodyParams {
  13. form: HTMLFormElement,
  14. encType: string,
  15. serializers?: EncTypeSerializerMap,
  16. options?: SerializerOptions,
  17. }
  18. export const DEFAULT_ENCTYPE_SERIALIZERS: EncTypeSerializerMap = {
  19. [ENCTYPE_APPLICATION_JSON]: (data: unknown) => JSON.stringify(data),
  20. };
  21. export const serializeBody = (params: SerializeBodyParams) => {
  22. const {
  23. form,
  24. encType,
  25. serializers = DEFAULT_ENCTYPE_SERIALIZERS,
  26. options,
  27. } = params;
  28. if (encType === ENCTYPE_X_WWW_FORM_URLENCODED) {
  29. return new URLSearchParams(form);
  30. }
  31. if (encType === ENCTYPE_MULTIPART_FORM_DATA) {
  32. // type error when provided a submitter element for some reason...
  33. const FormDataUnknown = FormData as unknown as {
  34. new(formElement?: HTMLElement, submitter?: HTMLElement): BodyInit;
  35. };
  36. return new FormDataUnknown(form, options?.submitter);
  37. }
  38. if (typeof serializers[encType] === 'function') {
  39. return serializers[encType](getFormValues(form, options));
  40. }
  41. throw new Error(`Unsupported encType: ${encType}`);
  42. };
  43. export type EncTypeDeserializer = (data: string) => unknown;
  44. export type EncTypeDeserializerMap = Record<string, EncTypeDeserializer>;
  45. export interface DeserializeBodyParams {
  46. req: IncomingMessage,
  47. deserializers?: EncTypeDeserializerMap,
  48. }
  49. export const DEFAULT_ENCTYPE_DESERIALIZERS: EncTypeDeserializerMap = {
  50. [ENCTYPE_APPLICATION_JSON]: (data: string) => JSON.parse(data) as Record<string, unknown>,
  51. };
  52. export const deserializeBody = async (params: DeserializeBodyParams) => {
  53. const { req, deserializers = DEFAULT_ENCTYPE_DESERIALIZERS } = params;
  54. const contentType = req.headers['content-type'] ?? 'application/octet-stream';
  55. if (contentType?.startsWith(`${ENCTYPE_MULTIPART_FORM_DATA};`)) {
  56. return parseMultipartFormData(req);
  57. }
  58. const encoding = (req.headers['content-encoding'] ?? 'utf-8') as BufferEncoding;
  59. const bodyRaw = await getBody(req);
  60. if (contentType === ENCTYPE_X_WWW_FORM_URLENCODED) {
  61. return Object.fromEntries(
  62. new URLSearchParams(bodyRaw.toString(encoding)).entries(),
  63. );
  64. }
  65. if (typeof deserializers[contentType] === 'function') {
  66. return deserializers[contentType](bodyRaw.toString(encoding));
  67. }
  68. return bodyRaw.toString('binary');
  69. };