
八、Node-http模块
一、Web服务器
当应用程序(客户端)需要某一个服务器时,可以向一台服务器,通过http请求获取到这个资源,提供资源的这个服务器,就是一个Web服务器。
目前有很多开源的Web服务器:Nginx、Apache(静态)、Apache Tomcat(静态、动态)、Node.js
Web服务器初体验
1 | const http = require('http'); |
创建服务器
创建服务器对象,我们是通过 createServer 来完成的
- http.createServer会返回服务器的对象;
- 底层其实使用直接 new Server 对象
1 | const http = require('http'); |
上面我们已经看到,创建Server时会传入一个回调函数,这个回调函数在被调用时会传入两个参数:
- req:request请求对象,包含请求相关的信息;
- res:response响应对象,包含我们要发送给客户端的信息;
Server通过listen方法来开启服务器,并且在某一个主机和端口上监听网络请求:
- 也就是当我们通过 ip:port的方式发送到我们监听的Web服务器上时;
- 我们就可以对其进行相关的处理;
listen函数有三个参数:
端口port: 可以不传, 系统会默认分配端口
主机host: 通常可以传入localhost、ip地址127.0.0.1、或者ip地址0.0.0.0,默认是0.0.0.0;
- localhost:本质上是一个域名,通常情况下会被解析成127.0.0.1;
127.0.0.1:回环地址(Loop Back Address),表达的意思其实是我们主机自己发出去的包,直接被自己接收;
- 正常的数据库包经常 应用层 - 传输层 - 网络层 - 数据链路层 - 物理层 ;
- 而回环地址,是在网络层直接就被获取到了,是不会经常数据链路层和物理层的;
- 比如我们监听 127.0.0.1时,在同一个网段下的主机中,通过ip地址是不能访问的;
- 0.0.0.0:
- 监听IPV4上所有的地址,再根据端口找到不同的应用程序;
- 比如我们监听 0.0.0.0时,在同一个网段下的主机中,通过ip地址是可以访问的;
- 正常的数据库包经常 应用层 - 传输层 - 网络层 - 数据链路层 - 物理层 ;
回调函数:服务器启动成功时的回调函数;
request对象
在向服务器发送请求时,我们会携带很多信息,比如:
- 本次请求的URL,服务器需要根据不同的URL进行不同的处理;
- 本次请求的请求方式,比如GET、POST请求传入的参数和处理的方式是不同的;
- 本次请求的headers中也会携带一些信息,比如客户端信息、接受数据的格式、支持的编码格式等;
- 等等…
这些信息,Node会帮助我们封装到一个request的对象中,我们可以直接来处理这个request对象:
1 | const http = require('http'); |
URL的处理
客户端在发送请求时,会请求不同的数据,那么会传入不同的请求地址:
服务器端需要根据不同的请求地址,作出不同的响应:
1 | const http = require('http'); |
URL的解析
那么如果用户发送的地址中还携带一些额外的参数呢?
- http://localhost:8000/login?name=why&password=123;
- 这个时候,url的值是 /login?name=why&password=123;
我们如何对它进行解析呢?使用内置模块url
1 | const url = require('url'); |
这样就方便操作了:
1 | const http = require('http'); |
那我们想要获取参数呢,要自己进行截取吗?
可以使用querystring模块
1 | const http = require('http'); |
method相关的处理
在Restful规范(设计风格)中,我们对于数据的增删改查应该通过不同的请求方式:
- GET:查询数据;
- POST:新建数据;
- PATCH:更新数据;
- DELETE:删除数据;
所以,我们可以通过判断不同的请求方式进行不同的处理。
- 比如创建一个用户:
- 请求接口为 /users;
- 请求方式为 POST请求;
- 携带数据 username和password
一般在提交表单也就是用户名,密码的时候,我们一般采用POST方法,并且相关参数放到body中,那么服务器怎么获取这些表单信息呢?
用postman来模拟发送数据:
1 | const http = require('http'); |
注意,想在服务器得到表单数据,要使用req.on() 方法,因为这其实就是输入流。(Stream)
可以看到此时打印结果:
1 | <Buffer 7b 0a 09 22 75 75 73 65 72 6e 61 6d 65 22 20 3a 20 22 6a 61 63 6b 22 2c 0a 09 22 70 61 73 73 77 6f 72 64 22 20 3a 20 22 32 30 22 0a 7d> |
转换方法1 data.toString()
转换方法二:req.setEncoding(‘utf-8’) 如果数据是字符形式
此时打印结果:
1 | { |
这是个字符串类型,想要准确拿到需要进行截取,所以把转成对象形式:
1 | console.log(JSON.parse(data.toString())); |
这样就方便后续的操作了。
将JSON字符串格式转成对象类型,通过JSON.parse方法即可。
headers
在request对象的header中也包含很多有用的信息,客户端会默认传递过来一些信息
1 | const http = require('http'); |
使用浏览器去访问:
1 | { |
使用postman,携带参数访问 :
1 | { |
content-type是这次请求携带的数据的类型:
- application/json表示是一个json类型;
- text/plain表示是文本类型;
- application/xml表示是xml类型;
- multipart/form-data表示是上传文件
content-length:
- 文件的大小和长度
keep-alive:
- http是基于TCP协议的,但是通常在进行一次请求和响应结束后会立刻中断;
- 在http1.0中,如果想要继续保持连接:
- 浏览器需要在请求头中添加 connection: keep-alive;
- 服务器需要在响应头中添加 connection:keey-alive;
- 当客户端再次放请求时,就会使用同一个连接,直接一方中断连接;
- 在http1.1中,所有连接默认是 connection: keep-alive的;
- 不同的Web服务器会有不同的保持 keep-alive的时间;
- Node中默认是5s中;
accept-encoding:
- 告知服务器,客户端支持的文件压缩格式,比如js文件可以使用gzip编码,对应 .gz文件;
accept:
- 告知服务器,客户端可接受文件的格式类型;
user-agent:
- 客户端相关的信息;
response对象
如果我们希望给客户端响应的结果数据,可以通过两种方式:
- res.write方法:这种方式是直接写出数据,但是并没有关闭流;
- res.end方法:这种方式是写出最后的数据,并且写出后会关闭流;
如果我们没有调用 end,客户端将会一直等待结果:
- 所以客户端在发送网络请求时,都会设置超时时间
状态码
Http状态码(Http Status Code)是用来表示Http响应状态的数字代码:
Http状态码非常多,可以根据不同的情况,给客户端返回不同的状态码;
常见的状态码是下面这些:
1 | res.writeHead(401); |
响应头文件
返回头部信息,主要有两种方式:
- res.setHeader:一次写入一个头部信息;
- res.writeHead:同时写入header和status;
1 | res.setHeader('Content-Type', 'application/json;charset=utf8'); |
Header设置 Content-Type有什么作用呢?
- 默认客户端接收到的是字符串,客户端会按照自己默认的方式进行处理;
http请求
Node既可以当作服务器,也可以发送http请求
axios库可以在浏览器中使用,也可以在Node中使用:
- 在浏览器中,axios使用的是封装xhr;
- 在Node中,使用的是http内置模块;
1 | http.get('http://localhost:8000', (result) => { |
如果是别的类型的请求:
1 | const req = http.request( |
注意:
- http.get()自动结束请求
- 但是别的方式的要手动end()
文件上传
文件上传一般采用的格式为form-data
但是如果上传一张图片,服务器不能直接把所有数据都保存起来,这样会得不到正确图片,因为上传的整个数据里还包括其他信息,比如文件描述等等
所有我们需要进行截取拼接等等,拿到正确的图片编码,保存起来,才能得到正确的图片。
比如我们使用postman来上传一张图片:
代码:
1 | const http = require('http'); |
好家伙 此时开启服务器,上传文件
总之就是这种方法是错误的,得不到正确图片
想要原生正确上传,那就截取拼接等等
可以自己尝试下。