Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,24 @@ describe('Body methods with caching', () => {
expect(await req.text()).toEqual(text)
})

test('req.raw.json() should work after req.json() is called', async () => {
const data = { foo: 'bar' }
const req = new HonoRequest(
new Request('http://localhost', {
method: 'POST',
body: JSON.stringify(data),
})
)

// First call to req.json() - consumes the body
const firstResult = await req.json()
expect(firstResult).toEqual(data)

// Second call to req.raw.json() - should work with cached body
const secondResult = await req.raw.json()
expect(secondResult).toEqual(data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also verify that it still works as expected when try to get the body for the third time?

To switch it up a bit we can also use .text which would still result in getting the cached body and constructing a new Request object

})

test('req.arrayBuffer()', async () => {
const buffer = new TextEncoder().encode('{"foo":"bar"}').buffer
const req = new HonoRequest(
Expand Down
53 changes: 37 additions & 16 deletions src/request.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { GET_MATCH_RESULT } from './request/constants'
import type { Result } from './router'
import type {
Expand Down Expand Up @@ -28,6 +27,7 @@ type BodyCache = Partial<Body & { parsedBody: BodyData }>
const tryDecodeURIComponent = (str: string) => tryDecode(str, decodeURIComponent_)

export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
private _raw: Request
/**
* `.raw` can get the raw Request object.
*
Expand All @@ -42,8 +42,7 @@ export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
* })
* ```
*/
raw: Request

raw!: Request
#validatedData: { [K in keyof ValidationTargets]?: {} } // Short name of validatedData
#matchResult: Result<[unknown, RouterRoute]>
routeIndex: number = 0
Expand All @@ -67,10 +66,30 @@ export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
path: string = '/',
matchResult: Result<[unknown, RouterRoute]> = [[]]
) {
this.raw = request
this._raw = request
this.path = path
this.#matchResult = matchResult
this.#validatedData = {}
/* eslint-disable @typescript-eslint/no-this-alias */
const self = this
Object.defineProperty(this, 'raw', {
get() {
if (self._raw.bodyUsed) {
const body = self.#getAnyCachedBody()
if (body) {
return new Request(self._raw, {
...self._raw,
body,
})
}
}
return self._raw
},
set(req: Request) {
self._raw = req
},
enumerable: true,
})
}

/**
Expand Down Expand Up @@ -209,25 +228,27 @@ export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
return (this.bodyCache.parsedBody ??= await parseBody(this, options))
}

#cachedBody = (key: keyof Body) => {
#getAnyCachedBody = () => {
const anyCachedKey = Object.keys(this.bodyCache)[0]
if (anyCachedKey) {
let body = this.bodyCache[anyCachedKey as keyof Body]
if (anyCachedKey === 'json') {
body = JSON.stringify(body)
}
return body
}
}

#cachedBody = async (key: keyof Body) => {
const { bodyCache, raw } = this
const cachedBody = bodyCache[key]

if (cachedBody) {
return cachedBody
}

const anyCachedKey = Object.keys(bodyCache)[0]
if (anyCachedKey) {
return (bodyCache[anyCachedKey as keyof Body] as Promise<BodyInit>).then((body) => {
if (anyCachedKey === 'json') {
body = JSON.stringify(body)
}
return new Response(body)[key]()
})
}

return (bodyCache[key] = raw[key]())
const body = this.#getAnyCachedBody()
return body ? new Response(body)[key]() : (bodyCache[key] = await raw[key]())
}

/**
Expand Down
Loading