TCP 和 UDP 都属于网络传输层协议,如果要构建高效的网络应用,就应该从传输层进行着手。但是对于经典的浏览器网页和服务端通信场景,如果单纯的使用更底层的传输层协议则会变得麻烦。
所以对于经典的B(Browser)S(Server)通信,基于传输层之上专门制定了更上一层的通信协议:HTTP,用于浏览器和服务端进行通信。由于 HTTP 协议本身并不考虑数据如何传输及其他细节问题,所以属于应用层协议。
Node 提供了基本的 http 和 https 模块用于 HTTP 和 HTTPS 的封装。
const http = require('http')
const server = http.createServer()
Server 实例
API | 说明 |
---|---|
Event:‘close’ | 服务关闭时触发 |
Event:‘request’ | 收到请求消息时触发 |
server.close() | 关闭服务 |
server.listening | 获取服务状态 |
请求对象
API | 说明 |
---|---|
request.method | 请求方法 |
request.url | 请求路径 |
request.headers | 请求头 |
request.httpVersion | 请求HTTP协议版本 |
响应对象
API | 说明 |
---|---|
response.end() | 结束响应 |
response.setHeader(name, value) | 设置响应头 |
response.removeHeader(name, value) | 删除响应头 |
response.statusCode | 设置响应状态码 |
response.statusMessage | 设置响应状态短语 |
response.write() | 写入响应数据 |
response.writeHead() | 写入响应头 |
使用 Node 构建 http 服务
Hello World
const http = require('http')
const hostname = '127.0.0.1'
const port = 3000
const server = http.createServer((req, res) => {
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain')
res.end('Hello World\n')
})
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`)
})
根据不同 url 处理不同请求
const http = require('http')
const hostname = '127.0.0.1'
const port = 3000
const server = http.createServer((req, res) => {
const url = req.url
if (url === '/') {
res.end('Hello World!')
} else if (url === '/a') {
res.end('Hello a!')
} else if (url === '/b') {
res.end('Hello b!')
} else {
res.statusCode = 404
res.end('404 Not Found.')
}
})
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`)
})
响应 HTML 内容
const http = require('http')
const fs = require('fs')
const hostname = '127.0.0.1'
const port = 3000
const server = http.createServer((req, res) => {
fs.readFile('./index.html', (err, data) => {
if (err) {
throw err
}
res.statusCode = 200
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(data)
})
// res.end(`
// <h1>Hello World!</h1>
// <p>你好,世界!</p>
// `)
})
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`)
})
处理页面中的静态资源
const http = require('http')
const fs = require('fs')
const hostname = '127.0.0.1'
const port = 3000
const server = http.createServer((req, res) => {
const url = req.url
if (url === '/') {
fs.readFile('./index.html', (err, data) => {
if (err) {
throw err
}
res.statusCode = 200
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(data)
})
} else if (url === '/assets/css/main.css') {
fs.readFile('./assets/css/main.css', (err, data) => {
if (err) {
throw err
}
res.statusCode = 200
res.setHeader('Content-Type', 'text/css; charset=utf-8')
res.end(data)
})
} else if (url === '/assets/js/main.js') {
fs.readFile('./assets/js/main.js', (err, data) => {
if (err) {
throw err
}
res.statusCode = 200
res.setHeader('Content-Type', 'text/javascript; charset=utf-8')
res.end(data)
})
} else {
res.statusCode = 404
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end('404 Not Found.')
}
})
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`)
})
统一处理页面中的静态资源
const http = require('http')
const fs = require('fs')
const mime = require('mime')
const path = require('path')
const hostname = '127.0.0.1'
const port = 3000
const server = http.createServer((req, res) => {
const url = req.url
if (url === '/') {
fs.readFile('./index.html', (err, data) => {
if (err) {
throw err
}
res.statusCode = 200
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(data)
})
} else if (url.startsWith('/assets/')) {
// /assets/js/main.js
fs.readFile(`.${url}`, (err, data) => {
if (err) {
res.statusCode = 404
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end('404 Not Found.')
}
const contentType = mime.getType(path.extname(url))
res.statusCode = 200
res.setHeader('Content-Type', contentType)
res.end(data)
})
} else {
res.statusCode = 404
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end('404 Not Found.')
}
})
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`)
})
使用模板引擎处理动态页面
假如我们有一份数据 todos
需要展示到页面中
const todos = [
{ title: '吃饭', completed: false },
{ title: '睡觉', completed: true },
{ title: '打豆豆', completed: false }
]
如何将一组数据列表展示到一个页面中,最简单的方式就是字符串替换,但是如果有不止一份数据需要展示到页面中的时候就会变得非常麻烦,所以前人将此种方式整合规则之后开发了我们常见的模板引擎。
例如我们经常在网页源码中看到下面这样一段代码
<ul>
<% todos.forEach(function (item) { %>
<li><%= item.title %></li>
<% }) %>
</ul>
或者是
<ul>
{{ each todos }}
<li>{{ $value.title }}</li>
{{ /each }}
</ul>
无论如何,我们看到的这些语法都在模板引擎所指定的一些规则,目的就是让我们可以非常方便的在网页中进行字符串替换以达到动态网页的效果。
在 Node 中,有很多优秀的模板引擎,它们大抵相同,但都各有特点
基本使用
const template = require('art-template')
// const ret = template.render('Hello {{ message }}', {
// message: 'World'
// })
const ret = template.render(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<h1>Hello {{ message }}</h1>
<ul>
{{ each todos }}
<li>{{ $value.title }} <input type="checkbox" {{ $value.completed ? 'checked' : '' }} /></li>
{{ /each }}
</ul>
</body>
</html>
`, {
message: 'World',
todos: [
{ title: '吃饭', completed: false },
{ title: '睡觉', completed: true },
{ title: '打豆豆', completed: false }
]
})
console.log(ret)
结合 http 服务渲染页面
const http = require('http')
const template = require('art-template')
const fs = require('fs')
const hostname = '127.0.0.1'
const port = 3000
const server = http.createServer((req, res) => {
const url = req.url
if (url === '/') {
fs.readFile('./index2.html', (err, data) => {
if (err) {
throw err
}
const htmlStr = template.render(data.toString(), {
message: '程序员',
todos: [
{ title: '吃饭', completed: true },
{ title: '睡觉', completed: true },
{ title: '打豆豆', completed: false }
]
})
res.statusCode = 200
res.setHeader('Content-Type', 'text/html')
res.end(htmlStr)
})
}
})
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`)
})
第三方 HTTP 服务框架
更多优秀的第三方资源 awesome node.js