forked from denoland/fresh
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmod.ts
More file actions
136 lines (130 loc) · 4.35 KB
/
mod.ts
File metadata and controls
136 lines (130 loc) · 4.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import { type Context, getInternals } from "../context.ts";
import type { App as _App } from "../app.ts";
import type { Define as _Define } from "../define.ts";
import { recordSpanError, tracer } from "../otel.ts";
/**
* A middleware function is the basic building block of Fresh. It allows you
* to respond to an incoming request in any way you want. You can redirect
* routes, serve files, create APIs and much more. Middlewares can be chained by
* calling {@linkcode Context.next|ctx.next()} inside of the function.
*
* Middlewares can be synchronous or asynchronous. If a middleware returns a
* {@linkcode Response} object, the response will be sent back to the client. If
* a middleware returns a `Promise<Response>`, Fresh will wait for the promise
* to resolve before sending the response.
*
* A {@linkcode Context} object is passed to the middleware function. This
* object contains the original request object, as well as any state related to
* the current request. The context object also contains methods to redirect
* the client to another URL, or to call the next middleware in the chain.
*
* Middlewares can be defined as a single function or an array of functions.
* When an array of middlewares is passed to
* {@linkcode _App.prototype.use|app.use}, Fresh will call each middleware in the
* order they are defined.
*
* Middlewares can also be defined using the
* {@linkcode _Define.middleware|define.middleware} method. This
* method is optional, but it can be useful for type checking and code
* completion. It does not register the middleware with the app.
*
* ## Examples
*
* ### Logging middleware
*
* This example shows how to create a simple middleware that logs incoming
* requests.
*
* ```ts
* // Define a middleware function that logs incoming requests. Using the
* // `define.middleware` method is optional, but it can be useful for type
* // checking and code completion. It does not register the middleware with the
* // app.
* const loggerMiddleware = define.middleware((ctx) => {
* console.log(`${ctx.request.method} ${ctx.request.url}`);
* // Call the next middleware
* return ctx.next();
* });
*
* // To register the middleware to the app, use `app.use`.
* app.use(loggerMiddleware)
* ```
*
* ### Redirect middleware
*
* This example shows how to create a middleware that redirects requests from
* one URL to another.
*
* ```ts
* // Any request to a URL that starts with "/legacy/" will be redirected to
* // "/modern".
* const redirectMiddleware = define.middleware((ctx) => {
* if (ctx.url.pathname.startsWith("/legacy/")) {
* return ctx.redirect("/modern");
* }
*
* // Otherwise call the next middleware
* return ctx.next();
* });
*
* // Again, register the middleware with the app.
* app.use(redirectMiddleware);
* ```
*/
export type Middleware<State> = (
ctx: Context<State>,
) => Response | Promise<Response>;
/**
* @deprecated Use {@linkcode Middleware} instead.
*/
export type MiddlewareFn<State> = Middleware<State>;
/**
* A lazy {@linkcode Middleware}
*/
export type MaybeLazyMiddleware<State> = (
ctx: Context<State>,
) => Response | Promise<Response | Middleware<State>>;
export async function runMiddlewares<State>(
middlewares: MaybeLazyMiddleware<State>[],
ctx: Context<State>,
): Promise<Response> {
return await tracer.startActiveSpan("middlewares", {
attributes: { "fresh.middleware.count": middlewares.length },
}, async (span) => {
try {
let fn = ctx.next;
let i = middlewares.length;
while (i--) {
const local = fn;
let next = middlewares[i];
const idx = i;
fn = async () => {
const internals = getInternals(ctx);
const { app: prevApp, layouts: prevLayouts } = internals;
ctx.next = local;
try {
const result = await next(ctx);
if (typeof result === "function") {
middlewares[idx] = result;
next = result;
return await result(ctx);
}
return result;
} catch (err) {
ctx.error = err;
throw err;
} finally {
internals.app = prevApp;
internals.layouts = prevLayouts;
}
};
}
return await fn();
} catch (err) {
recordSpanError(span, err);
throw err;
} finally {
span.end();
}
});
}