您将如何使用 Ramda.js 重构此函数?
How would you refactor this function using Ramda.js?
我正在努力学习 Ramda.js,但总是很难想象在 Ramda 中事情会变得多么简单。如果你是 Ramda 或函数式编程专家,你会如何重构它?
const validateFirebaseIdToken = async (req, res, next) => {
console.log("Check if request is authorized with Firebase ID token");
if (
(!req.headers.authorization ||
!req.headers.authorization.startsWith("Bearer ")) &&
!(req.cookies && req.cookies.__session)
) {
console.error(
"No Firebase ID token was passed as a Bearer token in the Authorization header.",
"Make sure you authorize your request by providing the following HTTP header:",
"Authorization: Bearer <Firebase ID Token>",
'or by passing a "__session" cookie.'
);
res.status(403).send("Unauthorized");
return;
}
let idToken;
if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer ")
) {
console.log('Found "Authorization" header');
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split("Bearer ")[1];
} else if (req.cookies) {
console.log('Found "__session" cookie');
// Read the ID Token from cookie.
idToken = req.cookies.__session;
} else {
// No cookie
res.status(403).send("Unauthorized");
return;
}
try {
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
//console.log("ID Token correctly decoded", decodedIdToken);
req.user = decodedIdToken;
next();
return;
} catch (error) {
console.error("Error while verifying Firebase ID token:", error);
res.status(403).send("Unauthorized");
return;
}
};
由于我无法重现您的环境,因此我无法保证这是 100% 有效的代码,但我已验证大部分功能都按预期工作。
正在检查请求
两组条件语句可以合并在一起。检查请求的有效性并提取令牌可以表示如下:(我们稍后会处理未授权的情况)
const token = hasToken(req) ? getToken(req) : false;
hasToken
将检查请求 req
是否具有有效授权 header 或 __session
cookie:
const hasBearer = pipe(pathOr('', ['headers', 'authorization']), startsWith('Bearer'));
const hasSession = hasPath(['cookies', '__session']);
const hasToken = either(hasBearer, hasSession);
获取令牌
我们可以从 header 或 cookie 中获取令牌:
const getBearer = pipe(path(['headers', 'authorization']), replace('Bearer ', ''));
const getSession = path(['cookies', '__session']);
const getToken = either(getBearer, getSession);
生成 403“未经授权”响应
我们也可以为此创建一个函数。它可以很简单:
const unauthorized = res => res.status(403).send('Unauthorized');
但我们也可以将其分解为更小的可重用块:
const status = invoker(1, 'status');
const send = invoker(1, 'send');
const unauthorized = pipe(status(403), send('Unauthorized'));
因此,如果您想生成其他类型的响应,您可以这样做:
const notFound = pipe(status(404), send('Not Found'));
const serverError = pipe(status(500), send('Server Error'));
正在验证令牌的有效性
我们可以使用函数式方法 try/catch:
const verifyToken = async (admin, token) =>
tryCatch(() => admin.auth().verifyIdToken(token), F)();
收回 admin
的控制权
最初的 validateFirebaseIdToken
函数依赖于外部变量 admin
,恕我直言,这并不理想。最好将它作为参数传递。为此,我们可以使用 curry
:
const validateFirebaseIdTokenFunction = curry(async (admin, req, res, next) => {
// …
});
把所有东西放在一起
const {
curry,
either,
F,
hasPath,
invoker,
path,
pathOr,
pipe,
replace,
set,
startsWith,
tryCatch } = require('ramda');
const hasBearer = pipe(pathOr('', ['headers', 'authorization']), startsWith('Bearer'));
const getBearer = pipe(path(['headers', 'authorization']), replace('Bearer ', ''));
const hasSession = hasPath(['cookies', '__session']);
const getSession = path(['cookies', '__session']);
const hasToken = either(hasBearer, hasSession);
const getToken = either(getBearer, getSession);
const status = invoker(1, 'status');
const send = invoker(1, 'send');
const unauthorized = pipe(status(403), send('Unauthorized'));
const verifyToken = async (admin, token) =>
tryCatch(() => admin.auth().verifyIdToken(token), F)();
const validateFirebaseIdTokenFunction = curry(async (admin, req, res, next) => {
const token = hasToken(req) ? getToken(req) : false;
const tokenVerified = token ? await verifyToken(admin, token) : false;
return tokenVerified ? set('user', tokenVerified, req) && next() : unauthorized(res);
});
// This function now “waits” for the remaining arguments: `req`, `res` and `next`
const validateFirebaseIdToken = validateFirebaseIdTokenFunction(admin);
使用的 Ramda 函数列表
我正在努力学习 Ramda.js,但总是很难想象在 Ramda 中事情会变得多么简单。如果你是 Ramda 或函数式编程专家,你会如何重构它?
const validateFirebaseIdToken = async (req, res, next) => {
console.log("Check if request is authorized with Firebase ID token");
if (
(!req.headers.authorization ||
!req.headers.authorization.startsWith("Bearer ")) &&
!(req.cookies && req.cookies.__session)
) {
console.error(
"No Firebase ID token was passed as a Bearer token in the Authorization header.",
"Make sure you authorize your request by providing the following HTTP header:",
"Authorization: Bearer <Firebase ID Token>",
'or by passing a "__session" cookie.'
);
res.status(403).send("Unauthorized");
return;
}
let idToken;
if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer ")
) {
console.log('Found "Authorization" header');
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split("Bearer ")[1];
} else if (req.cookies) {
console.log('Found "__session" cookie');
// Read the ID Token from cookie.
idToken = req.cookies.__session;
} else {
// No cookie
res.status(403).send("Unauthorized");
return;
}
try {
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
//console.log("ID Token correctly decoded", decodedIdToken);
req.user = decodedIdToken;
next();
return;
} catch (error) {
console.error("Error while verifying Firebase ID token:", error);
res.status(403).send("Unauthorized");
return;
}
};
由于我无法重现您的环境,因此我无法保证这是 100% 有效的代码,但我已验证大部分功能都按预期工作。
正在检查请求
两组条件语句可以合并在一起。检查请求的有效性并提取令牌可以表示如下:(我们稍后会处理未授权的情况)
const token = hasToken(req) ? getToken(req) : false;
hasToken
将检查请求 req
是否具有有效授权 header 或 __session
cookie:
const hasBearer = pipe(pathOr('', ['headers', 'authorization']), startsWith('Bearer'));
const hasSession = hasPath(['cookies', '__session']);
const hasToken = either(hasBearer, hasSession);
获取令牌
我们可以从 header 或 cookie 中获取令牌:
const getBearer = pipe(path(['headers', 'authorization']), replace('Bearer ', ''));
const getSession = path(['cookies', '__session']);
const getToken = either(getBearer, getSession);
生成 403“未经授权”响应
我们也可以为此创建一个函数。它可以很简单:
const unauthorized = res => res.status(403).send('Unauthorized');
但我们也可以将其分解为更小的可重用块:
const status = invoker(1, 'status');
const send = invoker(1, 'send');
const unauthorized = pipe(status(403), send('Unauthorized'));
因此,如果您想生成其他类型的响应,您可以这样做:
const notFound = pipe(status(404), send('Not Found'));
const serverError = pipe(status(500), send('Server Error'));
正在验证令牌的有效性
我们可以使用函数式方法 try/catch:
const verifyToken = async (admin, token) =>
tryCatch(() => admin.auth().verifyIdToken(token), F)();
收回 admin
最初的 validateFirebaseIdToken
函数依赖于外部变量 admin
,恕我直言,这并不理想。最好将它作为参数传递。为此,我们可以使用 curry
:
const validateFirebaseIdTokenFunction = curry(async (admin, req, res, next) => {
// …
});
把所有东西放在一起
const {
curry,
either,
F,
hasPath,
invoker,
path,
pathOr,
pipe,
replace,
set,
startsWith,
tryCatch } = require('ramda');
const hasBearer = pipe(pathOr('', ['headers', 'authorization']), startsWith('Bearer'));
const getBearer = pipe(path(['headers', 'authorization']), replace('Bearer ', ''));
const hasSession = hasPath(['cookies', '__session']);
const getSession = path(['cookies', '__session']);
const hasToken = either(hasBearer, hasSession);
const getToken = either(getBearer, getSession);
const status = invoker(1, 'status');
const send = invoker(1, 'send');
const unauthorized = pipe(status(403), send('Unauthorized'));
const verifyToken = async (admin, token) =>
tryCatch(() => admin.auth().verifyIdToken(token), F)();
const validateFirebaseIdTokenFunction = curry(async (admin, req, res, next) => {
const token = hasToken(req) ? getToken(req) : false;
const tokenVerified = token ? await verifyToken(admin, token) : false;
return tokenVerified ? set('user', tokenVerified, req) && next() : unauthorized(res);
});
// This function now “waits” for the remaining arguments: `req`, `res` and `next`
const validateFirebaseIdToken = validateFirebaseIdTokenFunction(admin);
使用的 Ramda 函数列表