Skip to content

Commit

Permalink
✨feat(main): implementa middleware customizado para log e tratamento …
Browse files Browse the repository at this point in the history
…de erro | Parte 36

- Implementa Classes Específicas para Erros HTTP
- Implementa e Configura o "Middleware" para "Log" de Erro
- Implementa e Configura o "Middleware" para Responder Erros
- Refatora o "Controller" Recuperar Categoria Por Id para Interceptar Erros e Usar Erros HTTP Apropriados
- Refatorando o "Middleware" para Verificação de "Content-Types"para Usar Classe que Representa Erro HTTP Relacionado
- Implementa e Configura o "Middleware" para Lidar com Rotas Inexistentes
  • Loading branch information
diegoarmandoo committed Dec 15, 2023
1 parent 1452933 commit 8325c8c
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 2 deletions.
6 changes: 6 additions & 0 deletions src/main/presentation/http/app.express.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import helmet from "helmet";
import compression from "compression";
import { customMorgan } from "./middlewares/custom-morgan.middleware";
import { logger } from "@shared/helpers/logger.winston";
import { errorLogger } from "./middlewares/error-logger.middleware";
import { errorResponder } from "./middlewares/error-responser.middleware";
import { invalidPath } from "./middlewares/invalid-path.middleware";

const createExpressApplication = async (): Promise<Application> => {
const app: Application = express();
Expand All @@ -25,6 +28,9 @@ const createExpressApplication = async (): Promise<Application> => {
app.use('/api/v1', apiv1Router);

//Middleware de Tratamento de Erros (Error Handling)
app.use(invalidPath);
app.use(errorLogger);
app.use(errorResponder);

return app;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { HttpErrors } from "@shared/presentation/http/http.error";
import { NextFunction, Request, Response } from "express";

const allowedContentTypes = ['application/json'];
Expand All @@ -7,7 +8,7 @@ const contentTypeMiddleware = (request: Request, response: Response, next: NextF
const contentType = request.headers['content-type'];

if (!contentType || !allowedContentTypes.includes(contentType)) {
return response.status(415).send('Tipo de Mídia Não Suportado');
next(new HttpErrors.UnsupportedMediaTypeError());
}

next();
Expand Down
20 changes: 20 additions & 0 deletions src/main/presentation/http/middlewares/error-logger.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { logger } from "@shared/helpers/logger.winston";
import { HttpError } from "@shared/presentation/http/http.error";
import { NextFunction, Request, Response } from "express";

const errorLoggerMiddleware = (error: HttpError, request: Request, response: Response, next: NextFunction) => {
let statusCode = error.statusCode || 500;

const logErro = JSON.stringify({
name: error.name,
statusCode: statusCode,
message: error.message,
stack: process.env.NODE_ENV === 'development' ? error.stack : {}
}, null, 2);

logger.error(logErro);

next(error);
}

export { errorLoggerMiddleware as errorLogger }
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { HttpError } from "@shared/presentation/http/http.error";
import { NextFunction, Request, Response } from "express";

const errorResponderMiddleware = (error: HttpError, request: Request, response: Response, next: NextFunction) => {
let statusCode = error.statusCode || 500;
response.status(statusCode).json({
name: error.name,
statusCode: statusCode,
message: error.message,
stack: process.env.NODE_ENV === 'development' ? error.stack : {}
});
}

export { errorResponderMiddleware as errorResponder }
10 changes: 10 additions & 0 deletions src/main/presentation/http/middlewares/invalid-path.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { HttpErrors } from "@shared/presentation/http/http.error";
import { NextFunction, Request, Response } from "express";

//Lança um erro 404 para caminhos indefinidos que vai ser tratado pelos middlewares de erros (log de erro e o responder de erro)
const invalidPathMiddleware = (request: Request, response: Response, next: NextFunction) => {
const error = new HttpErrors.NotFoundError();
next(error);
}

export { invalidPathMiddleware as invalidPath }
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MockProxy, mock, mockReset } from "vitest-mock-extended";
import { RecuperarCategoriaPorIdExpressController } from "./recuperar-categoria-por-id.express.controller";
import { ICategoria } from "@modules/catalogo/domain/categoria/categoria.types";
import { CategoriaApplicationExceptions } from "@modules/catalogo/application/exceptions/categoria.application.exception";
import { HttpError, HttpErrors } from "@shared/presentation/http/http.error";


let requestMock: MockProxy<Request>;
Expand Down Expand Up @@ -70,7 +71,7 @@ describe('Controller Express: Recuperar Categoria por ID', () => {

expect(recuperarCategoriaPorIdUseCaseMock.execute).toHaveBeenCalledWith(categoriaInputDTO.id);
expect(nextMock).toHaveBeenCalled();
expect(nextMock.mock.lastCall[0].name).toBe(CategoriaApplicationExceptions.CategoriaNaoEncontrada.name);
expect(nextMock.mock.lastCall[0].name).toBe(HttpErrors.NotFoundError.name);

});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { CategoriaApplicationExceptions } from "@modules/catalogo/application/exceptions/categoria.application.exception";
import { RecuperarCategoriaPorIdUseCase } from "@modules/catalogo/application/use-cases/recuperar-categoria-por-id/recuperar-categoria-por-id.use-case";
import { ICategoria } from "@modules/catalogo/domain/categoria/categoria.types";
import { ExpressController } from "@shared/presentation/http/express.controller";
import { HttpErrors } from "@shared/presentation/http/http.error";
import { NextFunction, Request, Response } from "express";

class RecuperarCategoriaPorIdExpressController extends ExpressController {
Expand All @@ -19,6 +21,9 @@ class RecuperarCategoriaPorIdExpressController extends ExpressController {
this.sendSuccessResponse(response,categoriaOutputDTO);
}
catch (error) {
if (error instanceof CategoriaApplicationExceptions.CategoriaNaoEncontrada){
error = new HttpErrors.NotFoundError({ message: error.message });
}
next(error);
}
}
Expand Down
36 changes: 36 additions & 0 deletions src/shared/presentation/http/http.error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class HttpError extends Error {
statusCode: number;

constructor(statusCode:number, message: string = '⚠️ Erro HTTP genérico') {
super(message);
this.name = 'HttpError';
this.statusCode = statusCode;
this.message = message;
Object.setPrototypeOf(this, HttpError.prototype);
Error.captureStackTrace(this, this.constructor);
}

}

class NotFoundError extends HttpError {
constructor( params?: {statusCode?: number, message?: string}) {
const { statusCode, message} = params || {};
super(statusCode || 404, message || '⚠️ Servidor Não Conseguiu Encontrar o Recurso Solicitado.');
this.name = 'NotFoundError';
}
}

class UnsupportedMediaTypeError extends HttpError {
constructor( params?: {statusCode?: number, message?: string}) {
const { statusCode, message} = params || {};
super(statusCode || 415, message || '⚠️ Servidor se Recusou a Aceitar a Requisição Porque o Formato do Payload Não é Um Formato Suportado.');
this.name = 'UnsupportedMediaTypeError';
}
}

const HttpErrors = {
NotFoundError: NotFoundError,
UnsupportedMediaTypeError: UnsupportedMediaTypeError
}

export { HttpError, HttpErrors }

0 comments on commit 8325c8c

Please sign in to comment.