Nestjs

参考文档:docs.nestjs.cn

说起Nestjs的异常过滤器,不能不提.Net的全局过滤器Filter,功能那是相当的强悍,用理论话说叫AOP 面向切面编程,可谓方便了太多需要异常处理的场景。说回Nestjs的异常过滤器,实现类似的功能,采用相似的处理方式,只不过一个面向C#,一个面向Nodejs,很荣幸的我,在两个框架都找到了类似的东西。

面向切面编程AOP,是一种类似于编程规范的东东,同门师兄弟有叫面向接口编程、SOLID原则等等。

Nestjs内置了默认的全局异常过滤器,处理能够转换成Httpexception的异常。

如果是Httpexception或其子类异常,那么会返回该异常的JSON格式:

  1. {"exceptionCode":40005,"message":"自定义异常","path":"/"}

如果不是Httpexception或其子类异常,那么会返回:

  1. {"statusCode":500,"message":"Internal server error"}

由于Nestjs采用了内置的默认异常处理,因此不会出现由于出现未捕获的异常导致程序崩溃。

由于内置异常处理返回值格式无法调整,因此自定义异常就显得又为正常。自定义异常可以使返回异常信息自定义,且可以增加自定义异常编码,方便客户端人员根据异常编码进行不同的展示。

如何自定义异常?

不重复造轮子是程序员的自我约束,首先我们新建我们自己的异常基类:

  1. 1 import { HttpException } from "@nestjs/common";
  2. 2
  3. 3 /**
  4. 4 * 定义基础异常类
  5. 5 *
  6. 6 * @export
  7. 7 * @class BaseException
  8. 8 * @extends {HttpException}
  9. 9 */
  10. 10 export class BaseException extends HttpException {
  11. 11
  12. 12 /**
  13. 13 * Creates an instance of BaseException.
  14. 14 * @param {number} exceptionCode 自定义异常编号
  15. 15 * @param {string} errorMessage 提示信息
  16. 16 * @param {number} statusCode 状态码
  17. 17 * @memberof BaseException
  18. 18 */
  19. 19 constructor(public exceptionCode: number, public errorMessage: string, public statusCode: number) {
  20. 20 super({ exceptionCode: exceptionCode, errorMessage: errorMessage }, statusCode);
  21. 21 }
  22. 22
  23. 23 /**
  24. 24 * 获取自定义异常代码
  25. 25 *
  26. 26 * @return {*}
  27. 27 * @memberof BaseException
  28. 28 */
  29. 29 getExceptionCode(): number {
  30. 30 return this.exceptionCode;
  31. 31 }
  32. 32
  33. 33 getErrorMessage(): string {
  34. 34 return this.errorMessage;
  35. 35 }
  36. 36
  37. 37 getStatusCode(): number {
  38. 38 return this.statusCode;
  39. 39 }
  40. 40 }

然后我们新建一个未授权异常类型,其中增加了自定义异常代码:

  1. 1 import { HttpStatus } from "@nestjs/common";
  2. 2 import { BaseException } from "./base.exception";
  3. 3
  4. 4 export class UnCauhtException extends BaseException {
  5. 5 constructor() {
  6. 6 super(40000, "系统运行异常,请联系管理员!", HttpStatus.FORBIDDEN);
  7. 7 }
  8. 8 }

建立好了自定义异常,那么我们就需要处理未授权异常,首先新建自定义异常处理基类,请注意 此处我们使用的事Express

  1. 1 import { ArgumentsHost, ExceptionFilter, HttpException } from "@nestjs/common";
  2. 2 import { HttpArgumentsHost } from "@nestjs/common/interfaces";
  3. 3 import { BaseException } from "src/exceptions/base.exception";
  4. 4 import { Response, Request } from "express";
  5. 5
  6. 6 /**
  7. 7 * 异常基础类过滤器
  8. 8 *
  9. 9 * @export
  10. 10 * @class BaseExceptionFilter
  11. 11 * @implements {ExceptionFilter<BaseException>}
  12. 12 */
  13. 13 export abstract class BaseExceptionFilter implements ExceptionFilter<BaseException>
  14. 14 {
  15. 15 /**
  16. 16 * 异常类捕获
  17. 17 *
  18. 18 * @abstract
  19. 19 * @param {BaseException} exception
  20. 20 * @param {ArgumentsHost} host
  21. 21 * @memberof BaseExceptionFilter
  22. 22 */
  23. 23 abstract catch(exception: BaseException, host: ArgumentsHost);
  24. 24
  25. 25 /**
  26. 26 * 获取http请求上下文参数
  27. 27 *
  28. 28 * @protected
  29. 29 * @param {ArgumentsHost} host
  30. 30 * @return {*}
  31. 31 * @memberof BaseExceptionFilter
  32. 32 */
  33. 33 protected getHttpContext(host: ArgumentsHost) {
  34. 34 return host.switchToHttp();
  35. 35 }
  36. 36
  37. 37 /**
  38. 38 * 获取http 响应参数
  39. 39 *
  40. 40 * @protected
  41. 41 * @param {HttpArgumentsHost} httpContext
  42. 42 * @return {*}
  43. 43 * @memberof BaseExceptionFilter
  44. 44 */
  45. 45 protected getResponse(httpContext: HttpArgumentsHost): Response {
  46. 46 return httpContext.getResponse<Response>();
  47. 47 }
  48. 48
  49. 49 /**
  50. 50 * 获取http请求参数
  51. 51 *
  52. 52 * @protected
  53. 53 * @param {HttpArgumentsHost} httpContext
  54. 54 * @return {*}
  55. 55 * @memberof BaseExceptionFilter
  56. 56 */
  57. 57 protected getRequest(httpContext: HttpArgumentsHost): Request {
  58. 58 return httpContext.getRequest<Request>();
  59. 59 }
  60. 60
  61. 61 /**
  62. 62 * 写入异常信息到客户端
  63. 63 *
  64. 64 * @param {ArgumentsHost} host
  65. 65 * @param {BaseException} exception
  66. 66 * @memberof BaseExceptionFilter
  67. 67 */
  68. 68 protected writeToClient(host: ArgumentsHost, exception: BaseException) {
  69. 69 const ctx = this.getHttpContext(host);
  70. 70 if(exception instanceof BaseException){
  71. 71 this.getResponse(ctx).status(exception.statusCode).json({
  72. 72 exceptionCode: exception.getExceptionCode(),
  73. 73 message: exception.getErrorMessage(),
  74. 74 path: this.getRequest(ctx).url
  75. 75 });
  76. 76 }else {
  77. 77 const httpException=exception ;
  78. 78 this.getResponse(ctx).status(500).json({
  79. 79 message: "未处理的异常",
  80. 80 path: this.getRequest(ctx).url
  81. 81 });
  82. 82 }
  83. 83
  84. 84 }
  85. 85 }

