HTTP、脚本化HTTP 与 nestjs 异常处理

发表于:2020-9-27 09:54

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:逍遥_乔治    来源:掘金

#
HTTPS
  HTTP 简易介绍
  http 对于一个前端是软实力,其实如果你想往后端靠近,扎实的 HTTP 基础会很好的帮助你。
  特点简介
  消息结构
  请求方法
  响应状态
  状态码
  content-type 内容类型
  全名
  HyperText Transfer Protocol
  超文本传输协议
  基于
  TCP/IP
  传输数据类型
  html
  文件
  查询结果
  特点
  无状态
  媒体独立(只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送)
  无连接,一次链接之处理一个请求。也能实现长链接
  消息结构
  客户端: 请求消息结构
  请求行(request line)、请求头部(header)、空行和请求数据四个部分组成
  请求行
  请求体
  空行
  请求数据
  服务端:响应应消息结果
  状态行、消息报头、空行和响应正文。
  状态行
  消息报头
  空行
  响应正文
  请求方法
  HTTP1.0 包含以下三种
  GET --- 请求指定的页面信息,并返回实体主体。
  POST --- 一般用于创新新的数据、修改已有的数据
  HEAD --- 用于获取报头
  HTTP1.1 新增六种
  OPTIONS --- 允许客户端查看服务器的性能。
  PUT --- 从客户端向服务器传送的数据取代指定的文档的内容。
  PATCH --- 是对 PUT 方法的补充,用来对已知资源进行局部更新 。
  DELETE --- 请求服务器删除指定的页面。
  TRACE --- 回显服务器收到的请求,主要用于测试或诊断。
  CONNECT --- HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
  所以在 1.1 以及之前,包含 9 中方法。
  响应头
  Allow
  Content-Encoding
  Content-Length
  Content-Type
  Date
  Expires
  Last-Modified
  Location
  Refresh
  Server
  Set-Cookie
  WWW-Authenticate
  状态码
  对于状态码,我们时常不能记住他们具体的用处,有些具有多年工作经验的从业者,也不见得能记住状态码。
  下面是最常用的状态码:成功、失败(失败的种类比较多了,所以可以理解为,整个状态码,就是对于 失败的识别,然后然用户知道,发生了什么失败!)
  200 - 请求成功
  301 - 资源(网页等)被永久转移到其它URL
  404 - 请求的资源(网页等)不存在
  500 - 内部服务器错误
  分类
  HTTP状态码分类
  1 --- 信息,服务器收到请求,需要请求者继续执行操作
  2 --- 成功,操作被成功接收并处理
  3 --- 重定向,需要进一步的操作以完成请求
  4 --- 客户端错误,请求包含语法错误或无法完成请求
  5 --- 服务器错误,服务器在处理请求的过程中发生了错误
  脚本化 HTTP
  浏览器在 XMLHttpRequest 对象上定义了 HTTP 的API。浏览器中通过 XMLHttpRequest 完成 HTTP 的请求与响应工作。
  使用 XMLHTTPRequest 创建一个请求实例
const request = new XMLHttpRequest();
  其实 XMLHTTPRequest 是有兼容性问题的。在早起的 IE 浏览器宏是并不支持这个对象的。而是用的 ActiveXObject 对象创建。所以,为了兼容会自己封装一个具有兼容性的 XMLHttpRequest 对象,方便使用。
  请求流程
  open 打开一个请求
  设置请求头
  send 发送一个请求
  监听请求状态
request.onReadyStateChange = function() {}
  响应
  获取响应内容
  state
  readyState
  responseText
  getResponseHeader
  responseXML
  nestjs 中 http 状态码
  nestjs 使用枚举类型 HttpStatus 定义了 http 状态码
