Node全解05静态服务器

HTTP模块

一些有用的工具

node-dev

  • 当文件更新时自动重启 node
  • 避免每次改完代码都要重新运行的麻烦
  • 不宜在生产环境使用

ts-node

  • 让 node 支持直接运行 TS代码
  • 不宜在生产环境使用

ts-node-dev

  • 这个工具结合了上面两个工具
  • 可以用 TS 开发 Node.js 程序,且会自动重启
  • 不宜在生产环境使用,非常适合用来学习

你可以这样安装 yarn global add ts-node-dev

一些有用的工具2

WebStorm自动格式化

  • ReformatCode 设置为 Ctrl + L 或者其他键位
  • 可以快速帮你格式化 TS / JS 代码

WebStorm自动写代码

  • Complete Current Statement 设置为 Ctrl + Enter 或者其他键位
  • Show Context Actions 设置为 Alt + Enter 或者其他键位

VSCode配置

  • 开启自动保存 autosave
  • format on save
  • format

一些有用的工具3

WebStorm Git配置

  • 选择设置 =》 搜索 git => path选中你安装的git路径

VSCode配置

  • 设置 搜索 settings 打开设置 json
1
2
3
4
{
"git.enabled": true,
"git.path": "/usr/bin/git"
}

Curl命令

  • get: curl -v url
  • post: curl -v -d "name=xxx" url
  • 设置请求头: -H 'Content-Type:application/json'
  • 设置动词: -X PUT
  • JSON请求: curl -d '{"name":"bob"}' -H 'Content-Type:pplication/json' url

创建项目

  • 项目目录 node-server-demo
  • 由于我们写 ts 别忘了安装 yarn global add ts-node-dev
  • yarn init -y
  • yarn add –dev @types/node 安装 node 声明文件
  • 新建 index.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import * as http from 'http';

    const server = http.createServer();

    server.on('request', (request, response) => {
    console.log('有人请求了')
    response.end("hi");
    })

    server.listen(8888);
  • 启动服务ts-node-dev index.ts

  • 发请求curl http://localhost:8888

server 是什么东西

http.Server 类的实例

  • 根据文档能知道 http.createServer 的返回值类型
  • 根据文档知道 server 拥有几个事件和方法
  • 其中目前能用上的 只有 request事件 和 listen()

继承自net.Server

  • 根据文档知道继承关系
  • 看到 net.Server 又有几个方法
  • 其中也就 error事件 和 address() 能用上

一些技巧

  • 有的时候 点不出来东西,那是因为 类型声明文件不够匹配可能写的是 any
  • 所以你可以通过 加类型声明 来达到 “点出来提示”
  • 如果你用的是 vscode 请安装 auto import

index.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import * as http from 'http';
import { IncomingMessage, ServerResponse } from 'http';

const server = http.createServer();

server.on('request', (request: IncomingMessage, response: ServerResponse) => {
console.log(request.constructor);
console.log(response.constructor);
// 得知它是什么 然后声明它的类型 , 这样你在 request. 的时候才会有提示
console.log(request.httpVersion);
console.log(request.url);
response.end("hi");
})

server.listen(8888);

如何获取请求体的内容

  • request.on('data', fn)
  • request.on('end', fn)
  • curl -v -d "name=aaa" http://localhost:8888

有个问题:这个上传方式就算用户上传1G的东西都可以拿到,因为你是一小段一小段拿到然后拼起来,只要服务器内存够。就可以处理上传的内容

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
import * as http from 'http';
import { IncomingMessage, ServerResponse } from 'http';

const server = http.createServer();

server.on('request', (request: IncomingMessage, response: ServerResponse) => {
console.log(request.constructor);
console.log(response.constructor);
// 得知它是什么 然后声明它的类型 , 这样你在 request. 的时候才会有提示
console.log('request.httpVersion')
console.log(request.httpVersion);
console.log('request.url');
console.log(request.url);
console.log('request.headers');
console.log(request.headers);
console.log('request.method');
console.log(request.method);

// 获取请求体
const array = [];
request.on('data', (chunk) => {
array.push(chunk)
})
request.on('end', () => {
const body = Buffer.concat(array).toString();
console.log(body)
response.end("hi");
})

})

server.listen(8888);

request,response 是啥

