合抱之木,生于毫末; 九层之台,起于垒土; 千里之行,始于足下。 --- 《道德经》 第六十四章。
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