![](/img/trans.png)
[英]Can i access Loader data in Action function in remix.run?
[英]Remix: middleware pattern to run code before loader on every request?
Remix 中是否有推荐的模式用于在每个请求上运行通用代码,并可能将上下文数据添加到请求中? 像中间件? 例如,一个用例可能是进行日志记录或身份验证。
我看到的一件事似乎与此类似,是通过getLoadContext
API加载程序上下文。 这使您可以填充context
object,该上下文作为 arg 传递给所有路由加载器。
它确实有效,最初似乎是这样做的方式,但它的文档说......
这是一种在适配器的请求/响应 API 与您的 Remix 应用程序之间架起桥梁的方法
这个 API 是一个逃生舱口,很少需要它
...这让我不这么认为,因为
此 API 明确用于与服务器运行时的自定义集成。 但似乎中间件不应该特定于服务器运行时——它们应该只是作为 Remix 功能的“应用程序”级别的一部分。
运行中间件是 web 框架中非常常见的模式!
那么,对于在每个加载器之前运行的中间件,Remix 是否有更好的模式?
您可以直接在加载器内部调用 function 而不是中间件,这也将更加明确。 如果您想尽早从那些“中间件”Remix 中返回响应,让您抛出响应 object。
例如,如果您想检查用户是否具有某个角色,您可以创建此 function:
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
};
Remix 内部无法在加载程序之前运行代码。
正如您所发现的,有 loader 上下文,但它甚至在 remix 开始执行其工作之前就运行(例如,您将不知道匹配哪些路由模块)。
您还可以在将请求重新混合到 JS 文件中之前运行任意代码,在该文件中您将适配器用于您要部署到的平台(这取决于您使用的启动器。如果您选择了重新混合,则此文件不存在服务器作为您的服务器)
现在它应该适用于某些用例,但我同意这是目前 remix 中缺少的功能。
在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 {};
};
这是我与 typescript 混音的中间件实现,效果很好
ctx.return(something)
=== useLoaderData()
import compose from '@utils/compose';
export default function Index() {
const ctx = useLoaderData();
return <div>{ctx.name}</div>;
}
type DefaultCtx = {
name: string;
} & Request;
export const loader =(...args)=>compose<DefaultCtx>(
async (ctx, next) => {
ctx.name = 'first';
await next();
},
async (ctx, next) => {
ctx.name = 'secnod';
await next();
},
async (ctx, next) => {
ctx.name = 'third';
ctx.return(ctx);
await next();
}
)(args);
compose 和 koa 一样;
这是compose的实现
type Next = () => Promise<void>;
type Context = {};
type Middle<T = {}> = (ctx: Context & T, next: Next) => void;
const compose = <T>(...middlewares: Middle<T>[]) => {
return middlewares.reverse().reduce(
(dispatch, middleware) => {
return async ctx =>
middleware(ctx, async () => dispatch(ctx, async () => {}));
},
async () => {}
);
};
export type Middleware<T = {}, P = unknown> = (
ctx: Context & T & { return: (param: P) => void },
next: Next
) => void;
const returnEarly: Middleware = async (ctx, next) => {
return new Promise<any>(async resolve => {
ctx.return = resolve;
await next();
});
};
const componseWithReturn = <T>(...middlewares: Middleware<T>[]) =>
compose(returnEarly, ...middlewares) as (ctx: T) => void;
export default componseWithReturn;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.