我正在使用 MVC 模型创建 Express 后端,但我仍然不知道在哪里抛出错误。
我正在使用 express-async-errors 和 http-errors ,这样我就可以在任何地方
throw
出现错误,该错误将由我创建的中间件处理。
但我的问题是:
1 - 我
throw
完整的Error
在我的服务上如下:
export default class UserService {
public error(){
throw createError(404,"Error Message")
}
}
并且不要在我的
try/catch
上创建
Controller
块
2 - 我在我的服务上
throw
一个简单的Error
或消息,并让控制器处理它:
服务
export default class UserService {
public error(){
throw Error("Error Message")
}
}
控制器
export default class UserController {
public simpleMethod(){
try {
userService.error()
} catch (error:any) {
throw createError(404,error.message)
}
}
}
3 - 或者只是
return
一个 value
类似的对象,具有名为 error
的属性,并让控制器处理一切,如下所示:
服务
export default class UserService {
public error(){
return {error:"Error Message"}
}
}
控制器
export default class UserController {
public simpleMethod() {
const message = userService.error()
if (message.error) {
throw createError(404, message.error)
}
}
}
注意:我认为处理此问题的最佳方法是直接在服务上,因为您可以更具体地了解错误类型,但有些人建议在控制器上执行此操作,所以,正确的方法是什么处理这个?
这是我花了很多时间的事情。我尝试过很多不同的方法,这对我来说是最有效的。我正在分享一个通过电子邮件注册租户的示例,以便您可以根据您的用例/API 进行相应的规划。
一开始可能看起来很吓人,因为它需要很多步骤来设置,但最好的部分是你只需要做一次。之后,您只需将代码文件复制粘贴到不同的项目中,它们就会完美地工作,因为这些代码文件将独立工作。
{
"success": true,
"message": "",
"data": {...}
}
根据操作,success
将是true
或false
,message
将是解释响应/结果的正确消息,并且data
将包含与响应相关的任何类型的相关数据负载(如果需要)。
// put below code in a file named as response-handler.js
const { logError, logInfo } = require('../logger');
const { HttpError } = require('./error');
function sendSuccess(code, success, message, data) {
return {
code,
success,
message,
data
}
}
function sendError(code = 500, message = "something went wrong!") {
throw new HttpError(code, false, message);
}
function handleResponse(result, req, res, next) {
const { code, success, message, data } = result;
if (code) {
logInfo(`responded with ${code}: ${message}`)
return res
.status(code)
.json({
success,
message,
data
})
} else {
logError(message);
return res.status(500).json({
success: false,
message: 'something went wrong'
})
}
}
module.exports = {
handleResponse,
sendSuccess,
sendError
};
因此,在我的代码库中,在控制器中,我将使用
sendSuccess
和 sendError
返回成功或错误对象,最终将由 handleResponse
处理以发送正确的响应。
Error
只能处理消息。但我们需要错误/状态代码、成功标志和消息才能正确发送响应。这样// put the below code in error.js
class HttpError extends Error {
constructor(code, success, message) {
super(message);
this.code = code;
this.success = success;
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = {
HttpError
}
第 2 点中的
sendError
最终将使用 HttpError
来抛出错误。
handleResponse
中间件。const authRoutes = require('./auth/route/auth.route');
const tenantRoutes = require('./tenant/route/tenant.route');
const expenseRoutes = require('./expense/route/expense.route');
const categoryRoutes = require('./category/route/category.route');
const { handleResponse } = require('./core/http/response-handler');
app.get("/ping", (req, res) => {
res.status(200).json({
success: true,
message: 'hello from euro!'
});
})
app.use(authRoutes);
app.use(tenantRoutes);
app.use(expenseRoutes);
app.use(categoryRoutes);
app.use(handleResponse); // <- handleResponse must be the last middleware, this is important, so that it can handle all the routes defined above it
// put below code in the route tenant.route.js
const router = require('express').Router();
const API_ENDPOINTS = require('../../support/request-mapper');
const { registerUserViaEmail } = require('../controller/tenant.controller');
router
.post(
API_ENDPOINTS.REGISTER_VIA_EMAIL,
[],
(req, res, next) => registerUserViaEmail(req,res).then(next).catch(next)
)
module.exports = router;
registerUserViaEmail(req,res).then(next).catch(next)
是这个设置的关键。简单来说,无论 then
内部发生什么好事(catch
)或坏事(registerUserViaEmail
),都让 handleResponse
来处理。一旦我向您展示控制器代码,您就会明白这一点。
//put the below code in tenant.controller.js
const { passwordEncoder } = require('../../core/crypto/password');
const { sendSuccess, sendError } = require('../../core/http/response-handler');
const { logError } = require('../../core/logger');
const Tenant = require('../model/tenant');
async function registerUserViaEmail(req, res) {
const tenant = await Tenant.findOne({ email: req.body.email }).exec();
if (tenant) {
sendError(409, "user already exists!");
}
try {
await Tenant.create({
name: req.body.name,
email: req.body.email,
password: await passwordEncoder(req.body.password),
provider: 'BASIC'
})
} catch (error) {
logError(error.message, error);
sendError();
}
return sendSuccess(201, true, "user registered successfully!");
}
module.exports = {
registerUserViaEmail
}
如您所见,我能够处理自定义/已处理错误(
401
重复用户错误)、未处理错误(catch
块错误)和成功响应。
因此,一旦设置了基本文件,只有路由和控制器会因项目而异。
希望这能帮助您思考最适合您的框架。