Node.js 内置模块-Http开启web服务器:
一.服务器相关概念
1.服务器:
- 提供网络服务的一台机器,通过在自己的电脑上安装特殊的软件(或者是运行某段特殊的代码)来提供服务 简单来说:服务器 = 电脑 + 能给其它电脑提供服务的软件
2.客户端与服务器:
- 提供服务的是服务器,享受服务的是客户端
3.web服务器(重点学习):
- 安装apache/tomcat/iis或者在nodejs环境写代码来提供:图片/视频/音频浏览/新闻数据浏览等服务的服务器
- 用户通过浏览器来享受web服务器提供的图片,视频,音频等网页内容浏览的服务
- 用户用url地址来访问某个web服务器上的资源
- 浏览器端发起请求,web服务器收到请求后,响应这个请求,并将处理结果返回给浏览器
- 浏览器端与web服务器是通过http(或者是https)协议来进行请求和响应的
4.ftp服务器:
- 安装serv-U软件,为其它电脑提供文件下载,共享服务
5.数据库服务器:
- 安装mysql软件,为其它电脑提供数据库服务
6.IP地址:
- 全称: Internet Protocol Address
- 作用: 标识一个网络设备(计算机、手机、电视)在某一个具体的网络当中的地址,要访问某个电脑上的资源,先要找到它的ip
- 在同一个网络中,计算机的IP是不允许相同的,都是唯一的
- IP分类:IPV4(互联网协议第6版)、 IPV6(互联网协议第6版)
- IPV4格式: [0-255].[0-255].[0-255].[0-255] 即为四个 0-255 的数字组成
- 127.0.0.1(本机访问) 或 192.168.106.2(局域网访问) 或 220.181.38.149(外网访问)
- IPV6格式:X:X:X:X:X:X:X:X,每个X 以十六进制表示(了解)
- FF01:0:0:0:0:0:0:1101 → FF01::1101,可以把连续的一段0压缩为“::”
7.域名: ip地址的别名
- 127.0.0.1 的别名为 localhost 系统自动解析的,不需要做额外的配置
- 220.181.38.149的别名为 www.baidu.com 需要域名解析系统把域名翻译成IP地址,需要额外购买和配置
8.端口:
-
理解:如果理解IP地址(一台服务器)是一栋大商场,端口就是商场中的商铺的编号
-
一个网络设备可以有65536个端口,范围是从[0,65535])
-
不同的端口被不同的软件占用,以提供不同的服务
-
服务器要提供服务必须要通过指定的端口
-
客户端与服务器都需要通过特定端口要进行通信(http://157.122.54.189:9092)
-
端口是可以编程分配
-
有一些端口号是被预定了的
- http: 80
- https:443
- mysql:3306
9.HTTP协议:
-
协议:制定客户端与服务器之间的通讯规则,不同的协议的作用也不同,我们主要了解HTTP协议
-
http协议
- 定义:HTTP(HyperText Transfer Protocol) 超文本传输协议,浏览器与web服务器都要遵守的协议
- HTTP 协议中明确规定了请求数据和响应数据的格式(报文)
-
浏览器 请求 资源 要遵守 http 协议: 请求报文(请求行,请求头,请求体)
-
服务器 返回 资源 要遵守 http 协议: 响应报文(响应行,响应头,响应体)
二.核心模块Http实现一个Web服务器
1.用http 模块写一个简单的web服务器:
//1.创建一个web服务器 导入http模块
const http = require("http")
//2.创建一个服务
const server = http.createServer((req, res) => {
console.log('接受到了客户端的请求');
res.end('OK')
})
//3.监听8000端口并启动web服务器等待客户端请求
server.listen(8000, () => {
console.log('服务器准备就绪');
})
复制代码
工作原理:使用http模块在本机上创建一个Web服务器,它来接收浏览器的请求,并给出响应。
Http模块创建Web服务器代码解析:
2.req.url:
- 通过 req.url来获取当前请求的url路径
- 不同的请求url通过req.url获取的结果
代码:
//1.创建一个web服务器 导入http模块
const http = require("http")
//2.创建一个服务
const server = http.createServer((req, res) => {
//获取到url的结果
console.log(req.url);
res.end('OK')
})
//3.监听8000端口并启动web服务器等待客户端请求
server.listen(8000, () => {
console.log('服务器准备就绪');
})
复制代码
3.res.end()设置响应体:
- 语法:res.end(响应的数据) end()只能传入buffer或者是String类型的数据
- 作用:设置响应体并结束本次请求
- 用法:在createServer的回调函数中使用
- 注意:一次请求只能有一个res.end()响应,多个以第一个响应的数据为准,同时服务器报错终止
4.res.setHeader()设置响应头解决中文乱码:
-
作用:设置响应头信息,控制浏览器的一些行为
-
语法:res.setHeader(响应头,响应值) ,两个参数的具体值都是http协议规定的
-
用法:在createServer的回调函数中使用
-
注意:
- res.setHeader()在res.end()之前才有效
- res.setHeader()可以设置多次
-
解决响应中文字符在浏览器显示成乱码问题
- res.setHeader(“Content-Type”, ”text/html;charset=utf8”):
- res.setHeader("Content-Type", 'application/javascript;charset=utf8' );
//1.创建一个web服务器 导入http模块
const http = require("http")
//2.创建一个服务
const server = http.createServer((req, res) => {
//防止中文乱码
res.setHeader("Content-Type", 'application/javascript;charset=utf8');
res.end('OK')
})
//3.监听8000端口并启动web服务器等待客户端请求
server.listen(8000, () => {
console.log('服务器准备就绪');
})
复制代码
5.res.statusCode设置状态码:
-
场景:浏览器输入了一个不存在的url,则服务器返回404状态码
-
语法:res.statusCode = 状态码,状态码是http协议规定的
- 状态码: 500 (服务器异常) 404(资源找不到)
-
注意:res.statusCode只有在res.end()前执行才有效
6.修改了服务器的代码要重启:
-
场景:更改res.end()的内容,重启后,再次观察是否有更新
- 停止服务: 在小黑窗中按下ctrl+c 停止服务
- 重启服务:就是重新运行程序(按下向上的箭头,再回车)
7.content-type作用:
在http协议中,content-type用来告诉对方本次传输的数据的类型是什么
-
在请求头中设置content-type来告诉服务器,本次请求携带的数据是什么类型的
-
在响应头中设置content-type来告诉浏览器,本次返回的数据是什么类型的
-
res.setHeader方法可以设置content-type这个响应头,浏览器根据不同类型做出不同解析
-
※ 请求头和响应头都可以设置content-type
-
常见的几种文件类型及content-type
- .html:res.setHeader('content-type', 'text/html;charset=utf8')
- .css:res.setHeader('content-type', 'text/css;charset=utf8')
- .js:res.setHeader('content-type', 'application/javascript')
- .png:res.setHeader('content-type', 'image/png')
- json数据:res.setHeader('content-type', 'application/json;charset=utf-8')
三.Web服务器处理接口响应
1.接口请求方式:
- 发送接口请求的类型: get post delete put ...
- web服务器通过req.method获取当前请求的类型
2.接口请求的各种表现形式:
- get请求不带参数的接口: http://127.0.0.1:8003/getHero
- get请求带参数的接口: http://127.0.0.1:8003/getHero**?heroName=后羿**
- post请求接口 http://127.0.0.1:8003/addHero (参数在请求报文体中传输)
3.不带参数的接口:
-
场景:在server.js中写代码,提供一个名为getHero的Get接口(http://127.0.0.1:8003/getHero)它以json字符串格式返回db/data.json的内容
//导入http模块 const http = require("http") //创建服务 const server = http.createServer((req, res) => { const { method, url } = req //防止中文乱码 res.setHeader("Content-Type", 'application/javascript;charset=utf8'); if (method === "GET" && url === '/member/cart') { res.end("获取购物车商品") } else if (method === "POST" && url === '/member/cart') { res.end("加入购物车") } else if (method === "DELETE" && url === '/member/cart') { res.end("删除购物车") } else { res.end("OK") } }) server.listen(8001, () => { // 启动服务(监听) console.log('请求成功'); }) 复制代码
4.带参数的Get接口:
场景:在server.js中写代码,提供一个名为getHero的Get接口(http://127.0.0.1:8003/getHero?name=xxx)它以json字符串格式返回db/data.json的name为xxx的内容
前置知识:
-
get请求的参数是附加在url后面
-
URLSearchParams 解析参数
-
URLSearchParams可以将查询字符串解析成一个对象,通过get方法获取到指定参数值
<script> const query = 'name=后裔' //通过URLSearchParams解析参数值 const usp = new URLSearchParams(query) //通过get方法获取到查询字符串name参数的值 const nameValue = usp.get('name') console.log(nameValue); </script> 复制代码
-
5.postman工具测试api接口:
- postman是一款api接口的测试工具,可以模拟get,post,文件上传等请求
- 下载地址+汉化
四.代码片段:导包express
//1.导包
const express = require('express')
const fs = require('fs')
//2.创建服务
const server = express()
const stuPath = "./db/student.json"
//获取请求数据需要主动开启,不开启功能req.body的值为undefined
server.use(express.urlencoded())
server.get("/", (req, res) => {
res.send("主页")
})
//======业务开始========
//查询所有学员
server.get("/student", (req, res) => {
//读取文件
fs.readFile(stuPath, (err, data) => {
//读取失败,就响应失败的提示
if (err) return res.send({
code: 1,
message: "获取失败",
})
//成功响应
res.send({
code: 0,
message: "获取成功",
data: JSON.parse(data)
})
})
})
//增加学员
server.post("/student", (req, res) => {
// console.log('请求体数据', req.body);
//读取文件
fs.readFile(stuPath, (err, data) => {
//读取文件失败提醒
if (err) return res.send({
code: 1,
message: "获取失败",
})
//处理学生格式
const stu = {
id: Date.now(),
name: req.body.name,
age: Number(req.body.age),
sex: req.body.sex
}
//把读取的文件解析成数组
const arr = JSON.parse(data)
//追加数据
arr.push(stu)
//把新数据写入目标文件中 注意参数二要转JSON字符串
fs.writeFile(stuPath, JSON.stringify(arr, null, 2), err => {
//写入文件失败提醒
if (err) return res.send({
code: 1,
message: "增加失败",
})
//写入成功
res.send({
code: 0,
message: "增加成功",
})
})
})
})
//删除学员
server.delete("/student", (req, res) => {
// console.log('query查询参数', req.query)
//读取文件
fs.readFile(stuPath, (err, data) => {
//失败提示
if (err) return res.send({
code: 1,
message: "获取失败",
})
//根据id删除一条数据
const arr = JSON.parse(data)
// 通过过滤实现删除
const newArr = arr.filter(item => item.id !== Number(req.query.id))
// 写入新数组
fs.writeFile(stuPath, JSON.stringify(newArr, null, 2), (err, data) => {
//失败提示
if (err) return res.send({
code: 1,
message: "删除失败",
})
//删除成功提示
res.send({
code: 0,
message: "删除成功",
})
})
})
})
//修改学员
server.put("/student", (req, res) => {
// console.log(req.body)
// 读取文件
fs.readFile(stuPath, (err, data) => {
//失败提示
if (err) return res.send({
code: 1,
message: "获取失败",
})
//转换成数组
const arr = JSON.parse(data)
//从数组中查找要修改的数组项
const stu = arr.find(item => item.id === Number(req.body.id))
// 修改信息
stu.name = req.body.name
stu.age = Number(req.body.age)
stu.sex = req.body.sex
// 重新写入
fs.writeFile(stuPath, JSON.stringify(arr, null, 2), err => {
//失败提示
if (err) return res.send({
code: 1,
message: "修改失败",
})
//成功提示
res.send({
code: 0,
message: "更新学员成功",
})
})
})
})
//3.// 启动服务(监听)
server.listen(8000, () => {
console.log('服务器启动完成');
})
复制代码