总结
浏览器(客户端)-> 服务端 交互
一、浏览器(客户端)-> 服务端 交互
发起请求: 请求报文
响应请求: 响应报文
报文是:字符串
请求报文对象:request
二 、请求报文对象:request
- 请求行:
- 请求类型:(get、post) 后期还会接触到其他的请求类型
request.method
- 请求url:在域名/网址之后的从/开始的后面的部分就是请求url
request.url
- http协议版本号:http/1.1
request.httpVersion
- 请求类型:(get、post) 后期还会接触到其他的请求类型
- 请求头:request.headers 返回的是一个对象 let {host,connection} =
request.headers
每一个网址中请求的头都有一些差异,但是会有一些公共的头信息.[参考课件]
后期自己搭建服务的时候,可以自行添加自定义头信息 - 空行
- 请求体:
区分于请求方式-
如果是get请求,则没有请求体
其他类型有请求体
get虽然没有请求体,但是可以将需要向服务端传递的数据表现在url地址的请求字符串中
http://localhost/index.html?name=admin&pass=123456?参数名=参数值&参数名=参数值这种格式叫做请求字符串/查询字符串,是附加在请求的url的后面
使用url模块来解析url中的字符串转变为对象的形式const url = require(‘url’)
url.parse(request.url,true)
; //{name:admin,pass:123456} -
post请求有请求体
通过事件let s = '' request.on('data',chunk=>{ s+=chunk;//chunk 获取请求的数据 }) request.on('end',()=>{ console.log(s); //name=admin&pass=123456 response.end('xxx') })
-
响应报文对象:response
三、响应报文:response
-
响应行:
-
http协议版本号
-
响应状态码 200、404(找不到)、500(服务器相关的错误)
response.statusCode
-
响应状态字符串
response.statusMessage
200 -> ok
404 -> not found
500 -> internal server error响应状态码和响应状态字符串是一一对应的
-
-
响应头:
response.setHeader('头名','头值')
response.writeHead(响应状态码,{'头名':'头值'})
-
空行:
-
响应体:服务端给客户端一个响应结果
图片、文字、json格式字符串、网站标签内容(html+css)。。。response.end()
response.write()
write可以调用多次,输出多次的响应结果,但是最后必须要依赖end方法
end方法虽然可以书写多次,但是最终只能执行第一个end,因为程序一旦遇到了end,就直接将响应体结果响应会客户端
//引入http模块 url模块
const http = require('http');
const url1 = require('url');
//搭建服务对象
const server = http.createServer( (request,response)=>{//request 请求报文对象 response响应报文对象
const {method,url,httpVersion,headers} = request;//获取请求类型 请求url地址 请求http协议版本号 请求头
// 设置响应头 两种方法
response.setHeader('Content-Type','text/html;charset=utf-8');
response.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});
response.setHeader('a','100');//自定义响应头信息
if(method === "GET"){
// http://localhost/index.html?name=admin&pass=123456
let data = url1.parse(url,true)//data.query属性值从字符串转换成对象{name:admin,pass:123456}
console.log(data.query,data.pathname);//pathname 请求的url地址
}else if(method === "POST"){
// 通过事件获取post请求体
let str = '';
//接收请求数据中
request.on('data',chunk=>{
str += chunk;//请求数据
})
//接收数据完成
request.on('end',()=>{
response.end(str);//设置响应体
})
}
response.end('设置内容');//设置响应体方法一
//设置响应体方法二
// response.write('内容')
// response.write('内容1')
// response.write('内容2')
// response.end('设置内容3');
});
//设置监听端口
server.listen(8080,()=>{
console.log('服务启动成功')
})
HTTP协议
一、概念
HTTP(hypertext transport protocol)协议;中文叫超文本传输协议
是一种基于TCP/IP的应用层通信协议
这个协议详细规定了浏览器
和万维网服务器
之间互相通信的规则。
协议中主要规定了两个方面的内容
- 客户端:用来向服务器发送数据,可以被称之为请求报文
- 服务端:向客户端返回数据,可以被称之为响应报文
报文:其实就是一堆字符串
二、请求报文的组成 【重点】
- 请求行
- 请求头
- 空行
- 请求体
三、HTTP的请求行内容
-
请求类型(get、post、put、delete等)
-
请求URL(统一资源定位器)
例如:http://www.baidu.com:80/index.html?a=100&b=200#logo
- http: 协议(https、ftp、ssh等)
- www.baidu.com 域名
- 80 端口号
- /index.html 路径
- a=100&b=200 查询字符串(请求字符串)
- #logo 哈希(锚点链接)
-
HTTP协议版本号
四、HTTP请求头结构以及常见请求头
格式:头名:头值;
常见的请求头有:
请求头可以程序员自定义!!!
请求头 | 解释 |
---|---|
Host | 主机名 |
Connection | 连接的设置 keep-alive(保持连接);close(关闭连接) |
Cache-Control | 缓存控制 max-age = 0 (没有缓存) |
Upgrade-Insecure-Requests | 将网页中的http请求转化为https请求(很少用)老网站升级 |
User-Agent | 用户代理,客户端字符串标识,服务器可以通过这个标识来识别这个请求来自哪个客户端 ,一般在PC端和手机端的区分 |
Accept | 设置浏览器接收的数据类型 |
Accept-Encoding | 设置接收的压缩方式 |
Accept-Language | 设置接收的语言 q=0.7 为喜好系数,满分为1 |
Cookie | 后面单独讲 |
五、HTTP的请求体
请求体内容的格式是非常灵活的,
(可以是空)==>GET请求,
(也可以是字符串,还可以是JSON)===》POST请求
例如:
- 请求字符串(参数名=参数值&参数名=参数值):keywords=手机&price=2000
- JSON(键名必须是双引号,键值如果是字符串也必须是双引号):{“keywords”:“手机”,“price”:2000}
六、响应报文的组成以及响应行
-
响应行
HTTP/1.1 200 OK
-
HTTP/1.1:HTTP协议版本号
-
200:响应状态码 404 Not Found 500 Internal Server Error
还有一些状态码,参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
-
OK:响应状态字符串
响应状态码和响应字符串关系是一一对应的。
-
-
响应头
Cache-Control:缓存控制 private 私有的,只允许客户端缓存数据
Connection 链接设置
Content-Type:text/html;charset=utf-8 设置响应体的数据类型以及字符集,响应体为html,字符集utf-8
Content-Length:响应体的长度,单位为字节
-
空行
-
响应体
响应体内容的类型是非常灵活的,常见的类型有HTML、css、js、图片、JSON、视频、音频、font…
七、搭建WEB服务
使用nodejs 创建HTTP服务器
三步走:
① 引入http内置模块
const http = require("http")
② 创建服务
const server = http.createServer((request,response)=>{ //普通函数或箭头函数均可
//根据请求,设置响应报文内容
//end方法可以设置响应体
response.end("xxx")
})
- request:是一个对象,可以获取请求报文中的数据
- response:是一个对象,可以设置响应报文中的数据
③ 监听端口
- 端口号:
- 是计算机的服务窗口,总共是65536个服务窗口,程序可以监听端口来处理指定端口的数据
- 常见端口号的选择:8000、8080、8888、3000、9000 默认端口号为80,在『浏览器请求时』可以不写
其他端口号尽量选择大于1024的
server.listen(端口号,回调函数)
④ 浏览器请求对应端口
http://127.0.0.1:端口号
-
127.0.0.1始终都是指向本机,IP地址是计算机在网络中唯一的门牌号,
-
IP地址一般由四位数字组成,ipv4(第4个版本)
-
获取某网站的ip地址:
ping 网站域名
常见问题:
(1)当服务已经启动后
- 想要修改JS代码,刷新浏览器后,发现页面内容无变化
答:JS代码修改之后,『必须重启』服务(并非重新运行该文件),才能看到最新的显示结果
- 端口号被占用
Error: listen EADDRINUSE: address already in use :::8000
解决办法1:停止当前正在运行监听端口的服务 【使用较多】
答:找到对应命令行窗口,使用『ctrl+c』组合键
解决办法2:修改其他端口号使用
- 响应内容中文,浏览器中显示为乱码现象
答:response.writeHead(200,{"Content-Type":"text/plain;charset=utf-8"}) #默认为text文本
//1.引入http模块
const http = require('http');
//2.创建服务
const server = http.createServer((request,response)=>{
response.end('hello http');
})
//3.监听端口
server.listen(80,()=>{
console.log('80端口服务已启动');
})
八、获取HTTP请求的数据
想要获取请求的数据,必须通过request对象;
其中包含以下参数:
参数名 | 含义 | 语法 | 重点掌握 |
---|---|---|---|
method | 请求方法 | request.method | ***** |
httpVersion | 请求版本 | request.httpVersion | |
url | 请求路径 | request.url | ***** |
headers | 请求头 | request.headers | ***** |
on | 请求体 | request.on(‘data’, function(chunk){}) | |
request.on(‘end’, function(){}); |
特殊说明:
1、request.url:只能获取路径以及查询字符串,无法获取url中的域名以及协议的内容;
2、request.headers:将请求信息转化成一个对象,并将属性名都转化成了『小写』
3、关于路径:如果访问网站的时候,只填写了IP地址或者是域名信息,此时请求的路径为『/』
4、关于ip:ip是计算机在网络中的唯一门牌号,ipv4 156.23.56.85
127.0.0.1 始终是指向本机的
4、关于 favicon.ico:这个请求是属于『谷歌浏览器』自动发送的请求,其他浏览器就不一定会发送了,
用来获取当前网站的『小图标』
练习1:按照以下要求,搭建http服务,并按照不同url地址访问
请求类型 | 请求地址 | 响应体结果 |
---|---|---|
get | /login | 登录页面 |
get | /register | 注册页面 |
//1、引入http模块
const http = require("http");
//2、建立服务
const server = http.createServer((request,response)=>{
let {url,method} = request; //对象的解构赋值
//设置响应头信息
//解决中文乱码
response.setHeader("Content-Type","text/html;charset=utf-8")
if(url == "/register" && method == "GET"){
response.end("注册页面");
}else if(url=="/login" && method == "GET"){
response.end("登录页面");
}else{
response.end("<h1>404 Not Found</h1>")
}
})
//3、监听端口
server.listen(8000,()=>{
console.log('服务启动中....');
})
当请求url中携带请求参数时,就需要借助于url模块了
① 引入http内置模块
let http = require("http")
② 引入url模块
let url = require("url");
③ 创建服务
let server = http.createServer((request,response)=>{
//针对于请求中的url进行解析
//第二个true表示将query属性值从字符串转换成对象
//语法:url.parse(request.url,true)
response.end("xxx")
})
url模块中常用的参数
-
pathname:url中的路径部分
-
query:url中的查询字符串对象
③ 监听端口
端口号:
- 是计算机的服务窗口,总共是65536个服务窗口,程序可以监听端口来处理指定端口的数据
- 常见端口号的选择:8000、8080、8888、3000、9000 默认端口号为80,在『浏览器请求时』可以不写
- 其他端口号尽量选择大于1024的
练习2:按照以下要求,搭建http服务,并按照不同url地址访问,修改页面的背景颜色
例如:
/server?bg=yellowgreen
/server?bg=red
//1、引人http模块
const http = require("http");
//2、引入url模块
const url = require("url");
//3、创建服务
const server = http.createServer((request,response)=>{
// console.log(url); //url是一个对象
//如果想要解析地址栏的请求地址,需要调用url对象中的parse方法
//url.parse的第二个参数为true时将使用查询模块来分析查询字符串,默认为false,所以咱们设置为true
let data = url.parse(request.url,true);
let bg = data.query.bg?data.query.bg:'pink';
console.log(bg);
//设置响应结果的显示颜色
response.end(`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
background:${bg}
}
</style>
</head>
<body>
</body>
</html>`)
})
//4、监听端口
server.listen(8000,()=>{
console.log('服务正在启动中....ing');
})
九、获取HTTP请求体的数据
当请求的数据来自于表单,那么在响应服务之前,就需求获取请求体中的数据
<form method="post" action="http://127.0.0.1/">
用户名:<input type="text" name="username"/><br/>
密码:<input type="text" name="userpass"/><br/>
<button>登录</button>
</form>
关于请求体:
- GET请求的请求体绝大多数为空
- POST请求的请求体大多数都不为空
请求体获取的三步走:
1、声明一个字符串,用来显示请求信息
let str = "";
2、设置绑定事件,利用可读流数据读取的事件
request.on('data',chunk=>{
str+=chunk;
})
3、绑定事件,当数据读取完毕时触发的事件
request.on('end',()=>{
//获取请求体结果
console.log(str);
//响应结果
response.end("over~~~")
)
//1、引入http模块
const http = require("http");
//2、创建服务
const server = http.createServer((request,response)=>{
//4、声明一个字符串
let str = "";
//5、绑定事件
request.on('data',chunk=>{
str+=chunk;
})
request.on('end',()=>{
//获取请求体结果
console.log(str);
//响应结果
response.end('over~~~')
})
})
//3、监听端口
server.listen(8000,()=>{
console.log('服务启动中.....');
})
十、HTTP模块响应相关的API
- response.statusCode 设置响应状态码
- response.statusMessage 设置响应状态字符串 (
设置的较少
) - response.setHeader(‘头名’,‘头值’) 自定义头信息
- response.write(‘xx’) 设置响应体
- response.end(‘xxx’) 设置响应体
write和end的两种使用情况:
//1.情况1: write和end的结合使用 响应体相对分散
response.write('xx');
response.write('xx');
response.write('xx');
...
response.end(); //每一个请求,在处理的时候必须要有 end 方法的执行,不能省略
//2.情况2:单独使用end方法 响应体相对集中
response.end('xxx');
案例:搭建HTTP服务,响应一个表格4行3列,并实现隔行换色(JS)
//1.引入http模块
const http = require('http');
//2.创建服务
const server = http.createServer((request,response)=>{
response.end(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
td{
width:100px;
height:50px;
}
table,td{
border-collapse:collaspe;
}
</style>
</head>
<body>
<table border="1" align="center" cellpadding="3" cellspacing="3">
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<script>
//获取所有的tr行
let trs = document.querySelectorAll('tr');
//循环
trs.forEach((item,index)=>{
if(index%2==0){
item.style.background = "orange";
}
else{
item.style.background = "green";
}
})
</script>
</body>
</html>
`)
})
//3.监听端口
server.listen(80,()=>{
console.log('80端口启动中....ing');
});
案例2:搭建服务,响应带数据的表格
const data = [
{
id:1,
name:'孙燕姿',
song:'逆光'
},
{
id:2,
name:'周杰伦',
song:'不能说的密码'
},
{
id:3,
name:'林俊杰',
song:'不为谁而做的歌'
},
{
id:4,
name: '五月天',
song:'干杯'
},
{
id: 5,
name: '张艺兴',
song: '莲'
},
{
id:6,
name:'刘德华',
song:'冰雨'
},
{
id: 7,
name: '张学友',
song: '情人'
}
]
//要求:搭建表格,数据来自于data变量
require('http').createServer((request,response)=>{
let body = "";
body+=`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
td{
width:100px;
height:50px;
}
table,td{
border-collapse:collaspe;
}
</style>
</head>
<body>
<table border="1">
<tr>
<td>id</td>
<td>歌手名称</td>
<td>成名曲</td>
</tr>`;
data.forEach(item=>{
body+=`
<tr>
<td>${item.id}</td>
<td>${item.name}</td>
<td>${item.song}</td>
</tr>
`
})
body+=`
</table>
</body>
</html>
`;
response.end(body);
}).listen(80,()=>{
console.log('80端口正在运行中....ing');
})
关于响应:
- 响应文件内容:对于HTML内容,长时间不发生改变的文件,使用该方式
- 响应拼接后的结果:对于网页内容,经常发生改变的文件,使用该方式
- 而在工作中,最常见的情况是服务器端响应JSON
十一、响应文件内容固定实现
require('http').createServer((request,response)=>{
//获取请求的方法已经路径
let {url,method} = request;
//判断请求方式以及请求路径
if(method == "GET" && url == "/index.html"){
//需要响应文件中的内容
let data = require('fs').readFileSync(__dirname + '/index.html');
response.end(data);
}else if(method == "GET" && url == "/css/app.css"){
//需要响应文件中的内容
let data = require('fs').readFileSync(__dirname + '/public/css/app.css');
response.end(data);
}else if(method == "GET" && url == "/js/app.js"){
//需要响应文件中的内容
let data = require('fs').readFileSync(__dirname + '/public/js/app.js');
response.end(data);
}
else{
//404响应
response.statusCode = 404;
response.end("<h1>404 Not Found</h1>");
}
}).listen(80,()=>{
console.log('80端口正在启动中....');
})
很明显上面的代码,当只要有一个请求路径就需要进行判断,显然这种方式不够完美,那么我们需要封装
require('http').createServer((request,response)=>{
//获取请求的方法已经路径
let {url,method} = request;
//文件夹路径
let rootDir = __dirname + '/public';
//拼接文件路径
let filePath = rootDir + url;
//读取文件内容
fs.readFile(filePath,(err,data)=>{
//判断
if(err){
//如果出现错误,响应404状态码
response.statusCode = 404;
response.end('<h1>404 Not Found</h1>');
}else{
//响应文件内容
response.end(data);
}
})
}).listen(80,()=>{
console.log('80端口正在启动中....');
})
十二、GET和POST请求的区别
GET和POST是HTTP协议请求的两种方式。
-
GET主要用来获取数据,POST主要用来提交数据
-
GET带参数请求是将参数缀到URL之后,在地址栏中输入url访问网站就是GET请求,
POST带参数请求是将参数放到请求体中
-
POST请求相对GET安全一些,因为在浏览器中参数会暴露在地址栏
-
GET请求大小有限制,一般为2K,而POST请求则没有大小限制
-
GET类型报文请求方法的为GET,POST类型报文请求方法为POST
情况1:针对于安全性来说
POST更安全,GET安全系数较低
情况2:表现形式
POST提交数据通过请求体来提交
GET是将请求的数据暴露在地址栏中的请求字符串里面
情况3:传递数据大小
GET基本传递数据不到2KB
POST大小基本不上限(但是可以通过后台代码(java、php…)来设置)
情况4:使用场景
GET通常用于获取数据
POST通常用于提交数据
十三、GET和POST使用场景
GET请求的情况:
- 在地址栏直接输入url访问
- 点击a链接
- link标签引入css
- script标签引入js
- img标签引入图片
- form标签中的method为get
- ajax中的get请求
post请求的情况:
- form标签中的method为post
- ajax的post请求