1.服务器概念
概念
本质就是一台电脑,它的性能比个人电脑高很多
负责存放和对外提供资源的电脑,叫做服务器
负责获取和消费资源的电脑,叫做客户端
网络通信三要素
ip:设备在网络中的唯一标识(ping 网址即可获得ip地址,127.0.0.1,为本地ip,对应localhost)
ip和域名是一一对应的关系,在域名服务器中存放,并提供了二者的转换。
端口:程序在设备中的唯一标识
协议:规定了浏览器和服务器之间数据的传输形式
浏览器发送什么样的数据给服务器,服务器才能解析
服务器发送什么样的数据给浏览器,浏览器才能解析
2.URL地址
统一资源定位符,用来标注互联网上每个资源的唯一存放位置,可以定位互联网上唯一一个资源
组成
通信协议http
服务器名称(ip,域名)
资源在服务器上具体的存放位置
3.HTTP协议
概念
超文本传输协议
作用
规定了浏览器和服务器之间进行的网络数据传输所遵循的规范
特点
- 基于请求和响应模型
- 必须先有请求,再有响应
- 请求和响应必须成对出现
-
- 请求成功 200
- 请求失败 404,500
4.请求响应消息
请求-处理-响应
请求消息
概念
客户端给服务器的信息,告诉服务器,当前我浏览器的一些信息
浏览器给服务器
请求行
GET
请求方式
请求的URI(统一资源标识符)
请求头
(一些键值对,告诉服务器浏览器信息)
本地主机
host
浏览器版本信息
user-agent
发给服务器的数据格式
content-type
可接受数据个数
accept
支持的语言(浏览器期望获取自然语言的顺序)
accept-language
支持的编码
accept-encoding
保持连接(http1.1版本特性
connection
页面来源
referer
请求体
只有post请求才有
响应信息
服务器给浏览器
概念
服务器发给客户端的信息,告诉浏览器我发给你消息数据的类型和特点
组成
响应行
格式:协议版本,状态码,状态码描述
响应头
也是键值对,告诉浏览器我服务器的信息)
发送数据的格式(告诉浏览器我发送数据的类型
content-type
处理跨域请求用
access-control-allow-origin
本次响应的时间
date
保持连接(http1.1版本特性
connection
超时时长
keep-alive
本次响应的长度(发给浏览器内容的字节长度
content-length
响应体
将来展示到body(页面正文)中的内容
常见状态码
2xx 成功 200
3xx 重定向 302
4xx 客户端错误 404 资源找不到
5xx 服务器错误 500 服务器内部错误
5.请求响应对象
由来
由于请求消息和响应消息都是字符串,操作字符串非常麻烦,node将请求响应消息封装成了请求响应对象
程序员只需要调用这些对象的方法,就可以改变请求响应消息的内容
分类
请求对象(request对象)
封装了请求消息
主要用于获取请求数据(获取请求方法,获取请求体)
响应对象(response对象)
封装了响应消息
主要用于设置数据(设置响应状态码,设置响应体)
6.手写web服务
1.基本web服务
- 导入http模块
- 创建web服务器
- 绑定事件,处理请求(本质:为服务器server绑定一个request事件,监听客户端发送的请求,作出响应
- 设置端口,进行监听
// 1. 导入http模块
const http = require('http');
// 2. 创建web服务器
const server = http.createServer()
// 3. 绑定事件,处理请求
server.on('request',(req,res)=>{
// req请求对象,res响应对象——这两个服务器都是服务器传递过来的
// 程序员就可以在函数体中拿到这两个对象进行操作
res.end('<h1>hello world</h1>') // 设置响应体数据,响应到页面上
})
// 4. 设置端口,进行监听
server.listen(3000,function(){
console.log('服务器已启动,访问服务器地址:http://localhost:3000') //监听3000端口
})
2.解决中文乱码问题
前后编码不一致
服务器发送数据,使用ISO-8859-1(拉丁文),不支持中文
浏览器接收数据并解析:中文window使用GBK(国标)
解决
使用响应对象,设置响应头,统一编码
res.setHeader('content-type','text/html;charset=utf-8')
告诉浏览器我发送的数据格式是utf-8,浏览器接收之后,会以utf-8格式编码(Content-Type:
text/html;charset=utf-8)
3.根据不同的url响应不同内容(路由)
需求:根据不同的访问路径url响应不同的内容
http://localhost:3000/ 或者 http://localhost:3000/index 响应的内容为首页
http://localhost:3000/list 响应的是列表页
http://localhost:3000/detail 响应详情页
http://localhost:3000/aaa 乱输,页面找不到
以下面的访问路径为例进行解析:
假设访问路径为http://localhost:3000/detail?id=3 id为3的详情
detail?id=3在请求对象中
步骤
- 先通过请求对象req中的url属性,获取请求的url
- 再通过url的内置模块解析获取出来的url,得到url对象
- 从url对象中获取pathname属性值,就可以拿到访问路径
http:// localhost : 3000 /detail ? id=3
协议 ip域名 端口 访问路径pathname 参数query
关于url对象的解析
url.parse(urlStr) 第二个参数传true和false,表示是否要解析里面的请求参数query
默认为false,改为true之后,解析出来如下
Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '?id=3',
query: [Object: null prototype] { id: '3' }, 这里变成了对象(false的时候为字符串
pathname: '/detail',
path: '/detail?id=3',
href: '/detail?id=3'
}
// 3. 绑定事件,处理请求(本质:为服务器server绑定一个request事件,监听客户端发送的请求,作出响应)
server.on('request',(req,res)=>{ // req请求对象,res响应对象——这两个服务器都是服务器传递过来的
// 设置响应头,处理乱码,(告诉服务器,我发送的数据格式是utf-8
res.setHeader('content-type','text/html;charset=utf-8')
/* // 获取请求的url(假设访问路径是http://localhost:3000/detail?id=3
console.log(req) // 通过打印发现有url属性,值为'detail?id=3'
const urlStr = req.url
// 解析url(使用内置模块url,需要导入
const obj = url.parse(urlStr,true) // 解析出来是一个url对象
console.log(obj) // 里面有个属性pathname,值为/detail
// 获取pathname属性值
const pathname = obj.pathname
console.log(pathname) // detail */
// 通过url的内置模块,解析从请求对象中获取到的url字符串,然后再解构拿到pathname属性的值
const{pathname} = url.parse(req.url,true)
// 判断
if(pathname == '/' || pathname == '/index'){
res.end('<h1>首页</h1>') // 设置响应体数据,响应到页面上
}else if(pathname == '/list'){
res.end('<h1>列表页</h1>')
}else if(pathname == '/detail'){
res.end('<h1>详情页</h1>')
}else{
res.end('<h1>找不到</h1>')
}
})
4.获取get请求的参数
需求
假设访问路径http://localhost:3000/detail?id=3 并且是get请求
获取id=3这个数据(将来要根据id=3这个数据到数据库中查找对应的数据)
步骤
首先需要判断请求的路径是不是以/detail开头
如果是的话,判断请求的方式是不是get方式
如果是的话,解析获取到的url,拿到里面query属性的值
server.on('request', (req, res) => { // req请求对象,res响应对象——这两个服务器都是服务器传递过来的
// 设置响应头,处理乱码,(告诉服务器,我发送的数据格式是utf-8
res.setHeader('content-type', 'text/html;charset=utf-8')
// 通过url的内置模块,解析从请求对象中获取到的url字符串,然后再解构拿到pathname属性的值
const { pathname, query } = url.parse(req.url, true)
// 判断
if (pathname == '/' || pathname == '/index') {
res.end('<h1>首页</h1>') // 设置响应体数据,响应到页面上
} else if (pathname == '/list') {
res.end('<h1>列表页</h1>')
} else if (pathname == '/detail') {
// 判断请求方式
if (req.method == 'GET') {
// 获取get请求的参数
console.log(query)
console.log(query.id)
}
res.end('<h1>详情页</h1>')
} else {
res.end('<h1>找不到</h1>')
}
})
打印结果
应用场景
查找单个数据的时候会用到
5.获取post请求的参数
发送请求
借助表单发送post请求(以流的方式传输,只有在传输完成和?的时候能监听到
参数在请求体中
需求
获取post请求的参数(获取username和password的值)
步骤
- 提交到后台的访问路径http://localhost:3000,也就是pathname是'/'
- 获取请求方式,当请求方式为post时,进行下一步操作
const http = require('http');
const url = require('url');
// 导入模块用以解析参数
const qs = require('querystring');
const server = http.createServer()
server.on('request',(req,res)=>{ // req请求对象,res响应对象——这两个服务器都是服务器传递过来的
res.setHeader('content-type','text/html;charset=utf-8')
// 通过url的内置模块,解析从请求对象中获取到的url字符串,然后再解构拿到pathname属性的值
const{pathname} = url.parse(req.url,true)
// 判断
if(pathname == '/' || pathname == '/index'){
// 判断请求方式
if(req.method == 'POST'){
// 监听request的两个事件
// data事件-当服务器接收到post数据变化时调用
// end事件-当前服务器接收完post数据的时候调用
let postData = ''
req.on('data',data => {postData += data})
req.on('end',() => {
console.log(postData)
// 解析获取的参数
const obj = qs.parse(postData)
console.log(obj)
console.log(obj.username)
console.log(obj.password)
})
}
res.end('<h1>首页</h1>') // 设置响应体数据,响应到页面上
}else if(pathname == '/list'){
res.end('<h1>列表页</h1>')
}else if(pathname == '/detail'){
res.end('<h1>详情页</h1>')
}else{
res.end('<h1>找不到</h1>')
}
})
server.listen(3000,function(){
console.log('服务器已启动,访问服务器地址:http://localhost:3000') //监听3000端口/detail?id=3
})
// 导入模块用以解析参数
const qs = require('querystring');
// 解析获取的参数
const obj = qs.parse(postData)
console.log(obj)
console.log(obj.username)
console.log(obj.password)
nodemon使用
使用node可以热更新,修改代码之后,服务器会自动重启,来提高效率
安装
npm i nodemon -g
使用
nodemon 文件路径 启动服务