常用curl指令
- 模拟http请求
curl -v http://localhost:3000
- 查看端口占用
lsof -i:3000
- 杀死进程
kill -9 14337
PID:14337
nodemon
http模块
一个服务器例子
let http = require('http')
let url = require('url')
http.createServer((req, res) => {
let { pathname, query } = url.parse(req.url, true)
console.log('请求来自', pathname, query)
let arr = []
req.on('data', chunk => arr.push(chunk))
req.on('end', () => {
res.statusCode = 200
let postdata = Buffer.concat(arr).toString()
switch (req.headers['content-type']) {
case 'text/plain':
res.setHeader('Content-Type', 'text/plain;charset=utf-8')
res.end(postdata)
break
case 'application/json':
res.setHeader('Content-Type', 'application/json;charset=utf-8')
postdata = JSON.parse(postdata)
postdata.name = '我是服务器'
res.end(JSON.stringify(postdata))
break
case 'application/x-www-form-urlencoded':
res.setHeader('Content-Type','application/json;charset=utf-8');
postdata = url.parse('?' + postdata, true)
res.end(JSON.stringify(postdata.query))
break;
}
})
}).listen(3000, () => {
console.log('监听3000成功')
})
headers下属性 content-type 在设置(setHeader)时首字母大写,主要有这几种类别
- text/plain
- application/json
- application/x-www-form-urlencoded
在设置content-type时,通常加上编码
// 使用http的request方法,模拟一个请求
let http = require('http')
http.request({
host: 'localhost',
port: 3000,
path: '/list/?id=test',
method: 'POST',
headers: {
'Content-Type':'application/x-www-form-urlencoded',
type: 'fake'
}
}, res => {
let arr = []
res.on('data', chunk => arr.push(chunk))
res.on('end', () => {
let str = Buffer.concat(arr).toString();
console.log('服务器说:', str)
})
}).end('say=我是客户端')
实现一个简单的静态服务器
- 对不同类型文件做不同处理
- 对表态资源和接口分别处理
- 接口有跨域支持
- 有缓存机制
- 支持压缩
let http = require('http')
let url = require('url')
let fs = require('fs')
let fsp = fs.promises
let path = require('path')
let mime = require('mime')
let crypto = require('crypto')
let zlib = require('zlib')
class MyServer {
constructor (port) {
http.createServer(this.RequestListener.bind(this))
.listen(port, () => console.log('_____开始监听' + port))
}
async RequestListener (req, res) {
let { pathname, query } = url.parse(req.url, true)
console.log('访问', pathname)
// 处理接口,包含处理跨域
if (pathname.includes('/api') && ['POST','OPTIONS'].includes(req.method)) {
return this.handlerApi(req, res)
}
try {
pathname = path.join(__dirname, pathname)
let stat = await fsp.stat(pathname)
if (stat.isDirectory()) {
pathname = path.join(pathname, 'index.html')
await fsp.access(pathname)
}
// 处理缓存
if (this.handlerCache(req, res, stat)) {
res.statusCode = 304
res.end()
} else {
this.sendFile(pathname, res) // 发送文件
}
} catch (e) {
this.sendError(e, res) // 处理错误
}
}
handlerCache (req, res, stat) {
// res.setHeader('Cache-Control', 'max-age=10') // 非首页,10s强制缓存
// res.setHeader('Expires', new Date(new Date().getTime() + 10000)) // 老版浏览器10s强制缓存
// res.setHeader('Cache-Control', 'private') // 客户端自己缓存
res.setHeader('Cache-Control', 'no-cache') // 对比缓存,含首页
// 最后修改时间
let last_edit_time = stat.mtimeMs
res.setHeader('Last-Modified', last_edit_time)
if (last_edit_time != req.headers['if-modified-since']) return false
// Etag
let etag = [stat.mtimeMs, stat.ctimeMs, stat.size].join('')
etag = crypto.createHash('md5').update(etag).digest('base64')
res.setHeader('Etag', etag)
if (etag != req.headers['if-none-match']) return false
return true
}
handlerApi (req, res) {
// 解决跨域
res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
// res.setHeader('Access-Control-Allow-Methods', 'DELETE')
// 跨域请求有自定义header,需要加:
res.setHeader('Access-Control-Allow-Headers', "myheader")
// 跨域请求时,允许带cookie
res.setHeader('Access-Control-Allow-Credentials', true)
// 跨域请求后,30s内不用再OPTIONS
res.setHeader('Access-Control-Max-Age', 30)
res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.setHeader('Set-Cookie', 'name=mycookie')
res.end('{"detail": "我是接口"}')
return false
}
async sendFile (pathname, res) {
// gzip压缩
let transform_rar = null
if (req.headers['accept-encoding'].includes('gzip')) {
res.setHeader('Content-Encoding','gzip');
transform_rar = zlib.createGzip()
}
else if (req.headers['accept-encoding'].includes('deflate')) {
res.setHeader('Content-Encoding','defalte');
transform_rar = zlib.createDeflate()
}
res.setHeader('Content-Type', mime.getType(pathname) + ';charset=utf-8')
if (transform_rar)
fs.createReadStream(pathname).pipe(transform_rar).pipe(res)
else
fs.createReadStream(pathname).pipe(res)
}
async sendError (e, res) {
res.statusCode = '404'
res.setHeader('Content-Type', 'text/html;charset=utf-8')
res.end('文件不存在')
}
}
new MyServer(3000)
new MyServer(5000) // 5000用于实验跨域访问
静态资源有index.html和index.js两个,js由html引入
// index.js
// 创建一个跨域访问
let xhr = new XMLHttpRequest();
xhr.open('POST', 'http://localhost:5000/temp/api')
xhr.responseType = 'json'
xhr.setRequestHeader('myheader', 'lost')
xhr.withCredentials = true // 跨域请求时,带上cookie
xhr.onload = function () {
console.log(xhr.response)
}
xhr.send()
Http请求、响应头整理
获取使用headers['']关键字小写,设置使用setHeader()关键字首字母大写
请求:
-
content-type
请求类型和编码,带boundary=----为formdata文件上传 -
if-modified-since
对Last-Modified的带回 -
if-none-match
对Etag的带回 -
accept-encoding
浏览器支持的压缩格式 -
accept-language
浏览器默认语言,和它们的权重 -
referer | referrer
发请求的host -
user-agent
浏览器内核信息
响应:
-
Content-Type
返回类型和编码 -
Cache-Control
缓存机制 -
Expires
(old)缓存机制 -
Last-Modified
与no-cache缓存配合使用 -
Etag
与no-cache缓存配合使用 -
Access-Control-Allow-Origin
处理跨域 -
Access-Control-Allow-Headers
跨域请求有自定义header -
Access-Control-Allow-Credentials
跨域请求时,允许带cookie -
Access-Control-Max-Age
跨域请求后,30s内不用再OPTIONS -
Set-Cookie
服务器端设置cookie,参数可接受数组,获取通过req.headers.cookie -
Content-Encoding
压缩格式 -
Location
重定向请求 -
Content-Disposition
响应为下载,值为attachment;filename=xxx
.
.
使用包:http-server快速搭建静态文件服务器
http-proxy快速搭建一个反向代理
querystring格式化get参数或cookie
网友评论