找类

  • 根据文档, request 是 http. IncomingMessage 的实例
  • 根据文档, response 是 http. ServerResponse 的实例

Request

  • 拥有 headers, method , url 等属性
  • 从 stream.Readable 类继承了 data/end/error 事件
  • 为什么不直接拿到请求的消息体呢? 根TCP相关

Response

  • 拥有 getHeader/setHeader/end/write 等方法
  • 拥有 statusCode 属性,可读可写

    1
    2
    3
    4
    5
    6
    7
    8
    response.statusCode = 404;
    // 设置响应头
    response.setHeader('x-frank', 'I am frank')
    response.write('1\n');
    response.write('2\n');
    response.write('3\n');
    response.write('4\n');
    response.end(); // 如果你设置了 404 就 end里不传任何东西
    • 你也可以写回一个 image 但是要设置文件头标示它是一个 image
  • 继承了 Stream,目前用不上

项目1 根据url返回不同类型文件

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

import * as http from 'http';
import { IncomingMessage, ServerResponse } from 'http';
import * as fs from 'fs';
import * as p from 'path';

const server = http.createServer();
const publicDir = p.relative(__dirname, 'public');

server.on('request', (request: IncomingMessage, response: ServerResponse) => {
const { method, url, headers } = request;
switch (url) {
case '/index.html':
response.setHeader('Content-Type', 'text/html;charset=utf-8');
fs.readFile(p.resolve(publicDir, 'index.html'), (error, data) => {
if (error) throw error;
response.end(data.toString());
})
break;
case '/style.css':
response.setHeader('Content-Type', 'text/css;charset=utf-8');
fs.readFile(p.resolve(publicDir, 'style.css'), (error, data) => {
if (error) throw error;
response.end(data.toString());
})
break;
case '/main.js':
response.setHeader('Content-Type', 'text/javascript;charset=utf-8');
fs.readFile(p.resolve(publicDir, 'main.js'), (error, data) => {
if (error) throw error;
response.end(data.toString());
})
break;
}
})

server.listen(8888);

项目2 处理查询参数

1
2
3
4
5
6
7
8
9
import * as url from 'url';

server.on('request', (request: IncomingMessage, response: ServerResponse) => {
const { method, url: path, headers } = request;
const { pathname, search } = url.parse(path);
switch (pathname) {
...
}
});

项目3 匹配任意文件

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
40
// 匹配任意文件
import * as http from 'http';
import { IncomingMessage, ServerResponse } from 'http';
import * as fs from 'fs';
import * as p from 'path';
import * as url from 'url';

const server = http.createServer();
const publicDir = p.relative(__dirname, 'public');

server.on('request', (request: IncomingMessage, response: ServerResponse) => {
const { method, url: path, headers } = request;
const { pathname, search } = url.parse(path);

// /index.html ==> index.html
let filename = pathname.substr(1);
if (filename === '') {
filename = 'index.html'
}
response.setHeader('Content-Type', 'text/html;charset=utf-8');
fs.readFile(p.resolve(publicDir, filename), (error, data) => {
if (error) {
if (error.errno === -4058) {
response.statusCode = 404;
response.end('你要的文件不存在啊!!!');
} else if (error.errno === -4068) {
// http://localhost:8888/xxx
response.statusCode = 403;
response.end('无权限查看目录内容');
} else {
response.statusCode = 500;
response.end('服务器繁忙,请稍后再试');
}
} else {
response.end(data.toString());
}
})
})

server.listen(8888);

项目4 处理非get请求 添加缓存文件时间

1
2
3
4
5
6
7
8
9
10
if (method !== 'GET') {
response.statusCode = 200;
response.setHeader('Content-Type', 'text/html;charset=utf-8');
response.end("这是一个假的响应");
return;
}

...

response.setHeader('Cache-Control', `public,max-age=${cacheAge}`);

免费用 WebStorm的途径

申请条件

  • 开源项目的贡献者
  • 你的项目是开源的 如MIT
  • 你的项目是不盈利的
  • 你的项目至少活跃3个月
  • 你定期发布更新的版本
  • 满足条件就 点击申请按钮

填写材料

  • 邮箱地址一定不要错
  • No. of required licenses 填 1
  • 项目描述用 google 翻译即可
  • 正版 JetBrains 全家桶唾手可得,价值$649/年

项目地址

https://github.com/slTrust/node-server-demo