export enum HttpStatus {
  CONTINUE = 100,
  SWITCHING_PROTOCOLS = 101,
  PROCESSING = 102,
  OK = 200,
  CREATED = 201,
  ACCEPTED = 202,
  NON_AUTHORITATIVE_INFORMATION = 203,
  NO_CONTENT = 204,
  RESET_CONTENT = 205,
  PARTIAL_CONTENT = 206,
  AMBIGUOUS = 300,
  MOVED_PERMANENTLY = 301,
  FOUND = 302,
  SEE_OTHER = 303,
  NOT_MODIFIED = 304,
  TEMPORARY_REDIRECT = 307,
  PERMANENT_REDIRECT = 308,
  BAD_REQUEST = 400,
  UNAUTHORIZED = 401,
  PAYMENT_REQUIRED = 402,
  FORBIDDEN = 403,
  NOT_FOUND = 404,
  METHOD_NOT_ALLOWED = 405,
  NOT_ACCEPTABLE = 406,
  PROXY_AUTHENTICATION_REQUIRED = 407,
  REQUEST_TIMEOUT = 408,
  CONFLICT = 409,
  GONE = 410,
  LENGTH_REQUIRED = 411,
  PRECONDITION_FAILED = 412,
  PAYLOAD_TOO_LARGE = 413,
  URI_TOO_LONG = 414,
  UNSUPPORTED_MEDIA_TYPE = 415,
  REQUESTED_RANGE_NOT_SATISFIABLE = 416,
  EXPECTATION_FAILED = 417,
  I_AM_A_TEAPOT = 418,
  UNPROCESSABLE_ENTITY = 422,
  FAILED_DEPENDENCY = 424,
  TOO_MANY_REQUESTS = 429,
  INTERNAL_SERVER_ERROR = 500,
  NOT_IMPLEMENTED = 501,
  BAD_GATEWAY = 502,
  SERVICE_UNAVAILABLE = 503,
  GATEWAY_TIMEOUT = 504,
  HTTP_VERSION_NOT_SUPPORTED = 505,
}
  http 状态码广泛用于异常
  总结
  面试的过程中,关于 http 状态码,记忆模糊,其实 http 也是有规律的,不同的开头的状态码,被分到了不同的类型中。
  错误类接口和错误
// 错误接口
interface Error {
    name: string;
    message: string;
    stack?: string;
}

// 错误构造器接口
interface ErrorConstructor {
    new(message?: string): Error;
    (message?: string): Error;
    readonly prototype: Error;
}
// 错误类
declare var Error: ErrorConstructor;

// Http 异常类
export class HttpException extends Error {
  public readonly message: any;
 
  constructor(
    private readonly response: string | object,
    private readonly status: number,
  ) {
    super();
    this.message = response;
  }
    
  // 获取响应对象
  public getResponse(): string | object {
    return this.response;
  }

// 获取状态码
  public getStatus(): number {
    return this.status;
  }
// 转变成字符串
  public toString(): string {
    const message = this.getErrorString(this.message);
    return `Error: ${message}`;
  }
// 获取错误字符串
  private getErrorString(target: string | object): string {
    return isString(target) ? target : JSON.stringify(target);
  }
    // 静态方法创建 body
  public static createBody(
    message: object | string,
    error?: string,
    statusCode?: number,
  ) {
    if (!message) {
      return { statusCode, error };
    }
    return isObject(message) && !Array.isArray(message)
      ? message
      : { statusCode, error, message };
  }
}

// HttpStatus.BAD_REQUEST 是 400
export class BadRequestException extends HttpException {
  constructor(message?: string | object | any, error = 'Bad Request') {
    super(
      HttpException.createBody(message, error, HttpStatus.BAD_REQUEST),
      HttpStatus.BAD_REQUEST,
    );
  }
}

 // ConflictException 是 409
 export class ConflictException extends HttpException {
 constructor(message?: string | object | any, error = 'Conflict') {
    super(
      HttpException.createBody(message, error, HttpStatus.CONFLICT),
      HttpStatus.CONFLICT,
    );
  }
}

// ForbiddenException 是 403
export class ForbiddenException extends HttpException {
  constructor(message?: string | object | any, error = 'Forbidden') {
    super(
      HttpException.createBody(message, error, HttpStatus.FORBIDDEN),
      HttpStatus.FORBIDDEN,
    );
  }
}

// GatewayTimeoutException 504
export class GatewayTimeoutException extends HttpException {
  constructor(message?: string | object | any, error = 'Gateway Timeout') {
    super(
      HttpException.createBody(message, error, HttpStatus.GATEWAY_TIMEOUT),
      HttpStatus.GATEWAY_TIMEOUT,
    );
  }
}
// GoneException 401
export class GoneException extends HttpException {
  constructor(message?: string | object | any, error = 'Gone') {
    super(
      HttpException.createBody(message, error, HttpStatus.GONE),
      HttpStatus.GONE,
    );
  }
}

// 505
export class HttpVersionNotSupportedException extends HttpException {
  constructor(
    message?: string | object | any,
    error = 'HTTP Version Not Supported',
  ) {
    super(
      HttpException.createBody(
        message,
        error,
        HttpStatus.HTTP_VERSION_NOT_SUPPORTED,
      ),
      HttpStatus.HTTP_VERSION_NOT_SUPPORTED,
    );
  }
}

