网站首页 > 文章精选 正文
搭建服务器
如果去查看 express.js 或是 koa.js 的源码,会发现它们底层都是使用 node.js 的 http 模块来搭建的服务。本篇文章就介绍一下,我们自己如何通过 http 模块,仅通过 3 步,来实现一个简单的服务器的搭建:
1. 导入 http
const http = require('http')
复制代码
2. 创建服务器
使用 http.createServer() 创建一个(可以多次调用创建多个)服务器 server:
const server = http.createServer((req, res) => {
console.log('客户端发送了请求')
res.end('hello juejin')
})
复制代码
createServer() 里可以传入一个回调函数,当服务器被请求时触发,该回调会被传入 2 个参数,一个是请求对象 req(request),一个是响应对象 res(response),它们两是基于 stream(流)实现的。如果查看 createServer 的类型声明文件,可以看到 req 的类型是 IncomingMessage,res 的类型为 ServerResponse:
// http.d.ts
function createServer<
Request extends typeof IncomingMessage = typeof IncomingMessage,
Response extends typeof ServerResponse = typeof ServerResponse,
>(requestListener?: RequestListener<Request, Response>): Server<Request, Response>;
复制代码
而 IncomingMessage 继承自 stream.Readable:
// http.d.ts
class IncomingMessage extends stream.Readable {
// ...
}
复制代码
ServerResponse 继承自 OutgoingMessage:
// http.d.ts
class ServerResponse<Request extends IncomingMessage = IncomingMessage> extends OutgoingMessage<Request> {
// ...
}
复制代码
而 OutgoingMessage 又继承自 stream.Writable:
// http.d.ts
class OutgoingMessage<Request extends IncomingMessage = IncomingMessage> extends stream.Writable {
// ...
}
复制代码
正因为 res 本质上是可写流,所以我们使用 res.end('hello juejin') 来向客户端返回数据,当然也可以使用 res.write() ,但最后还是需要使用 res.end() 来关闭流,而不能像一般的可写流那样使用 res.close()。如果不关闭流,客户端的请求又没有设置响应时间,请求就会一直持续。
req 对象
如果我们往浏览器地址栏输入的为 "localhost:3010/login",我们可以通过req 对象,来获取本次请求的路径,请求方法和请求头等:
const server = http.createServer((req, res) => {
console.log(req.url)
console.log(req.method)
console.log(req.headers)
res.end('hello juejin')
})
复制代码
打印结果如下:
res 对象
我们除了能用 res.end() 来向客户端返回数据,还可以用 res.statusCode 或 res.writeHead() 来设置HTTP 响应状态码:
const server = http.createServer((req, res) => {
// ... 省略部分代码
req.on('end', () => {
res.statusCode = 201
// res.writeHead(201)
res.end('hello juejin')
})
})
复制代码
res.writeHead() 还可以传入其它参数:
比如,当我们返回的是中文时 res.end('你好 掘金!'),浏览器会显示乱码:
我们可以向 res.writeHead() 传入响应头设置,通过 content-type 来告诉浏览器我们传输的内容的类型与编码方式:
res.writeHead(201, {
'content-type': 'text/plain;charset=utf8;'
})
res.end('你好 掘金!')
复制代码
也可以直接通过 res.setHeader() 来设置响应头:
res.setHeader('content-type', 'text/plain;charset=utf8;')
复制代码
3. 指定要监听的端口号
server.listen(3010, () => {
console.log('服务器开启')
})
复制代码
端口号可以省略,操作系统会自动分配一个,自己配置则最好是大于 1024,并小于 65535。因为小于 1024 的可能已经被分配给某些特殊服务了,比如 80 端口就是为 HTTP 开放的,属于浏览网页服务默认的端口号。大于 65535 则会超出 2 字节所能表示的最大数,而一般操作系统都是用 2 字节来表示端口号的。
server.listen() 还可以传入 hostname 作为第二个参数,上例中我们没有传,则为默认的 '0.0.0.0'。我们也可以传入域名 'localhost' 或 localhost 解析得到的 ip '127.0.0.1',但是请注意,当 host 为 '127.0.0.1' 时,同一个网段下的其它主机将无法通过 ip 地址访问服务器,因为 127.0.0.1 是一个回环地址,其请求流程不会经过数据链路层和物理层。
server.listen(3010, 'localhost')
复制代码
做完上述 3 个步骤,当我们在浏览器输入 localhost:3010,就可以在页面看到返回了 "hello juejin":
而命令行则会打印两遍 "客户端发送了请求":
这是因为浏览器还会去请求 favicon.ico:
请求参数的解析
GET 的 query 参数
GET 请求的 query 参数可以通过 req.url 获取,比如请求为 "localhost:3010/list?name=jay&age=22",执行下面的代码:
const http = require('http')
const server = http.createServer((req, res) => {
console.log(req.url)
res.end('hello juejin')
})
复制代码
结果为字符串 "/list?name=jay&age=22",如果想转成对象方便获取,可以先使用 url 模块的 parse 方法:
const url = require('url')
const urlObj = url.parse(req.url)
console.log(urlObj)
复制代码
传入 req.url 获取一个 url 对象:
然后通过 querystring 模块的 parse 方法获取 query 对象:
const qs = require('querystring')
const queryString = urlObj.query
const queryObj = qs.parse(queryString)
console.log(queryObj) // { name: 'jay', age: '22' }
复制代码
请注意,这里用到的 url.parse() 和 querystring 模块都是旧版的 API,但使用起来比较方便,比如若是使用官方文档推荐的 URLSearchParams 替换 qs.parse(),就需要像下面这样获取请求参数:
const mySearchParams = new URLSearchParams(queryString)
for (const [key, value] of mySearchParams) {
console.log(key, value)
}
复制代码
执行结果如下:
POST 的 body 参数
前面说了 req 本质上为可读流,其是通过监听 'data' 事件获取的 POST 请求里 body 携带的普通 json 数据:
req.on('data', data => {
console.log(data)
const dataObj = JSON.parse(data)
console.log(dataObj)
})
复制代码
打印结果为:
因为可读流的 encoding 默认为 null,所以得到的 data 为 buffer 对象,如果想让 data 为字符串可以通过可读流的 setEncoding() 方法:
req.setEncoding('utf8')
req.on('data', data => {
console.log(data)
})
复制代码
发送 HTTP 请求
http 模块不仅可以用于搭建服务器,还可以用来发送 HTTP 请求。
GET 请求
发送 get 请求可以直接使用 http.get(),然后传入请求地址,返回的数据通过回调函数的 res 接收,res 是可读流,通过监听 'data' 事件获取数据,并使用 setEncoding 方法设置了字符编码 :
const http = require('http')
http.get('http://localhost:3010', res => {
res.setEncoding('utf8')
res.on('data', data => console.log(data))
})
复制代码
POST 请求
post 请求就没有 http.post 这样的方法了,而是需要使用 http.request(),然后传入配置指定请求方法,数据的接收同样是在回调函数中,只不过下例中没有设定字符编码而是直接使用了 toString() 方法 :
const req = http.request(
{
method: 'POST',
hostname: 'localhost',
port: 3010
},
res => res.on('data', data => console.log(data.toString()))
)
req.end()
复制代码
请注意,http.request() 的返回值,也就是 req 本质上是一个可写流,可以写入请求正文,但即使没写,也需要在最后调用 req.end() 来表示请求结束了。
猜你喜欢
- 2025-06-10 第五天快http头部信息注入cooik(http 头部信息)
- 2025-06-10 HttpUrlConnection发送url请求(后台springmvc)
- 2025-06-10 你看过的动画片没有一个能逃过这10大定律,不信来试试
- 2025-06-10 如何给你的网站申请一个免费SSL,让HTTP变HTTPS
- 2025-06-10 前端常见面试 - 请求篇(前端面试经典问题)
- 2025-06-10 地球的关键瞬间(地球的k)
- 2025-06-10 09《Nginx 入门教程》Nginx 的 Http 模块介绍(下)
- 2025-06-10 使用Python并发执行HTTP请求(python并发处理)
- 2025-06-10 HTTP 响应状态码你知道多少?(http响应状态码的含义)
- 2025-06-10 Python3 新一代Http请求库Httpx使用(详情版)
- 最近发表
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 计算机网络的拓扑结构是指() (45)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)
- mysql数据库面试题 (57)