rsnext/packages/next/server/base-http/node.ts
Javi Velasco 8752464816
Allow reading request bodies in middlewares (#34294) (#34519)
This PR brings back @Schniz awesome contribution to bring in bodies to middleware. It was reverted to leave it out of the stable release and to have some time to test it out in canary before officially releasing it. This PR is simply a `cherry-pick` of his original work.

Closes: #30953 
Closes: https://github.com/vercel/next.js/pull/34490

Co-authored-by: Gal Schlezinger <2054772+Schniz@users.noreply.github.com>
2022-02-18 19:43:43 +00:00

123 lines
2.9 KiB
TypeScript

import type { ServerResponse, IncomingMessage } from 'http'
import type { Writable, Readable } from 'stream'
import { NextApiRequestCookies, SYMBOL_CLEARED_COOKIES } from '../api-utils'
import { parseBody } from '../api-utils/node'
import { NEXT_REQUEST_META, RequestMeta } from '../request-meta'
import { BaseNextRequest, BaseNextResponse } from './index'
type Req = IncomingMessage & {
[NEXT_REQUEST_META]?: RequestMeta
cookies?: NextApiRequestCookies
}
export class NodeNextRequest extends BaseNextRequest<Readable> {
public headers = this._req.headers;
[NEXT_REQUEST_META]: RequestMeta
get originalRequest() {
// Need to mimic these changes to the original req object for places where we use it:
// render.tsx, api/ssg requests
this._req[NEXT_REQUEST_META] = this[NEXT_REQUEST_META]
this._req.url = this.url
this._req.cookies = this.cookies
return this._req
}
set originalRequest(value: Req) {
this._req = value
}
constructor(private _req: Req) {
super(_req.method!.toUpperCase(), _req.url!, _req)
}
async parseBody(limit: string | number): Promise<any> {
return parseBody(this._req, limit)
}
}
export class NodeNextResponse extends BaseNextResponse<Writable> {
private textBody: string | undefined = undefined
public [SYMBOL_CLEARED_COOKIES]?: boolean
get originalResponse() {
if (SYMBOL_CLEARED_COOKIES in this) {
this._res[SYMBOL_CLEARED_COOKIES] = this[SYMBOL_CLEARED_COOKIES]
}
return this._res
}
constructor(
private _res: ServerResponse & { [SYMBOL_CLEARED_COOKIES]?: boolean }
) {
super(_res)
}
get sent() {
return this._res.finished || this._res.headersSent
}
get statusCode() {
return this._res.statusCode
}
set statusCode(value: number) {
this._res.statusCode = value
}
get statusMessage() {
return this._res.statusMessage
}
set statusMessage(value: string) {
this._res.statusMessage = value
}
setHeader(name: string, value: string | string[]): this {
this._res.setHeader(name, value)
return this
}
getHeaderValues(name: string): string[] | undefined {
const values = this._res.getHeader(name)
if (values === undefined) return undefined
return (Array.isArray(values) ? values : [values]).map((value) =>
value.toString()
)
}
hasHeader(name: string): boolean {
return this._res.hasHeader(name)
}
getHeader(name: string): string | undefined {
const values = this.getHeaderValues(name)
return Array.isArray(values) ? values.join(',') : undefined
}
appendHeader(name: string, value: string): this {
const currentValues = this.getHeaderValues(name) ?? []
if (!currentValues.includes(value)) {
this._res.setHeader(name, [...currentValues, value])
}
return this
}
body(value: string) {
this.textBody = value
return this
}
send() {
this._res.end(this.textBody)
}
}