// 418
export class ImATeapotException extends HttpException {

  constructor(message?: string | object | any, error = `I'm a teapot`) {
    super(
      HttpException.createBody(message, error, HttpStatus.I_AM_A_TEAPOT),
      HttpStatus.I_AM_A_TEAPOT,
    );
  }
}

// 500
export class InternalServerErrorException extends HttpException {
  constructor(
    message?: string | object | any,
    error = 'Internal Server Error',
  ) {
    super(
      HttpException.createBody(
        message,
        error,
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
      HttpStatus.INTERNAL_SERVER_ERROR,
    );
  }
}

// 405
export class MethodNotAllowedException extends HttpException {
  constructor(message?: string | object | any, error = 'Method Not Allowed') {
    super(
      HttpException.createBody(message, error, HttpStatus.METHOD_NOT_ALLOWED),
      HttpStatus.METHOD_NOT_ALLOWED,
    );
  }
}

// 406
export class NotAcceptableException extends HttpException {
  constructor(message?: string | object | any, error = 'Not Acceptable') {
    super(
      HttpException.createBody(message, error, HttpStatus.NOT_ACCEPTABLE),
      HttpStatus.NOT_ACCEPTABLE,
    );
  }
}

// 404
export class NotFoundException extends HttpException {
  constructor(message?: string | object | any, error = 'Not Found') {
    super(
      HttpException.createBody(message, error, HttpStatus.NOT_FOUND),
      HttpStatus.NOT_FOUND,
    );
  }
}

// 501
export class NotImplementedException extends HttpException {
  
  constructor(message?: string | object | any, error = 'Not Implemented') {
    super(
      HttpException.createBody(message, error, HttpStatus.NOT_IMPLEMENTED),
      HttpStatus.NOT_IMPLEMENTED,
    );
  }
}

// 413
export class PayloadTooLargeException extends HttpException {
  constructor(message?: string | object | any, error = 'Payload Too Large') {
    super(
      HttpException.createBody(message, error, HttpStatus.PAYLOAD_TOO_LARGE),
      HttpStatus.PAYLOAD_TOO_LARGE,
    );
  }
}

//  408
export class RequestTimeoutException extends HttpException {
  constructor(message?: string | object | any, error = 'Request Timeout') {
    super(
      HttpException.createBody(message, error, HttpStatus.REQUEST_TIMEOUT),
      HttpStatus.REQUEST_TIMEOUT,
    );
  }
}

// 503
export class ServiceUnavailableException extends HttpException {
  constructor(message?: string | object | any, error = 'Service Unavailable') {
    super(
      HttpException.createBody(message, error, HttpStatus.SERVICE_UNAVAILABLE),
      HttpStatus.SERVICE_UNAVAILABLE,
    );
  }
}

// 401
export class UnauthorizedException extends HttpException {
  constructor(message?: string | object | any, error = 'Unauthorized') {
    super(
      HttpException.createBody(message, error, HttpStatus.UNAUTHORIZED),
      HttpStatus.UNAUTHORIZED,
    );
  }
}

// ...
  在 nestjs 中抛出一个异常
  标准异常
  标准异常的格式如下,只有两个字段:状态码和错误提示消息
{
  "statusCode": 500,
  "message": "Internal server error"
}
  下面我们可以控制器中,使用 throw 实例化HttpException 类型,输出标准的异常:
@Get()
async findAll() {
  throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
  第一个参数是 message, 第二个参数是状态码。
  覆盖标准输出,自定义一些信息,只需要将第一个参数改成一个对象:
@Get()
async findAll() {
  throw new HttpException({
    status: HttpStatus.FORBIDDEN,
    error: 'This is a custom message',
  }, HttpStatus.FORBIDDEN);
}
  当然我们可以自定义类,方便我们复用其他的项目:
export class ForbiddenException extends HttpException {
  constructor() {
    super('Forbidden', HttpStatus.FORBIDDEN);
  }
}
  异常与过滤器层配合
  异常过滤器,是使得有能力完全的控制异常。可以控制确切的控制流以及将响应的内容发送回客户端。
  过滤可以作用于:
  路由函数
  controller 类
  在全局中使用
  捕获全部的异常
  todo
  jsonp
  轮询
  comet

  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号