| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- // src/request.ts
- import { HTTPException } from "./http-exception.js";
- import { GET_MATCH_RESULT } from "./request/constants.js";
- import { parseBody } from "./utils/body.js";
- import { decodeURIComponent_, getQueryParam, getQueryParams, tryDecode } from "./utils/url.js";
- var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);
- var HonoRequest = class {
- /**
- * `.raw` can get the raw Request object.
- *
- * @see {@link https://hono.dev/docs/api/request#raw}
- *
- * @example
- * ```ts
- * // For Cloudflare Workers
- * app.post('/', async (c) => {
- * const metadata = c.req.raw.cf?.hostMetadata?
- * ...
- * })
- * ```
- */
- raw;
- #validatedData;
- // Short name of validatedData
- #matchResult;
- routeIndex = 0;
- /**
- * `.path` can get the pathname of the request.
- *
- * @see {@link https://hono.dev/docs/api/request#path}
- *
- * @example
- * ```ts
- * app.get('/about/me', (c) => {
- * const pathname = c.req.path // `/about/me`
- * })
- * ```
- */
- path;
- bodyCache = {};
- constructor(request, path = "/", matchResult = [[]]) {
- this.raw = request;
- this.path = path;
- this.#matchResult = matchResult;
- this.#validatedData = {};
- }
- param(key) {
- return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams();
- }
- #getDecodedParam(key) {
- const paramKey = this.#matchResult[0][this.routeIndex][1][key];
- const param = this.#getParamValue(paramKey);
- return param && /\%/.test(param) ? tryDecodeURIComponent(param) : param;
- }
- #getAllDecodedParams() {
- const decoded = {};
- const keys = Object.keys(this.#matchResult[0][this.routeIndex][1]);
- for (const key of keys) {
- const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);
- if (value !== void 0) {
- decoded[key] = /\%/.test(value) ? tryDecodeURIComponent(value) : value;
- }
- }
- return decoded;
- }
- #getParamValue(paramKey) {
- return this.#matchResult[1] ? this.#matchResult[1][paramKey] : paramKey;
- }
- query(key) {
- return getQueryParam(this.url, key);
- }
- queries(key) {
- return getQueryParams(this.url, key);
- }
- header(name) {
- if (name) {
- return this.raw.headers.get(name) ?? void 0;
- }
- const headerData = {};
- this.raw.headers.forEach((value, key) => {
- headerData[key] = value;
- });
- return headerData;
- }
- async parseBody(options) {
- return parseBody(this, options);
- }
- #cachedBody = (key) => {
- const { bodyCache, raw } = this;
- const cachedBody = bodyCache[key];
- if (cachedBody) {
- return cachedBody;
- }
- const anyCachedKey = Object.keys(bodyCache)[0];
- if (anyCachedKey) {
- return bodyCache[anyCachedKey].then((body) => {
- if (anyCachedKey === "json") {
- body = JSON.stringify(body);
- }
- return new Response(body)[key]();
- });
- }
- return bodyCache[key] = raw[key]();
- };
- /**
- * `.json()` can parse Request body of type `application/json`
- *
- * @see {@link https://hono.dev/docs/api/request#json}
- *
- * @example
- * ```ts
- * app.post('/entry', async (c) => {
- * const body = await c.req.json()
- * })
- * ```
- */
- json() {
- return this.#cachedBody("text").then((text) => JSON.parse(text));
- }
- /**
- * `.text()` can parse Request body of type `text/plain`
- *
- * @see {@link https://hono.dev/docs/api/request#text}
- *
- * @example
- * ```ts
- * app.post('/entry', async (c) => {
- * const body = await c.req.text()
- * })
- * ```
- */
- text() {
- return this.#cachedBody("text");
- }
- /**
- * `.arrayBuffer()` parse Request body as an `ArrayBuffer`
- *
- * @see {@link https://hono.dev/docs/api/request#arraybuffer}
- *
- * @example
- * ```ts
- * app.post('/entry', async (c) => {
- * const body = await c.req.arrayBuffer()
- * })
- * ```
- */
- arrayBuffer() {
- return this.#cachedBody("arrayBuffer");
- }
- /**
- * Parses the request body as a `Blob`.
- * @example
- * ```ts
- * app.post('/entry', async (c) => {
- * const body = await c.req.blob();
- * });
- * ```
- * @see https://hono.dev/docs/api/request#blob
- */
- blob() {
- return this.#cachedBody("blob");
- }
- /**
- * Parses the request body as `FormData`.
- * @example
- * ```ts
- * app.post('/entry', async (c) => {
- * const body = await c.req.formData();
- * });
- * ```
- * @see https://hono.dev/docs/api/request#formdata
- */
- formData() {
- return this.#cachedBody("formData");
- }
- /**
- * Adds validated data to the request.
- *
- * @param target - The target of the validation.
- * @param data - The validated data to add.
- */
- addValidatedData(target, data) {
- this.#validatedData[target] = data;
- }
- valid(target) {
- return this.#validatedData[target];
- }
- /**
- * `.url()` can get the request url strings.
- *
- * @see {@link https://hono.dev/docs/api/request#url}
- *
- * @example
- * ```ts
- * app.get('/about/me', (c) => {
- * const url = c.req.url // `http://localhost:8787/about/me`
- * ...
- * })
- * ```
- */
- get url() {
- return this.raw.url;
- }
- /**
- * `.method()` can get the method name of the request.
- *
- * @see {@link https://hono.dev/docs/api/request#method}
- *
- * @example
- * ```ts
- * app.get('/about/me', (c) => {
- * const method = c.req.method // `GET`
- * })
- * ```
- */
- get method() {
- return this.raw.method;
- }
- get [GET_MATCH_RESULT]() {
- return this.#matchResult;
- }
- /**
- * `.matchedRoutes()` can return a matched route in the handler
- *
- * @deprecated
- *
- * Use matchedRoutes helper defined in "hono/route" instead.
- *
- * @see {@link https://hono.dev/docs/api/request#matchedroutes}
- *
- * @example
- * ```ts
- * app.use('*', async function logger(c, next) {
- * await next()
- * c.req.matchedRoutes.forEach(({ handler, method, path }, i) => {
- * const name = handler.name || (handler.length < 2 ? '[handler]' : '[middleware]')
- * console.log(
- * method,
- * ' ',
- * path,
- * ' '.repeat(Math.max(10 - path.length, 0)),
- * name,
- * i === c.req.routeIndex ? '<- respond from here' : ''
- * )
- * })
- * })
- * ```
- */
- get matchedRoutes() {
- return this.#matchResult[0].map(([[, route]]) => route);
- }
- /**
- * `routePath()` can retrieve the path registered within the handler
- *
- * @deprecated
- *
- * Use routePath helper defined in "hono/route" instead.
- *
- * @see {@link https://hono.dev/docs/api/request#routepath}
- *
- * @example
- * ```ts
- * app.get('/posts/:id', (c) => {
- * return c.json({ path: c.req.routePath })
- * })
- * ```
- */
- get routePath() {
- return this.#matchResult[0].map(([[, route]]) => route)[this.routeIndex].path;
- }
- };
- var cloneRawRequest = async (req) => {
- if (!req.raw.bodyUsed) {
- return req.raw.clone();
- }
- const cacheKey = Object.keys(req.bodyCache)[0];
- if (!cacheKey) {
- throw new HTTPException(500, {
- message: "Cannot clone request: body was already consumed and not cached. Please use HonoRequest methods (e.g., req.json(), req.text()) instead of consuming req.raw directly."
- });
- }
- const requestInit = {
- body: await req[cacheKey](),
- cache: req.raw.cache,
- credentials: req.raw.credentials,
- headers: req.header(),
- integrity: req.raw.integrity,
- keepalive: req.raw.keepalive,
- method: req.method,
- mode: req.raw.mode,
- redirect: req.raw.redirect,
- referrer: req.raw.referrer,
- referrerPolicy: req.raw.referrerPolicy,
- signal: req.raw.signal
- };
- return new Request(req.url, requestInit);
- };
- export {
- HonoRequest,
- cloneRawRequest
- };
|