Little H title

this is subtitle


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 公益404

webpack简单入门

发表于 2019-04-25 | 分类于 前端教程

webpack 简单入门

输出

见到启动 webpack 后输出的信息, 这是啥?

1
2
3
4
5
6
7
8
ℹ 「wds」: Project is running at http://localhost:8080/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from ./src
ℹ 「atl」: Using typescript@3.4.5 from typescript
ℹ 「atl」: Using tsconfig.json from /Users/xuheng/qiniu/work/annotation-sys/tsconfig.json
ℹ 「atl」: Checking started in a separate process...
ℹ 「atl」: Time: 2979ms
ℹ 「wdm」: Hash: d3a23643aefb00fc4a9b

其实都是使用依赖的简写, 如 wds 就是webpack-dev-server. atl 就是awesome-typescript-loader, wdm 是webpack-dev-middleware

Hot Module Replacement(HMR), 最方便的方法就使用 wds 咯

  • 在 webpack 配置文件中添加 HMR 插件;
  • 在 Webpack Dev Server 中添加“hot”参数;

postcss

在webpack项目中配置postcss,实现autoprefix

注意点

关于 less-loader, 要装 less 的
webpack error in Cannot find module ‘less’

less-loader

也说了 The less-loader requires less as peerDependency. Thus you are able to control the versions accurately.

参考

深入浅出 Webpack
入门 Webpack,看这篇就够了
Node 的 path.resolve(__dirname,’./src’)

tsconfig.json配置解析

发表于 2019-04-25 | 分类于 前端

tsconfig.json 配置解析

  • 指定待编译的文件
  • 定义编译选项

安装外typescript后用tsc --init就可以生成, 也可以自己创咯

概述

如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是 TypeScript 项目的根目录。 tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项。

一个项目可以通过以下方式之一来编译:
使用tsconfig.json(推荐)

  • 不带任何输入文件的情况下调用tsc,编译器会从当前目录开始去查找tsconfig.json文件,逐级向上搜索父目录。(常用)
  • 不带任何输入文件的情况下调用tsc,且使用命令行参数—project(或-p)指定一个包含tsconfig.json文件的目录。(不常用)

当命令行上指定了输入文件时,tsconfig.json文件会被忽略。(不建议)

示例

tsconfig.json示例文件:

使用"files"属性 (不建议)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true
},
"files": [
"core.ts",
"sys.ts",
"types.ts",
"scanner.ts",
"parser.ts",
"utilities.ts",
"binder.ts",
"checker.ts",
"emitter.ts",
"program.ts",
"commandLineParser.ts",
"tsc.ts",
"diagnosticInformationMap.generated.ts"
]
}

使用"include"和"exclude"属性 (推荐)