新建未授权异常处理:

  1. 1 import { ArgumentsHost, Catch } from "@nestjs/common";
  2. 2 import { AuthException } from "src/exceptions/auth.exception";
  3. 3 import { BaseException } from "src/exceptions/base.exception";
  4. 4 import { BaseExceptionFilter } from "./base.exception.filter";
  5. 5
  6. 6 @Catch(AuthException)
  7. 7 export class AuthExceptionFilter extends BaseExceptionFilter
  8. 8 {
  9. 9 constructor(){
  10. 10 super();
  11. 11 console.log("授权异常构造函数初始化"+new Date().toISOString());
  12. 12 }
  13. 13 catch(exception: AuthException, host: ArgumentsHost) {
  14. 14 exception.exceptionCode=40002;
  15. 15 console.log("授权异常执行"+new Date().toISOString());
  16. 16 this.writeToClient(host,exception);
  17. 17 }
  18. 18
  19. 19 }

针对未授权异常处理类,进行几点说明:

  1. 增加了Catch注解,只捕获Authexception的异常,其他类型的异常此类不进行处理
  2. 继承自定义异常处理类Baseexceptionfilter

异常处理类可应用于method、controller、全局,甚至同一个Controller可以定义多个自定义异常类

  1. 1 import { Controller, ForbiddenException, Get, HttpException, HttpStatus, UseFilters } from '@nestjs/common';
  2. 2 import { AppService } from './app.service';
  3. 3 import { AuthException } from './exceptions/auth.exception';
  4. 4 import { BusinessException } from './exceptions/business.exception';
  5. 5 import { UnCauhtException } from './exceptions/uncauht.exception';
  6. 6 import { AuthExceptionFilter } from './filters/auth.exception.filter';
  7. 7 import { BusinessExceptionFilter } from './filters/business.exception.filter';
  8. 8
  9. 9
  10. 10 /**
  11. 11 * 带有单个路由的基本控制器示例ff
  12. 12 */
  13. 13 @UseFilters(AuthExceptionFilter,BusinessExceptionFilter)
  14. 14 @Controller()
  15. 15 export class AppController {
  16. 16 constructor(private readonly appService: AppService) {}
  17. 17
  18. 18 @Get()
  19. 19 getHello(): string {
  20. 20 //throw new Error("666");
  21. 21 throw new BusinessException("自定义异常",HttpStatus.OK);
  22. 22 throw new AuthException();
  23. 23 throw new HttpException("自定义异常",HttpStatus.FORBIDDEN);
  24. 24 return this.appService.getHello();
  25. 25 }
  26. 26
  27. 27 @Get("name")
  28. 28 getName():string
  29. 29 {
  30. 30 return "guozhiqi";
  31. 31 }
  32. 32 }

几点说明:

  1. 我们使用Usefilters注解进行异常过滤器的添加
  2. 我们在Appcontroller中定义了两种不同类型的自定义异常处理类
  3. 也就是我们Appcontroller中抛出的异常,只要是我们定义的这两种,那么都可以被正常处理。
  1. Usefitlers中我们自定义的异常处理类会初始化几次?
    答案:我们通过类型注册到Appcontroller的自定义异常类只会在程序初始化的时候初始化一次。也就是说程序启动之后,每个controller、每个method定义了哪些异常处理类都已经确定。
  2. 如果我们捕获到异常,但不进行任何处理,会发生什么?
    答案:如果我们的异常处理方法什么也不做,那么恭喜你,会成功的将浏览器请求hang死,因为异常未处理,那么浏览器会一直得不到响应。
  3. 多个异常之间的处理顺序如何?
    答案:如果多个异常处理均可以捕获到该异常,那么只有第一个有效,也就是说异常处理类和 中间件不同,异常处理类只能其中一个处理,而中间件需要都进行处理。
  4. Nestjs的@Usefilters 像谁?
    首先从JS角度来看,像Angular,如果往后端看,最像Spring。

 

Nestjs的异常处理并不复杂,复杂的是需要我们针对不同类型的异常进行处理,提取异常的共性。

版权声明:本文为jiagoushi原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/jiagoushi/p/14382569.html