基本使用
import http from 'http'
// 创建本地服务器接收数据
const server = http.createServer((req, res) => {
console.log(req.url)
res.writeHead(200, {
'Content-Type': 'application/json'
// 'Content-Type': 'text/html;charset=utf-8' // 将内容以 html 标签和 utf-8 的形式展示到网页上
})
// write 中的内容直接展示到网页上
// res.write('hello')
res.end(JSON.stringify({
data: "hello"
}))
})
server.listen(8000,()=> {
console.log("server is running")
})
响应内容出现中文乱码解决:
res..setHeader('content-type','text/html;charset=utf-8')
如果端口被其他程序占用,可以使用资源监视器找到占用端口的程序,然后使用任务管理器关闭对应的程序.
/**
* 目标:基于 http 模块创建 Web 服务程序
* 1.1 加载 http 模块,创建 Web 服务对象
* 1.2 监听 request 请求事件,设置响应头和响应体
* 1.3 配置端口号并启动 Web 服务
* 1.4 浏览器请求(http://localhost:3000)测试
*/
// 1.1 加载 http 模块,创建 Web 服务对象
const http = require('http')
const server = http.createServer()
// 1.2 监听 request 请求事件,设置响应头和响应体
server.on('request', (req, res) => {
// 设置响应头-内容类型-普通文本以及中文编码格式
res.setHeader('Content-Type', 'text/plain;charset=utf-8')
// 设置响应体内容,结束本次请求与响应
res.end('欢迎使用 Node.js 和 http 模块创建的 Web 服务')
})
// 1.3 配置端口号并启动 Web 服务
server.listen(3000, () => {
console.log('Web 服务启动成功了')
})
获取 http 请求报文
注意事项:
- request.url 只能获取路径以及查询字符串,无法获取 URL 中的域名以及协议的内容
- request.headers 将请求信息转化成一个对象,并将属性名都转化成了『小写』
- 关于路径:如果访问网站的时候,只填写了 IP 地址或者是域名信息,此时请求的路径为
/
- 关于 favicon.ico:这个请求是属于浏览器自动发送的请求
获取 http 响应报文
资源类型(MIME)
媒体类型(通常称为 Multipurpose Internet Mail Extensions 或 MIME 类型 )是一种标准,用来表示文档、文件或字节流的性质和格式。
mime 类型结构: [type]/[subType]
例如: text/html text/css image/jpeg image/png application/json
HTTP 服务可以设置响应头 Content-Type 来表明响应体的 MIME 类型,浏览器会根据该类型决定如何处理资源。
下面是常见文件对应的 mime 类型
- html: ‘text/html’,
- css: ‘text/css’,
- js: ‘text/javascript’,
- png: ‘image/png’,
- jpg: ‘image/jpeg’,
- gif: ‘image/gif’,
- mp4: ‘video/mp4’,
- mp3: ‘audio/mpeg’,
- json: ‘application/json’
对于未知的资源类型,可以选择 application/octet-stream 类型,浏览器在遇到该类型的响应时,会对响应体内容进行独立存储,也就是我们常见的下载效果。
GET 请求的情况:
- 在地址栏直接输入 url 访问
- 点击 a 链接
- link 标签引入 css
- script 标签引入 js
- img 标签引入图片
- form 标签中的 method 为 get (不区分大小写)
- ajax 中的 get 请求
POST 请求的情况:
- form 标签中的 method 为 post(不区分大小写)
- AJAX 的 post 请求
1.1 解决跨域问题
接口 jsonp 解决跨域
// server.js
const http = require('http')
const url = require('url')
const app = http.createServer((req, res) => {
let urlObj = url.parse(req.url, true)
console.log(urlObj.query.callback)
switch (urlObj.pathname) {
case '/api/user':
res.end(`${urlObj.query.callback}(${JSON.stringify({name:'xxx',age:18})})`)
break
default:
res.end('404.')
break
}
})
app.listen(3000, () => {
console.log('localhost:3000')
})
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
const oscript = document.createElement('script');
oscript.src = 'http://localhost:3000/api/user?callback=test';
document.body.appendChild(oscript);
function test(obj) {
console.log(obj)
}
</script>
</body>
</html>
CORS 解决跨域
// server.js
const http = require('http')
const url = require('url')
const app = http.createServer((req, res) => {
let urlObj = url.parse(req.url, true)
// console.log(urlObj.query.callback)
res.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8',
// CORS 头
'Access-Control-Allow-Origin': '*'
})
switch (urlObj.pathname) {
case '/api/user':
res.end(`${JSON.stringify({ name: 'xxx', age: 18 })}`)
break
default:
res.end('404.')
break
}
})
app.listen(3000, () => {
console.log('localhost:3000')
})
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
fetch('http://localhost:3000/api/user')
.then(res=>res.json())
.then(res=>console.log(res))
</script>
</body>
</html>
通过 cors 解决跨域问题,我们也可以直接使用 cors 这个库,而且这个库有它先天的优势。
前端 反向代理只能在开发环境进行跨域,部署后不能跨域。
部署后可以在 Nginx 中配置代理,但是流程较为繁琐。
使用 cors 非常简单,原理就是上面的添加 Access-Control-Allow-Origin 响应头,开发阶段和部署后均能使用。
https://www.npmjs.com/package/cors
1.2 web服务器
get
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
fetch('http://localhost:3000/api/user')
.then(res=>res.json())
.then(res=>console.log(res))
</script>
</body>
</html>
// get.js
const http = require('http')
const https = require('https')
const url = require('url')
const app = http.createServer((req, res) => {
let urlObj = url.parse(req.url, true)
// console.log(urlObj.query.callback)
res.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8',
// CORS 头
'Access-Control-Allow-Origin': '*'
})
switch (urlObj.pathname) {
case '/api/user':
// 现在作为客户端 去猫眼api请求数据
// 注意协议要统一:https还是http
httpget(res)
break
default:
res.end('404.')
break
}
})
app.listen(3000, () => {
console.log('localhost:3000')
})
function httpget(response) {
let data = ''
https.get(`https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E7%9F%B3%E5%AE%B6%E5%BA%84&ci=76&channelId=4`,res => {
// data 是一份一份的数据收集,end 是最终收集到的所有数据
res.on('data', chunk => {
data += chunk
})
res.on('end', () => {
console.log(data)
response.end(data)
})
})
}
另一种写法:
// get.js
const http = require('http')
const https = require('https')
const url = require('url')
const app = http.createServer((req, res) => {
let urlObj = url.parse(req.url, true)
// console.log(urlObj.query.callback)
res.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8',
// CORS 头
'Access-Control-Allow-Origin': '*'
})
switch (urlObj.pathname) {
case '/api/user':
// 现在作为客户端 去猫眼api请求数据
// 注意协议要统一:https还是http
// data 收集好的时候调用内部传入的 cb 函数
httpget((data)=> {
res.end(data)
})
break
default:
res.end('404.')
break
}
})
app.listen(3000, () => {
console.log('localhost:3000')
})
function httpget(cb) {
let data = ''
https.get(`https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E7%9F%B3%E5%AE%B6%E5%BA%84&ci=76&channelId=4`,res => {
// data 是一份一份的数据收集,end 是最终收集到的所有数据
res.on('data', chunk => {
data += chunk
})
res.on('end', () => {
console.log(data)
cb(data)
})
})
}
post
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
fetch('http://localhost:3000/api/user')
.then(res=>res.json())
.then(res=>console.log(res))
</script>
</body>
</html>
// post.js
const http = require('http')
const https = require('https')
const url = require('url')
const app = http.createServer((req, res) => {
let urlObj = url.parse(req.url, true)
// console.log(urlObj.query.callback)
res.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8',
// CORS 头
'Access-Control-Allow-Origin': '*'
})
switch (urlObj.pathname) {
case '/api/user':
// 现在作为客户端 去小米优品 api 请求数据
// 注意协议要统一:https还是http
httpPost((data) => {
res.end(data)
})
break
default:
res.end('404.')
break
}
})
app.listen(3000, () => {
console.log('localhost:3000')
})
function httpPost(cb) {
let data = ''
const options = {
hostname: 'm.xiaomiyoupin.com',
port: '443', // 80 是 http 默认端口号,443 是 https 默认端口号
path: '/mtop/market/search/placeHolder',
methods: "POST",
headers: {
"Content-Type": "application/json",
}
}
const req = https.request(options, (res) => {
res.on('data', (chunk) => {
data += chunk
})
res.on('end', () => {
cb(data)
})
})
req.write(JSON.stringify([{}, { baseParam: { ypClient: 1 } }]))
req.end()
}
1.3 动静分离
动静分离是一种优化技术,将动态生成的内容(如动态网页、API请求)与静态资源(如HTML、CSS、JavaScript、图像文件)分开处理和分发。
- 静态资源是指:内容长时间不发生改变的资源,例如图片,视频,CSS 文件,JS文件,HTML文件,字体文件等
- 动态资源是指:内容经常更新的资源,例如百度首页,网易首页,京东搜索列表页面等
HTTP 服务在哪个文件夹中寻找静态资源,那个文件夹就是
静态资源目录,也称之为网站根目录
通过将动态内容和静态资源存储在不同的服务器或服务上,并使用不同的处理机制,可以提高网站的处理效率和响应速度。这种分离的好处包括:
- 性能优化:将静态资源与动态内容分离可以提高网站的加载速度。可以使用缓存机制将静态资源存储在CDN(内容分发网络)或浏览器缓存中,从而减少网络请求和数据传输的开销。
- 负载均衡:通过将动态请求分发到不同的服务器或服务上,可以平衡服务器的负载,提高整个系统的可伸缩性和容错性。
- 安全性:通过将静态资源与动态内容分离,可以更好地管理访问控制和安全策略。
实现动静分离的方法
- 使用反向代理服务器(如Nginx、Apache)将静态请求和动态请求转发到不同的后端服务器或服务。
- 将静态资源部署到CDN上,通过CDN分发静态资源,减轻源服务器的负载。
- 使用专门的静态文件服务器(如Amazon S3、Google Cloud Storage)存储和提供静态资源,而将动态请求交给应用服务器处理。
import fs from 'node:fs'
import http from 'node:http'
import mime from 'mime'
import path from 'node:path'
const server = http.createServer((req, res) => {
const {method, url} = req
if (method === 'GET' && url.startsWith('/static')) {
const staticPath = path.join(process.cwd(), url)
fs.readFile(staticPath, (err, data) => {
if (err) {
res.writeHead(404, {
'Content-Type': 'text/plain'
})
res.end('not found')
} else {
console.log('是兄弟就来缓存我')
const type = mime.getType(staticPath)
res.writeHead(200, {
// 要求使用 esm 语法,使用 commonjs 会报错
'content-type': type, // mime 类型 包含 text/html,text/css 等所有的mime 类型
"cache-control": 'public, max-age=3600' // public 缓存任何资源 缓存3600s
})
res.end(data)
}
})
}
// 处理动态资源
if ((method === 'GET' || method === 'POST') && url.startsWith('/api')) {
// ...处理动态资源的逻辑
}
})
server.listen(80, () => {
console.log('server is running at localhost:80')
})
常见的mime类型:
-
文本文件:
- text/plain:纯文本文件
- text/html:HTML 文件
- text/css:CSS 样式表文件
- text/javascript:JavaScript 文件
- application/json:JSON 数据
-
图像文件:
- image/jpeg:JPEG 图像
- image/png:PNG 图像
- image/gif:GIF 图像
- image/svg+xml:SVG 图像
-
音频文件:
- audio/mpeg:MPEG 音频
- audio/wav:WAV 音频
- audio/midi:MIDI 音频
-
视频文件:
- video/mp4:MP4 视频
- video/mpeg:MPEG 视频
- video/quicktime:QuickTime 视频
-
应用程序文件:
- application/pdf:PDF 文件
- application/zip:ZIP 压缩文件
- application/x-www-form-urlencoded:表单提交数据
- multipart/form-data:多部分表单数据
1.4 邮件服务
常见的用途:
-
用户注册和验证:当用户在网站或应用程序上注册时,可以使用邮件服务发送验证链接或验证码给用户,以确保其提供的电子邮件地址有效并与其身份匹配。
-
密码重置:当用户忘记密码时,邮件服务可以发送包含密码重置链接的电子邮件给用户,以允许他们设置新密码。
-
通知和提醒:邮件服务可以用来向用户发送各种通知和提醒,如订单确认、发货通知、预约提醒、会议邀请等。
-
营销和促销活动:企业可以使用邮件服务向客户发送营销和促销信息,如特价促销、新品发布、节日祝福等。
-
订阅和邮件列表:邮件服务可以用来处理用户的订阅请求,向订阅者发送定期或即时的更新内容,比如新闻资讯、博客文章等。
前提准备,需要用到 js-yaml
(用于解析yaml文件) 和 nodemailer
这两个库。
user.yaml 分别填写自己的邮箱和密码(QQ邮箱这里是授权码)
pass: 'xxx'
user: 'xxx@qq.com'
QQ 邮箱服务的文档:QQ 邮箱服务
POP3/SMTP 设置方法:
- 用户名/帐户: 你的QQ邮箱完整的地址
- 密码: 生成的授权码
- 电子邮件地址: 你的QQ邮箱的完整邮件地址
- 接收邮件服务器: pop.qq.com,使用SSL,端口号995
- 发送邮件服务器: smtp.qq.com,使用SSL,端口号465或587
生成授权码:
index.js
import nodemailer from 'nodemailer'
import yaml from 'js-yaml'
import http from 'node:http'
import fs from 'node:fs'
import url from "node:url";
const userInfo = yaml.load(fs.readFileSync('./user.yaml', 'utf8'))
// 初始化邮件服务
const transport = nodemailer.createTransport({
service: 'qq', // 服务商
host: 'smtp.qq.com', //主机
port: 465,
secure: true,
auth: {
user: userInfo.user, // 邮箱账号
pass: userInfo.pass // 密码 / 授权码
}
})
http.createServer(async (req, res) => {
const {method} = req
const {pathname} = url.parse(req.url)
if (method === 'POST' && pathname === '/send/mail') {
let data = ''
req.on('data', chunk => {
data += chunk
})
req.on('end', () => {
const {to, subject, text} = JSON.parse(data)
transport.sendMail({
to, // 收件人
from: userInfo.user, // 发件人
subject, // 主题
text // 内容
})
res.end('ok')
})
}
}).listen(3000, () => {
console.log('listening on port 3000')
})
POST http://localhost:3000/send/mail HTTP/1.1
Content-Type: application/json
{
"to": "xxx@qq.com",
"subject": "标题",
"text": "我想你了,你还好吗。那天分别之后,你就一句话也没说..."
}
请求成功就可以正常发送邮件了。