1
2
3
4
5
6
7
8
9
10
11
12
{
"compilerOptions": {
"module": "system",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"outFile": "../../built/local/tsc.js",
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}

细节

"compilerOptions"可以被忽略,这时编译器会使用默认值。在这里查看完整的编译器选项列表。

"files"指定一个包含相对或绝对文件路径的列表。 "include"和"exclude"属性指定一个文件glob匹配模式列表。 支持的glob通配符有:

  • * 匹配 0 或多个字符(不包括目录分隔符)
  • ? 匹配一个任意字符(不包括目录分隔符)
  • **/ 递归匹配任意子目录

如果一个glob模式里的某部分只包含*或.*,那么仅有支持的文件扩展名类型被包含在内(比如默认.ts,.tsx,和.d.ts, 如果 allowJs设置能true还包含.js和.jsx)。

  1. 如果"files"和"include"都没有被指定,编译器默认包含当前目录和子目录下所有的TypeScript文件(.ts, .d.ts 和 .tsx),排除在"exclude"里指定的文件。
    JS文件(.js和.jsx)也被包含进来如果allowJs被设置成true。

  2. 如果指定了 "files"或"include",编译器会将它们结合一并包含进来。
    使用 "outDir"指定的目录下的文件永远会被编译器排除,除非你明确地使用"files"将其包含进来(就算你没用exclude也会被排除出去)。

  3. 使用"include"引入的文件可以使用"exclude"属性过滤。 然而,通过 "files"属性明确指定的文件却总是会被包含在内,不管"exclude"如何设置。 如果没有特殊指定, "exclude"默认情况下会排除node_modules,bower_components,jspm_packages和<outDir>目录。

  4. 任何被"files"或"include"指定的文件所引用的文件也会被包含进来。 A.ts引用了B.ts,因此B.ts不能被排除,除非引用它的A.ts在"exclude"列表中。

需要注意编译器不会去引入那些可能做为输出的文件;比如,假设我们包含了index.ts,那么index.d.ts和index.js会被排除在外。 通常来讲,不推荐只有扩展名的不同来区分同目录下的文件。

tsconfig.json文件可以是个空文件,那么所有默认的文件(如上面所述)都会以默认配置选项编译。

在命令行上指定的编译选项会覆盖在tsconfig.json文件里的相应选项。

file, include, exclude, outDir总结

  1. 没有file和include, 默认包含当前tsconfig.json目录下和子目录下素有的(.ts, tsx, .d.ts)文件
  2. file是一定不会被排除的, include的可以用exclude的排除掉一部分. outDir这个输出文件也会被排除,(按编译器不会去引入那些可能做为输出的文件)
  3. 被file和include引用的文件的文件也会被包进来

即file和outDir都会包含和不包含, include和exclude来设置下

@types,typeRoots和types

默认所有可见的"@types"包会在编译过程中被包含进来。 node_modules/@types文件夹下以及它们子文件夹下的所有包都是可见的; 也就是说, ./node_modules/@types/,../node_modules/@types/和../../node_modules/@types/等等。

如果指定了typeRoots,只有typeRoots下面的包才会被包含进来。 比如:

1
2
3
4
5
{
"compilerOptions": {
"typeRoots": ["./typings"]
}
}

这个配置文件会包含所有./typings下面的包,而不包含./node_modules/@types里面的包。(但我们不推荐用typings了)

如果指定了types,只有被列出来的包才会被包含进来。 比如:

1
2
3
4
5
{
"compilerOptions": {
"types": ["node", "lodash", "express"]
}
}

这个tsconfig.json文件将仅会包含 ./node_modules/@types/node,./node_modules/@types/lodash和./node_modules/@types/express。
/@types/。 node_modules/@types/*里面的其它包不会被引入进来。

指定"types": []来禁用自动引入@types包。

注意,自动引入只在你使用了全局的声明(相反于模块)时是重要的。 如果你使用 import "foo"语句,TypeScript仍然会查找node_modules和node_modules/@types文件夹来获取foo包。

使用 extends 继承配置 (不常用)

tsconfig.json文件可以利用extends属性从另一个配置文件里继承配置。

extends是tsconfig.json文件里的顶级属性(与compilerOptions,files,include,和exclude一样)。 extends的值是一个字符串,包含指向另一个要继承文件的路径。

在原文件里的配置先被加载,然后被来至继承文件里的配置重写。 如果发现循环引用,则会报错。

来至所继承配置文件的files,include和exclude覆盖源配置文件的属性。

配置文件里的相对路径在解析时相对于它所在的文件。

比如:

configs/base.json:

1
2
3
4
5
6
{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true
}
}

tsconfig.json:

1
2
3
4
{
"extends": "./configs/base",
"files": ["main.ts", "supplemental.ts"]
}

tsconfig.nostrictnull.json:

1
2
3
4
5
6
{
"extends": "./tsconfig",
"compilerOptions": {
"strictNullChecks": false
}
}

compileOnSave (直接 false 吧)

在最顶层设置compileOnSave标记,可以让IDE在保存文件的时候根据tsconfig.json重新生成文件。

1
2
3
4
5
6
{
"compileOnSave": true,
"compilerOptions": {
"noImplicitAny": true
}
}

要想支持这个特性需要 Visual Studio 2015, TypeScript1.8.4 以上并且安装 atom-typescript 插件。

给一个简单常用的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"compileOnSave": false,
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"allowSyntheticDefaultImports": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
"strictNullChecks": false,
"baseUrl": "./src",
"lib": ["es2015", "dom"],
"jsx": "react",
"outDir": ".tmp"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "bower_components"],
}

参考

tsconfig.json
tsconfig.json 整理记录
完整的编译器选项列表
理解 Typescript 配置文件

http/2简介

发表于 2019-04-22

http/2简介

参考

怎样把网站升级到http/2
HTTP/3 竟然基于 UDP,HTTP 协议这些年都经历了啥?

mysql的timestamp和datetime区别

发表于 2019-04-11 | 分类于 mysql

mysql 的 timestamp 和 datetime 区别

首先 DATETIM 和 TIMESTAMP 类型所占的存储空间不同,前者 8 个字节,后者 4 个字节,这样造成的后果是两者能表示的时间范围不同。前者范围为 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59,后者范围为 1970-01-01 08:00:01 到 2038-01-19 11:14:07。所以可以看到 TIMESTAMP 支持的范围比 DATATIME 要小,容易出现超出的情况.

其次: TIMESTAMP 类型在默认情况下,insert、update 数据时,TIMESTAMP 列会自动以当前时间(CURRENT_TIMESTAMP)填充/更新。

第三: 最主要的区别-受时区影响不同TIMESTAMP 比较受时区 timezone 的影响以及 MYSQL 版本和服务器的 SQL MODE 的影响,

详细可以阅读这篇博客的演示:MySQL: Datetime Versus Timestamp Data Types
一个timestamp字段,一个datetime字段,修改时区SET TIME_ZONE = “america/new_york”;后,timestamp字段的值变了!
因此,如果应用场景有跨时区要求的要特别注意这点。

第四: 索引速度
不同。timestamp 更轻量,索引相对 datetime 更快。

所以一般来说,我比较倾向选择 DATETIME,至于你说到索引的问题,选择 DATETIME 作为索引,如果碰到大量数据查询慢的情况,也可以分区表解决。

参考

Mysql 时间字段格式如何选择,TIMESTAMP,DATETIME,INT?
MySQL date、datetime 和 timestamp 类型的区别
MySQL: Datetime Versus Timestamp Data Types
TIMESTAMP和DATETIME相同和区别
mysql datetime与timestamp区别

mysql的ENGINE=InnoDB

发表于 2019-04-11 | 分类于 mysql

mysql 的 ENGINE=InnoDB

最开始用 MySQL Administrator 建数据库的时候,表缺省是 InnoDB 类型,也就没有在意。后来用 Access2MySQL 导数据的时候发现只能导成 MyISAM 类型的表,不知道这两种类型有什么区别,就去查了查。
原来是 MyISAM 类型不支持事务处理等高级处理,而 InnoDB 类型支持。 MyISAM 类型的表强调的是性能,其执行数度比 InnoDB 类型更快,但是不提供事务支持,而 InnoDB 提供事务支持已经外部键等高级数据库功能。这样就可以根据数据表不同的用处是用不同的存储类型。
另外,MyISAM 类型的二进制数据文件可以在不同操作系统中迁移。也就是可以直接从 Windows 系统拷贝到 linux 系统中使用。

AUTO_INCREMENT=22

这个是自增的,在这里设置数字的意思是想要让这条语句在增长的时候,从22开始自增。

参考

mysql 中 engine=innodb 和 engine=myisam 的区别
mysql 中 engine=innodb 和 engine=myisam 的区别
MySQL中ENGINE=InnoDB、AUTO_INCREMENT的意思

mysql创建表的时候COLLATE干嘛的

发表于 2019-04-11 | 分类于 mysql

mysql 创建表的时候 COLLATE 干嘛的

在 mysql 中执行show create table <tablename>指令,可以看到一张表的建表语句,example 如下:

1
2
3
4
5
6
7
8
CREATE TABLE table_name(
column_name_1 data_type default value column_constraint,
column_name_2 data_type default value column_constraint,
...,
table_constraint
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8_unicode_ci;

复制代码大部分字段我们都能看懂,但是今天要讨论的是 COLLATE 关键字。这个值后面对应的 utf8_unicode_ci 是什么意思呢?面试的时候用这个题目考一考 DBA,应该可以难倒一大部分人。

COLLATE 是用来做什么的?

使用phpmyadmin的开发可能会非常眼熟,因为其中的中文表头已经给出了答案:

phpmyadmin.png

所谓 utf8_unicode_ci,其实是用来排序的规则。对于 mysql 中那些字符类型的列,如 VARCHAR,CHAR,TEXT 类型的列,都需要有一个 COLLATE 类型来告知 mysql 如何对该列进行排序和比较。
简而言之,COLLATE 会影响到 ORDER BY 语句的顺序,会影响到 WHERE 条件中大于小于号筛选出来的结果,会影响DISTINCT、GROUP BY、HAVING语句的查询结果。另外,mysql 建索引的时候,如果索引列是字符类型,也会影响索引创建,只不过这种影响我们感知不到。总之,凡是涉及到字符类型比较或排序的地方,都会和 COLLATE 有关。

各种 COLLATE 的区别

COLLATE通常是和数据编码(CHARSET)相关的,一般来说每种CHARSET都有多种它所支持的COLLATE,并且每种CHARSET都指定一种COLLATE为默认值。例如Latin1编码的默认COLLATE为latin1_swedish_ci,GBK编码的默认COLLATE为gbk_chinese_ci,utf8mb4编码的默认值为utf8mb4_general_ci。

这里顺便讲个题外话,mysql中有utf8和utf8mb4两种编码,在 mysql 中请大家忘记utf8,永远使用utf8mb4。这是 mysql 的一个遗留问题,mysql 中的 utf8 最多只能支持 3bytes 长度的字符编码,对于一些需要占据4bytes的文字,mysql 的 utf8 就不支持了,要使用 utf8mb4 才行。

很多COLLATE都带有_ci字样,这是Case Insensitive的缩写,即大小写无关,也就是说”A”和”a”在排序和比较的时候是一视同仁的。selection * from table1 where field1="a"同样可以把field1 为”A”的值选出来。与此同时,对于那些_cs后缀的COLLATE,则是Case Sensitive,即大小写敏感的。

在 mysql 中使用show collation指令可以查看到 mysql 所支持的所有COLLATE。以 utf8mb4 为例,该编码所支持的所有COLLATE如下图所示。

utf8mb4.png

mysql 中和 utf8mb4 相关的所有 COLLATE

图中我们能看到很多国家的语言自己的排序规则。在国内比较常用的是utf8mb4_general_ci(默认)、utf8mb4_unicode_ci、utf8mb4_bin这三个。我们来探究一下这三个的区别:

首先utf8mb4_bin的比较方法其实就是直接将所有字符看作二进制串,然后从最高位往最低位比对。所以很显然它是区分大小写的。
而utf8mb4_unicode_ci和utf8mb4_general_ci对于中文和英文来说,其实是没有任何区别的。对于我们开发的国内使用的系统来说,随便选哪个都行。只是对于某些西方国家的字母来说,utf8mb4_unicode_ci会比utf8mb4_general_ci更符合他们的语言习惯一些.
general 是 mysql 一个比较老的标准了。例如,德语字母“ß”,在utf8mb4_unicode_ci中是等价于”ss”两个字母的(这是符合德国人习惯的做法),而在utf8mb4_general_ci中,它却和字母“s”等价。不过,这两种编码的那些微小的区别,对于正常的开发来说,很难感知到。本身我们也很少直接用文字字段去排序,退一步说,即使这个字母排错了一两个,真的能给系统带来灾难性后果么?从网上找的各种帖子讨论来说,更多人推荐使用utf8mb4_unicode_ci,但是对于使用了默认值的系统,也并没有非常排斥,并不认为有什么大问题。

结论:推荐使用utf8mb4_unicode_ci,对于已经用了utf8mb4_general_ci的系统,也没有必要花时间改造。

另外需要注意的一点是,从mysql 8.0开始,mysql 默认的CHARSET已经不再是 Latin1 了,改为了utf8mb4参考链接 10.5 Configuring Application Character Set and Collation,并且默认的COLLATE也改为了utf8mb4_0900_ai_ci。utf8mb4_0900_ai_ci大体上就是unicode的进一步细分,0900 指代 unicode 比较算法的编号( Unicode Collation Algorithm version),ai 表示 accent insensitive(发音无关),例如 e, è, é, ê 和 ë 是一视同仁的。

What is the utf8mb4_0900_ai_ci Collation?
10.3.1 Collation Naming Conventions

COLLATE 设置级别及其优先级

设置COLLATE可以在实例级别、库级别、表级别、列级别、以及SQL 指定。

实例级别的COLLATE设置就是 mysql 配置文件或启动指令中的collation_connection系统变量。

库级别设置COLLATE的语句如下:

1
CREATE DATABASE <db_name> DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

如果库级别没有设置CHARSET和COLLATE,则库级别默认的CHARSET和COLLATE使用实例级别的设置。在 mysql8.0 以下版本中,你如果什么都不修改,默认的CHARSET是Latin1,默认的COLLATE是latin1_swedish_ci。从 mysql8.0 开始,默认的CHARSET已经改为了utf8mb4,默认的COLLATE改为了utf8mb4_0900_ai_ci。

表级别的COLLATE设置,则是在CREATE TABLE的时候加上相关设置语句,例如:

1
2
3
4
5
6
CREATE TABLE (
……
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT ='XX表';

复制代码如果表级别没有设置 CHARSET 和 COLLATE,则表级别会继承库级别的 CHARSET 与 COLLATE。

列级别的设置,则也在在CREATE TABLE中声明列的时候指定,例如

1
2
3
4
5
CREATE TABLE (

`field1` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
……
) ……

复制代码如果列级别没有设置 CHARSET 和 COLATE,则列级别会继承表级别的 CHARSET 与 COLLATE。

最后,你也可以在写 SQL 查询的时候显示声明 COLLATE 来覆盖任何库表列的 COLLATE 设置,不太常用,了解即可:

1
2
3
SELECT DISTINCT field1 COLLATE utf8mb4_general_ci FROM table1;

SELECT field1, field2 FROM table1 ORDER BY field1 COLLATE utf8mb4_unicode_ci;

如果全都显示设置了,那么优先级顺序是 SQL 语句 > 列级别设置 > 表级别设置 > 库级别设置 > 实例级别设置。也就是说列上所指定的 COLLATE 可以覆盖表上指定的 COLLATE,表上指定的 COLLATE 可以覆盖库级别的 COLLATE。如果没有指定,则继承下一级的设置。即列上面没有指定 COLLATE,则该列的 COLLATE 和表上设置的一样。
以上就是关于 mysql 的 COLLATE 相关知识。不过,在系统设计中,我们还是要尽量避免让系统严重依赖中文字段的排序结果,在 mysql 的查询中也应该尽量避免使用中文做查询条件。

参考

MYSQL 中的 COLLATE 是什么?

http3xx重定向状态码

发表于 2019-04-11 | 分类于 网络

http3xx 重定向状态码

简单说下 301/302/303/307/308 这 5 种状态码的情况

状态码的解释

301 Moved Permanently(永久移动)

被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。
除非额外指定,否则这个响应也是可缓存的。新的永久性的 URI 应当在响应的Location域中返回。
除非这是一个 HEAD 请求,否则响应的实体中应当包含指向新的 URI 的超链接及简短说明。如果这不是一个 GET 或者 HEAD 请求,因此浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。

注意:对于某些使用 HTTP/1.0 协议的浏览器,当它们发送的 POST 请求得到了一个 301 响应的话,接下来的重定向请求将会变成 GET 方式。

302 Found(发现)

要求客户端执行临时重定向(原始描述短语为“Moved Temporarily”)。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。
只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。新的临时性的 URI 应当在响应的Location域中返回。
除非这是一个 HEAD 请求,否则响应的实体中应当包含指向新的 URI 的超链接及简短说明。如果这不是一个 GET 或者 HEAD 请求,那么浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。

注意:虽然 RFC 1945 和 RFC 2068 规范不允许客户端在重定向时改变请求的方法,但是很多现存的浏览器将 302 响应视作为 303 响应,并且使用 GET 方式访问在 Location 中规定的 URI,而无视原先请求的方法。因此状态码 303 和 307 被添加了进来,用以明确服务器期待客户端进行何种反应。

303 See Other(查看其他)

对应当前请求的响应可以在另一个 URI 上被找到,当响应于 POST(或 PUT / DELETE)接收到响应时,客户端应该假定服务器已经收到数据,并且应该使用单独的 GET 消息发出重定向。这个方法的存在主要是为了允许由脚本激活的 POST 请求输出重定向到一个新的资源。这个新的 URI 不是原始资源的替代引用。同时,303 响应禁止被缓存。当然,第二个请求(重定向)可能被缓存。新的 URI 应当在响应的 Location 域中返回。除非这是一个 HEAD 请求,否则响应的实体中应当包含指向新的 URI 的超链接及简短说明。注意:许多 HTTP/1.1 版以前的浏览器不能正确理解 303 状态。如果需要考虑与这些浏览器之间的互动,302 状态码应该可以胜任,因为大多数的浏览器处理 302 响应时的方式恰恰就是上述规范要求客户端处理 303 响应时应当做的。

307 Temporary Redirect(临时重定向)

在这种情况下,请求应该与另一个 URI 重复,但后续的请求应仍使用原始的 URI。 与 302 相反,当重新发出原始请求时,不允许更改请求方法。 例如,应该使用另一个 POST 请求来重复 POST 请求

308 Permanent Redirect (永久重定向)

请求和所有将来的请求应该使用另一个 URI 重复。 307 和 308 重复 302 和 301 的行为,但不允许 HTTP 方法更改。 例如,将表单提交给永久重定向的资源可能会顺利进行。

301/302/303/307/308 的区别

301,302 是 http1.0的内容,303、307、308 是 http1.1的内容。

301 和 302 本来在规范中是不允许重定向时改变请求方法的(将 POST 改为 GET),但是许多浏览器却允许重定向时改变请求方法(这是一种不规范的实现)。

303 的出现正是为了给上面的 301,302 这种行为作出个规范(将错就错吧),也就是允许重定向时改变请求方法。此外 303 响应禁止被缓存。

大多数的浏览器处理 302 响应时的方式恰恰就是上述规范要求客户端处理 303 响应时应当做的,所以 303 基本用的很少,一般用 302。

307 和 308 的出现也是给上面的行为做个规范,不过是不允许重定向时改变请求方法。

Permanent Temporary
Allows changing the request method from POST to GET. 301 302
Does not allow changing the request method from POST to GET. 308 307

注:永久(Permanent)和临时(Temporary)的区别

永久是指原来访问的资源已经永久删除啦,客户端应该根据新的 URI 访问重定向。

临时是指访问的资源可能暂时先用 location 的 URI 访问,但旧资源还在的,下次你再来访问的时候可能就不用重定向了。

故 301 与 302 的区别:

301 表示搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302 表示旧地址 A 的资源还在(仍然可以访问),这个重定向只是临时地从旧地址 A 跳转到地址 B,搜索引擎会抓取新的内容而保存旧的网址。

使用场景

因为 301 与 302 的区别,所以导致产生 302 网址劫持,故不建议使用 302 重定向(然而浏览器默认是使用 302 重定向)

302 重定向和网址劫持(URL hijacking)

从网址 A 做一个 302 重定向到网址 B 时,主机服务器的隐含意思是网址 A 随时有可能改主意,重新显示本身的内容或转向其他的地方。大部分的搜索引擎在大部分情况下,当收到 302 重定向时,一般只要去抓取目标网址就可以了,也就是说网址 B。如果搜索引擎在遇到 302 转向时,百分之百的都抓取目标网址 B 的话,就不用担心网址 URL 劫持了。问题就在于,有的时候搜索引擎,尤其是 Google,并不能总是抓取目标网址。比如说,有的时候 A 网址很短,但是它做了一个 302 重定向到 B 网址,而 B 网址是一个很长的乱七八糟的 URL 网址,甚至还有可能包含一些问号之类的参数。很自然的,A 网址更加用户友好,而 B 网址既难看,又不用户友好。这时 Google 很有可能会仍然显示网址 A。由于搜索引擎排名算法只是程序而不是人,在遇到 302 重定向的时候,并不能像人一样的去准确判定哪一个网址更适当,这就造成了网址 URL 劫持的可能性。也就是说,一个不道德的人在他自己的网址 A 做一个 302 重定向到你的网址 B,出于某种原因, Google 搜索结果所显示的仍然是网址 A,但是所用的网页内容却是你的网址 B 上的内容,这种情况就叫做网址 URL 劫持。你辛辛苦苦所写的内容就这样被别人偷走了。302 重定向所造成的网址 URL 劫持现象,已经存在一段时间了。不过到目前为止,似乎也没有什么更好的解决方法。在正在进行的谷歌大爸爸数据中心转换中,302 重定向问题也是要被解决的目标之一。从一些搜索结果来看,网址劫持现象有所改善,但是并没有完全解决。

使用 301 的场景:(一般是资源位置永久更改)

1.域名到期不想续费(或者发现了更适合网站的域名),想换个域名。

2.在搜索引擎的搜索结果中出现了不带 www 的域名,而带 www 的域名却没有收录,这个时候可以用 301 重定向来告诉搜索引擎我们目标的域名是哪一个。

3.空间服务器不稳定,换空间的时候。

注:另外,返回 301 请求码进行跳转被谷歌认为是将网站地址由 HTTP 迁移到 HTTPS 的最佳方法(然而大家都用 302。。。。)

使用 302 的场景:(一般是普通的重定向需求:临时跳转)

1.未登录前先使用 302 重定向到登录页面,登录成功后再跳回到原来请求的页面

举个例子,比如我未登录京东前我就访问京东的个人界面 https://home.jd.com/ ,然后就会重定向到登录界面,我们可以通过浏览器的 dev-tool 查看状态码

我们可以发现响应的状态码为 302,并且返回了 location 为登录界面的 url,并且附带了 ReturnUrl 方便我们登录后跳回到 https://home.jd.com/

2.有时候需要自动刷新页面,比如 5 秒后回到订单详细页面之类。

例子略。

3.有时系统进行升级或者切换某些功能时,需要临时更换地址。

例子略。

4.像微博之类的使用短域名,用户浏览后需要重定向到真实的地址之类。
再来个例子,我访问了另一个微博的秒拍视频链接:http://t.cn/RuOcwxn,然后重定向了两次,先是301重定向到video.weibo.com,再302重定向到miaopai.com。

5.电脑端与移动端的转换

比如我访问网页端页面https://www.taobao.com/,302重定向到了移动端页面m.taobao.com

使用 303 的场景

几乎没有,一般就是用 302

使用 307 的场景

很少用,与 302 类似,只不过是针对 POST 方法的请求不允许更改方法

不过我在访问百度时,发现用了 307 状态码

使用 308 的场景

很少用,与 301 类似,只不过是针对 POST 方法的请求不允许更改方法

参考

详解重定向(HTTP 状态码 301/302/303/307/308)附例子

在前端用JavaScript来实现下载

发表于 2019-04-08 | 分类于 javascript教程

在前端用JavaScript来实现下载

很多情况下我们都只能给出个链接,让用户点击打开->另存为。如下面这个链接:

1
<a href=”file.js”>file.js</a>

用户点击这个链接的时候,浏览器会打开并显示链接指向的文件内容,显然,这并没有实现我们的需求。

幸好,HTML 5 里面为 <a> 标签添加了一个 download 的属性,我们可以轻易的利用它来实现下载功能。

1
<a href="http://somehost/somefile.zip" download="filename.zip">Download file</a>

只要为 <a> 标签添加 download 属性就行, 并且添加的属性值是下载的文件名.

在用javascript写的时候也是按这个思路

  • 用 JavaScript 创建一个隐藏的 <a> 标签
  • 设置它的 href 属性
  • 设置它的 download 属性
  • 用 JavaScript 来触发这个它的 click 事件

翻译成javascript代码就是

1
2
3
4
5
6
7
var a = document.createElement('a');
var url = window.URL.createObjectURL(blob);
var filename = 'what-you-want.csv';
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);

在设置属性的时候不用setAttribute可以看这个链接What is happening behind .setAttribute vs .attribute=?

DataURI

这只是下载对应href中的文件, 或者说是在服务器端的文件, 如果是在前端的文件呢, DataURI来解决
什么是data URI scheme及如何使用data URI scheme

这种取得资料的方法称为 http URI scheme, 同样的效果使用 data URI scheme

简单说就是这种方式会把所有的数据都存在href中, 而不会去请求服务器.

URL.createObjectURL()

URL.createObjectURL()

window.URL 里面有两个方法:

  • createObjectURL 用 blob 对象来创建一个 object URL(它是一个 DOMString),我们可以用这个 object URL 来表示某个 blob 对象,这个 object URL 可以用在 href 和 src 之类的属性上。
  • revokeObjectURL 释放由 createObjectURL 创建的 object URL,当该 object URL 不需要的时候,我们要主动调用这个方法来获取最佳性能和内存使用。

知道了这两个方法之后,我们再回去看看上面的例子就很容易理解了吧!只是用 blob 对象来创建一条 URL,然后让 <a> 标签引用该 URL,然后触发个点击事件,就可以下载文件了!

Blob 对象

Blob 全称是 Binary large object,它表示一个类文件对象,可以用它来表示一个文件。根据 MDN 上面的说法,File API 也是基于 blob 来实现的。

Blob

这里使用 Blob() 构造函数 允许用其它对象创建 Blob 对象。比如,用字符串构建一个 blob:

1
2
3
var debug = {hello: "world"};
var blob = new Blob([JSON.stringify(debug, null, 2)],
{type : 'application/json'});

所以依赖这个我们可以从后端拿到数据, 然后再前端下载它. 设计分页的我们也都是前端开分页.

MIME类型(Multipurpose Internet Mail Extensions)

上面Blob的类型type选择比如要下载csv的就要选text/csv

多用途Internet邮件扩展(MIME)类型

完整的MIME类型列表 可以按扩展名来查看MIME Type

参考

如何用 JavaScript 下载文件
前端 javascript 实现文件下载
在浏览器端用JS创建和下载文件
什么是data URI scheme及如何使用data URI scheme
What is happening behind .setAttribute vs .attribute=?
多用途Internet邮件扩展(MIME)类型
Media Types
什么是data URI scheme及如何使用data URI scheme
Data URLs
Blob

leetcode刷题 js版

发表于 2019-04-07 | 分类于 前端

leetcode刷题 js版

刷题地址在这leetcode

当然网址上也会有java版的解答, 涉及优化的细节

1. Two Sum

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
// Approach 1: Brute Force
var twoSum = function(nums, target) {
for( let i = 0; i < nums.length; i++) {
for(let j = i+1; j < nums.length; j++) {
if (nums[j] === target - nums[i]) {
return [i,j]
}
}
}
};
// Approach 2: Two-pass Hash Table
var twoSum = function(nums, target) {
const map = new Map()
for(let i = 0; i < nums.length; i++) {
map.set(nums[i], i);
}
for(let i = 0; i < nums.length; i++) {
let complement = target - nums[i];
if (map.has(complement) && map.get(complement) != i) {
return [map.get(complement), i]
}
}
};
// Approach 3: One-pass Hash Table
var twoSum = function(nums, target) {
const map = new Map()
for(let i = 0; i < nums.length; i++) {
let complement = target - nums[i];
if (map.has(complement)) {
return [map.get(complement), i]
}
map.set(nums[i], i)
}
};

2. Add Two Numbers

5. Longest Palindromic Substring

参考

LeetCode solutions with JavaScript
chihungyu1116/leetcode-javascript
Demonstrate all the questions on LeetCode in the form of animation.

前端的网络请求方式和http

发表于 2019-04-01 | 分类于 前端教程

前端的网络请求方式和http

http请求报文

HTTP之请求消息Request

客户端发送一个HTTP请求到服务器的请求消息包括以下格式:

请求行(request line)、请求头部(header)、空行和请求数据四个部分组成。

request.png

这里难理解的事请求头中各个字段的用法.

所有的头部信息HTTP Headers

列一些常见的

消息头 描述 更多信息 标准
date 报文创建的日期和时间 date RFC 7231
cache-control 在http 请求和响应中通过指定指令来实现缓存机制 cache-control
Connection 决定当前的事务完成后,是否会关闭网络连接 Connection

HTTP之响应消息Response

一般情况下,服务器接收并处理客户端发过来的请求后会返回一个HTTP的响应消息。

HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。

response.png

按http头部分

HTTP 首部表示在HTTP请求或响应中的用来传递附加信息的字段,修改所传递的消息(或者消息主体)的语义,或者使其更加精确。消息首部不区分大小写,开始于一行的开头,后面紧跟着一个’:’和与之相关的值。字段值在一个换行符(CR)前或者整个消息的末尾结束。

自定专用消息头可通过'X-' 前缀来添加;但是这种用法被IETF在2012年6月发布的 RFC5548 中明确弃用,原因是其会在非标准字段成为标准时造成不便;其他的消息头在 IANA 注册表 中列出, 其原始内容在 RFC 4229 中定义。 此外,IANA 还维护着被提议的新HTTP 消息头注册表.

按照惯例,可以把消息首部分为几类,尽管这种划分不存在于任何一份规范文档中:

  • General header:可以应用于请求和响应中,但是与在消息主体中的数据无关。
  • Request header:含有与所要获取的资源或者客户端自身相关的附加信息。
  • Response header:含有与响应相关的附加信息,比如它的位置或者与服务器相关的信息(名称、版本号等)。
  • Entity header: 含有与消息主体相关的附加信息,比如长度或者MIME类型。

General header

通用首部指的是可以应用于请求和响应中,但是不能应用于消息内容自身的 HTTP header 。 取决于应用的上下文环境,通用首部可以是response 或者 request headers。但是不可以是 entity headers。

最常见的通用首部包括: Date, Cache-Control or Connection.

Request header

请求头可以被定义为:被用于http请求中并且和请求主体无关的那一类HTTP header。某些请求头如Accept, Accept-*, If-*允许执行条件请求。某些请求头如:Cookie, User-Agent 和Referer描述了请求本身以确保服务端能返回正确的响应。

并非所有出现在请求中的http首部都属于请求头,例如在 POST请求中经常出现的Content-Length实际上是一个代表请求主体大小的entity header,虽然你也可以把它叫做请求头。

此外,CORS定义了一个叫做 simple headers的集合,它是请求头集合的一个子集。如果某次请求是只包含simple headers的话,则被认为是简单请求,不会触发请求预检(preflight)。

Response header

响应头 可以定义为:被用于http响应中并且和响应消息主体无关的那一类 HTTP header。像Age, Location 和 Server都属于响应头,他们被用于描述响应。

并非所有出现在响应中的http header都属于响应头,例如Content-Length就是一个代表响应体消息大小的entity header,虽然你也可以把它叫做响应头。

下面展示了一个 GET请求的响应头。需要注意的是,严格来说Content-Encoding和Content-Type都是属于entity headers。

Entity header

实体报头HTTP header用来描述消息体内容。实体报头既可用于请求也可用于响应中。如Content-Length,Content-Language,Content-Encoding之类的报头都是实体报头。

尽管实体报头既非请求或响应报头,(由于它经常出现在请求头或响应头中)它们通常包含于此类概念中。

在下面例子中,Content-Length是一个实体报头,而Host和User-Agent是request headers(请求报头)

按处理方式分

消息头也可以根据代理对其的处理方式分为:

端到端消息头

这类消息头必须被传输到最终的消息接收者,也即,请求的服务器或响应的客户端。中间的代理服务器必须转发未经修改的端到端消息头,并且必须缓存它们。

逐跳消息头

这类消息头仅对单次传输连接有意义,不能通过代理或缓存进行重新转发。这些消息头包括 Connection, Keep-Alive, Proxy-Authenticate, Proxy-Authorization, TE, Trailer, Transfer-Encoding 及 Upgrade。注意,只能使用 Connection 来设置逐跳一般头。

按使用类别

AuthenticationSection

  • WWW-Authenticate: Defines the authentication method that should be used to gain access to a resource.
  • Authorization: Contains the credentials to authenticate a user agent with a server.
  • Proxy-Authenticate: Defines the authentication method that should be used to gain access to a resource behind a Proxy server.
  • Proxy-Authorization: Contains the credentials to authenticate a user agent with a proxy server.

CachingSection

  • Age: The time in seconds the object has been in a proxy cache.
  • Cache-Control: Specifies directives for caching mechanisms in both requests and responses.
  • Clear-Site-Data: Clears browsing data (e.g. cookies, storage, cache) associated with the requesting website.
  • Expires: The date/time after which the response is considered stale.
  • Pragma: Implementation-specific header that may have various effects anywhere along the request-response chain. Used for backwards compatibility with HTTP/1.0 caches where the Cache-Control header is not yet present.
  • Warning: A general warning field containing information about possible problems.

ConditionalsSection

  • Last-Modified: It is a validator, the last modification date of the resource, used to compare several versions of the same resource. It is less accurate than ETag, but easier to calculate in some environments. Conditional requests using If-Modified-Since and If-Unmodified-Since use this value to change the behavior of the request.
  • ETag: It is a validator, a unique string identifying the version of the resource. Conditional requests using If-Match and If-None-Match use this value to change the behavior of the request.
  • If-Match: Makes the request conditional and applies the method only if the stored resource matches one of the given ETags.
  • If-None-Match: Makes the request conditional and applies the method only if the stored resource doesn’t match any of the given ETags. This is used to update caches (for safe requests), or to prevent to upload a new resource when one is already existing.
  • If-Modified-Since: Makes the request conditional and expects the entity to be transmitted only if it has been modified after the given date. This is used to transmit data only when the cache is out of date.
  • If-Unmodified-Since: Makes the request conditional and expects the entity to be transmitted only if it has not been modified after the given date. This is used to ensure the coherence of a new fragment of a specific range with previous ones, or to implement an optimistic concurrency control system when modifying existing documents.
  • Vary: Determines how to match future request headers to decide whether a cached response can be used rather than requesting a fresh one from the origin server.

Connection managementSection

  • Connection: Controls whether the network connection stays open after the current transaction finishes.
  • Keep-Alive: Controls how long a persistent connection should stay open.

Content negotiationSection

  • Accept: Informs the server about the types of data that can be sent back. It is MIME-type.
  • Accept-Charset: Informs the server about which character set the client is able to understand.
  • Accept-Encoding: Informs the server about the encoding algorithm, usually a compression algorithm, that can be used on the resource sent back.
  • Accept-Language: Informs the server about the language the server is expected to send back. This is a hint and is not necessarily under the full control of the user: the server should always pay attention not to override an explicit user choice (like selecting a language in a drop down list).

ControlsSection

  • Expect: Indicates expectations that need to be fulfilled by the server in order to properly handle the request.

CookiesSection

  • Cookie: Contains stored HTTP cookies previously sent by the server with the Set-Cookie header.
  • Set-Cookie: Send cookies from the server to the user agent.

CORSSection

Learn more about CORS here.

  • Access-Control-Allow-Origin: Indicates whether the response can be shared.
  • Access-Control-Allow-Credentials: Indicates whether the response to the request can be exposed when the credentials flag is true.
  • Access-Control-Allow-Headers: Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.
  • Access-Control-Allow-Methods: Specifies the method or methods allowed when accessing the resource in response to a preflight request.
  • Access-Control-Expose-Headers: Indicates which headers can be exposed as part of the response by listing their names.
  • Access-Control-Max-Age: Indicates how long the results of a preflight request can be cached.
  • Access-Control-Request-Headers: Used when issuing a preflight request to let the server know which HTTP headers will be used when the actual request is made.
  • Access-Control-Request-Method: Used when issuing a preflight request to let the server know which HTTP method will be used when the actual request is made.
  • Origin: Indicates where a fetch originates from.
  • Timing-Allow-Origin: Specifies origins that are allowed to see values of attributes retrieved via features of the Resource Timing API, which would otherwise be reported as zero due to cross-origin restrictions.

Do Not TrackSection

  • DNT: Used for expressing the user’s tracking preference.
  • Tk: Indicates the tracking status that applied to the corresponding request.

DownloadsSection

  • Content-Disposition: Is a response header if the resource transmitted should be displayed inline (default behavior when the header is not present), or it should be handled like a download and the browser should present a ‘Save As’ window.

Message body informationSection

  • Content-Length: Indicates the size of the entity-body, in decimal number of octets, sent to the recipient.
  • Content-Type: Indicates the media type of the resource.
  • Content-Encoding: Used to specify the compression algorithm.
  • Content-Language: Describes the language(s) intended for the audience, so that it allows a user to differentiate according to the users’ own preferred language.
  • Content-Location: Indicates an alternate location for the returned data.

RedirectsSection

  • Location: Indicates the URL to redirect a page to.

Request contextSection

  • From: Contains an Internet email address for a human user who controls the requesting user agent.
  • Host: Specifies the domain name of the server (for virtual hosting), and (optionally) the TCP port number on which the server is listening.
  • Referer: The address of the previous web page from which a link to the currently requested page was followed.
  • Referrer-Policy: Governs which referrer information sent in the Referer header should be included with requests made.
  • User-Agent: Contains a characteristic string that allows the network protocol peers to identify the application type, operating system, software vendor or software version of the requesting software user agent. See also the Firefox user agent string reference.

Response contextSection

  • Allow: Lists the set of HTTP request methods support by a resource.
  • Server: Contains information about the software used by the origin server to handle the request.

Range requestsSection

  • Accept-Ranges: Indicates if the server supports range requests, and if so in which unit the range can be expressed.
  • Range: Indicates the part of a document that the server should return.
  • If-Range: Creates a conditional range request that is only fulfilled if the given etag or date matches the remote resource. Used to prevent downloading two ranges from incompatible version of the resource.
  • Content-Range: Indicates where in a full body message a partial message belongs.

HTTP1.0 rfc1945

方法3种 GET, HEAD, POST

GET

GET方法意味着检索由Request-URI标识的任何信息(以实体的形式)。 如果Request-URI引用数据生成过程,则生成的数据应作为响应中的实体而不是过程的源文本返回,除非该文本恰好是过程的输出。 如果请求消息包括If-Modified-Since头字段,则GET方法的语义变为“条件GET”。 条件GET方法请求仅在自If-Modified-Since标头给出的日期之后修改标识的资源时才传输,如第10.9节所述。 条件GET方法旨在通过允许刷新缓存实体而不需要多个请求或传输不必要的数据来减少网络使用。

HEAD

此方法通常用于测试超文本链接的有效性,可访问性和最近的修改。 没有类似于条件GET的“条件HEAD”请求。 如果HEAD请求包含If-Modified-Since标头字段,则应忽略该字段。

POST

所有HTTP / 1.0 POST请求都需要有效的Content-Length。 一个HTTP / 1.0服务器应该无法确定请求消息内容的长度时应该响应400(错误请求)消息。

应用程序不能缓存对POST请求的响应,因为应用程序无法知道服务器将在以后的某个请求中返回等效响应。

1.0 目录

  1. Introduction ………………………………………. 4
    1.1 Purpose ………………………………………. 4 Uniform Resource Identifier (URI)
    1.2 Terminology …………………………………… 4 666
    1.3 Overall Operation ……………………………… 6 6666
    1.4 HTTP and MIME …………………………………. 8
  2. Notational Conventions and Generic Grammar ……………. 8
    2.1 Augmented BNF …………………………………. 8
    2.2 Basic Rules …………………………………… 10
  3. Protocol Parameters ………………………………… 12
    3.1 HTTP Version ………………………………….. 12
    3.2 Uniform Resource Identifiers ……………………. 14
     3.2.1  General Syntax ................................ 14
     3.2.2  http URL ...................................... 15
    
    3.3 Date/Time Formats ……………………………… 15
    3.4 Character Sets ………………………………… 17
    3.5 Content Codings ……………………………….. 18
    3.6 Media Types …………………………………… 19
     3.6.1  Canonicalization and Text Defaults ............ 19
     3.6.2  Multipart Types ............................... 20
    
    3.7 Product Tokens ………………………………… 20
  4. HTTP Message ………………………………………. 21
    4.1 Message Types …………………………………. 21
    4.2 Message Headers ……………………………….. 22
    4.3 General Header Fields ………………………….. 23
  5. Request …………………………………………… 23
    5.1 Request-Line ………………………………….. 23
     5.1.1  Method ........................................ 24
     5.1.2  Request-URI ................................... 24
    
    5.2 Request Header Fields ………………………….. 25
  6. Response ………………………………………….. 25
    6.1 Status-Line …………………………………… 26
     6.1.1  Status Code and Reason Phrase ................. 26
    
    6.2 Response Header Fields …………………………. 28
  7. Entity ……………………………………………. 28
    7.1 Entity Header Fields …………………………… 29
    7.2 Entity Body …………………………………… 29
     7.2.1  Type .......................................... 29
     7.2.2  Length ........................................ 30
    
  8. Method Definitions …………………………………. 30
    8.1 GET ………………………………………….. 31
    8.2 HEAD …………………………………………. 31
    8.3 POST …………………………………………. 31
  9. Status Code Definitions …………………………….. 32
    9.1 Informational 1xx ……………………………… 32
    9.2 Successful 2xx ………………………………… 32
    9.3 Redirection 3xx ……………………………….. 34
    9.4 Client Error 4xx ………………………………. 35
    9.5 Server Error 5xx ………………………………. 37
  10. Header Field Definitions ……………………………. 37
    10.1 Allow ……………………………………….. 38
    10.2 Authorization ………………………………… 38
    10.3 Content-Encoding ……………………………… 39
    10.4 Content-Length ……………………………….. 39
    10.5 Content-Type …………………………………. 40
    10.6 Date ………………………………………… 40
    10.7 Expires ……………………………………… 41
    10.8 From ………………………………………… 42
    10.9 If-Modified-Since …………………………….. 42
    10.10 Last-Modified ………………………………… 43
    10.11 Location …………………………………….. 44
    10.12 Pragma ………………………………………. 44
    10.13 Referer ……………………………………… 44
    10.14 Server ………………………………………. 45
    10.15 User-Agent …………………………………… 46
    10.16 WWW-Authenticate ……………………………… 46
  11. Access Authentication ………………………………. 47
    11.1 Basic Authentication Scheme ……………………. 48
  12. Security Considerations …………………………….. 49
    12.1 Authentication of Clients ……………………… 49
    12.2 Safe Methods …………………………………. 49
    12.3 Abuse of Server Log Information ………………… 50
    12.4 Transfer of Sensitive Information ………………. 50
    12.5 Attacks Based On File and Path Names ……………. 51
  13. Acknowledgments
http status code 说明
200 成功
301 永久重定向
302 短暂重定向 303
307 短暂重定向, 不改方法 303
308 永久重定向, 不改方法 301
400 客户端错误,比如缺少参数,或者业务上还不允许的操作(提交包含未标注图片的数据集)等等
401 Unauthorized 未带登录token
403 Forbidden 带了token,但是没有权限(比如标注未分配给自己的数据集等等)
404 资源不存在
409 Conflict 资源重复
500 服务器错误(程序异常,数据库异常等等情况)

参考

全面分析前端的网络请求方式
HTTP最强资料大全 66
HTTP Headers MDN

1…456…14
Henry x

Henry x

this is description

133 日志
25 分类
135 标签
GitHub E-Mail
Links
  • weibo
© 2019 Henry x