重新混合:在每个请求的加载器之前将中间件模式转换为 运行 代码?
Remix: middleware pattern to run code before loader on every request?
Remix 中是否有针对每个请求的 运行 公共代码的推荐模式,并可能向请求添加上下文数据?比如中间件?例如,一个用例可能是进行日志记录或身份验证。
我看到的与此相似的一件事是 loader context 通过 getLoadContext
API。这使您可以填充一个 context
对象,该对象作为 arg 传递给所有路由加载器。
它确实有效,最初似乎是这样做的方法,但是 the docs 它说...
It's a way to bridge the gap between the adapter's request/response API with your Remix app
This API is an escape hatch, it’s uncommon to need it
...这让我不这么认为,因为
此 API 明确用于与服务器运行时的自定义集成。但中间件似乎不应该特定于服务器运行时 - 它们应该只是作为 Remix 功能的 'application' 级别的一部分。
运行 中间件是 web 框架中非常 常见的 模式!
那么,对于在每个加载程序之前运行的中间件,Remix 是否有更好的模式?
在 Remix 中没有办法 运行 在 加载器之前 编码。
正如您所发现的,存在加载器上下文,但它 运行 甚至在 remix 开始完成其工作之前就已经存在(例如,因此您不知道匹配了哪些路由模块)。
您还可以 运行 任意代码,然后再处理 JS 文件中的重新混合请求,您在该文件中使用您要部署到的平台的适配器(这取决于您使用的启动器。此文件不如果您选择混音服务器作为服务器,则不存在)
目前它应该适用于某些用例,但我同意这是目前 remix 中缺少的功能。
不用中间件,直接在加载器内部调用一个函数,这样也会更显式。如果您想尽早 return 来自那些“中间件”的响应,Remix 让您可以抛出响应对象。
例如,如果您想检查用户是否具有特定角色,您可以创建此函数:
async function verifyUserRole(request: Request, expectedRole: string) {
let user = await getAuthenticatedUser(request); // somehow get the user
if (user.role === expectedRole) return user;
throw json({ message: "Forbidden" }, { status: 403 });
}
并且在任何加载程序中都这样调用它:
let loader: LoaderFunction = async ({ request }) => {
let user = await verifyUserRole(request, "admin");
// code here will only run if user is an admin
// and you'll also get the user object at the same time
};
另一个例子可能是需要 HTTPS
function requireHTTPS(request: Request) {
let url = new URL(request.url);
if (url.protocol === "https:") return;
url.protocol = "https:";
throw redirect(url.toString());
}
let loader: LoaderFunction = async ({ request }) => {
await requireHTTPS(request);
// run your loader (or action) code here
};
里面app/root.tsx
export let loader: LoaderFunction = ({ request }) => {
const url = new URL(request.url);
const hostname = url.hostname;
const proto = request.headers.get("X-Forwarded-Proto") ?? url.protocol;
url.host =
request.headers.get("X-Forwarded-Host") ??
request.headers.get("host") ??
url.host;
url.protocol = "https:";
if (proto === "http" && hostname !== "localhost") {
return redirect(url.toString(), {
headers: {
"X-Forwarded-Proto": "https",
},
});
}
return {};
};
Remix 中是否有针对每个请求的 运行 公共代码的推荐模式,并可能向请求添加上下文数据?比如中间件?例如,一个用例可能是进行日志记录或身份验证。
我看到的与此相似的一件事是 loader context 通过 getLoadContext
API。这使您可以填充一个 context
对象,该对象作为 arg 传递给所有路由加载器。
它确实有效,最初似乎是这样做的方法,但是 the docs 它说...
It's a way to bridge the gap between the adapter's request/response API with your Remix app
This API is an escape hatch, it’s uncommon to need it
...这让我不这么认为,因为
此 API 明确用于与服务器运行时的自定义集成。但中间件似乎不应该特定于服务器运行时 - 它们应该只是作为 Remix 功能的 'application' 级别的一部分。
运行 中间件是 web 框架中非常 常见的 模式!
那么,对于在每个加载程序之前运行的中间件,Remix 是否有更好的模式?
在 Remix 中没有办法 运行 在 加载器之前 编码。
正如您所发现的,存在加载器上下文,但它 运行 甚至在 remix 开始完成其工作之前就已经存在(例如,因此您不知道匹配了哪些路由模块)。
您还可以 运行 任意代码,然后再处理 JS 文件中的重新混合请求,您在该文件中使用您要部署到的平台的适配器(这取决于您使用的启动器。此文件不如果您选择混音服务器作为服务器,则不存在)
目前它应该适用于某些用例,但我同意这是目前 remix 中缺少的功能。
不用中间件,直接在加载器内部调用一个函数,这样也会更显式。如果您想尽早 return 来自那些“中间件”的响应,Remix 让您可以抛出响应对象。
例如,如果您想检查用户是否具有特定角色,您可以创建此函数:
async function verifyUserRole(request: Request, expectedRole: string) {
let user = await getAuthenticatedUser(request); // somehow get the user
if (user.role === expectedRole) return user;
throw json({ message: "Forbidden" }, { status: 403 });
}
并且在任何加载程序中都这样调用它:
let loader: LoaderFunction = async ({ request }) => {
let user = await verifyUserRole(request, "admin");
// code here will only run if user is an admin
// and you'll also get the user object at the same time
};
另一个例子可能是需要 HTTPS
function requireHTTPS(request: Request) {
let url = new URL(request.url);
if (url.protocol === "https:") return;
url.protocol = "https:";
throw redirect(url.toString());
}
let loader: LoaderFunction = async ({ request }) => {
await requireHTTPS(request);
// run your loader (or action) code here
};
里面app/root.tsx
export let loader: LoaderFunction = ({ request }) => {
const url = new URL(request.url);
const hostname = url.hostname;
const proto = request.headers.get("X-Forwarded-Proto") ?? url.protocol;
url.host =
request.headers.get("X-Forwarded-Host") ??
request.headers.get("host") ??
url.host;
url.protocol = "https:";
if (proto === "http" && hostname !== "localhost") {
return redirect(url.toString(), {
headers: {
"X-Forwarded-Proto": "https",
},
});
}
return {};
};