在介绍
http模块
之前先来了解一下什么是HTTP
HTTP(hypertext transport protocol)协议;中文叫超文本传输协议
是一种基于TCP/IP
的应用层通信协议
这个协议详细规定了 浏览器
和万维网 服务器
之间互相通信的规则。
协议中主要规定了两个方面的内容
发送数据
,可以被称之为请求报文
返回数据
,可以被称之为响应报文
报文:可以简单理解为就是一堆字符串
get
、post
、put
、delete
等)统一资源定位器
)http://www.baidu.com:80/index.html?a=100&b=200#logo
协议(https、ftp、ssh等)
域名
端口号
路径
查询字符串
(锚点链接)
请求体内容的格式是非常灵活的,
例如:
HTTP/1.1 200 OK
HTTP协议版本号
响应状态码
404 Not Found 500 Internal Server Errorhttps://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
响应状态描述
响应状态码和响应字符串关系是一一对应的。
Cache-Control
: 缓存控制 private 私有的,只允许客户端缓存数据
Connection
:链接设置
Content-Type
:text/html;charset=utf-8 设置响应体的数据类型以及字符集,响应体为html,字符集utf-8
Content-Length
:响应体的长度,单位为字节
//1. 导入 http 模块
const http = require('http')
//2. 创建服务对象 create 创建 server 服务
// request 意为请求. 是对请求报文的封装对象, 通过 request 对象可以获得请求报文的数据
// response 意为响应. 是对响应报文的封装对象, 通过 response 对象可以设置响应报文
const server = http.createServer((request, response) => {
response.end('hello world')
})
//3. 监听端口, 启动服务
server.listen(9000,()=>{
console.log('服务已经启动, 端口 9000 监听中...');
})
http.createServer
里的回调函数的执行时机: 当接收到HTTP 请求
的时候,就会执行。
http://127.0.0.1:9000
127.0.0.1是 回送地址 ,指
本地机
,一般用来测试使用。
ctrl + c
停止服务重启服务
才能生效 //设置响应头
response.setHeader('content-type', 'text/html;charset=utf-8')
Error: listen EADDRINUSE: address already in use :::9000
侦听端口
中找到9000端口
,然后记下它的PID
值;打开任务管理器,在详细信息
中找到对应的PID值
,右键结束任务
。5.HTTP
协议默认端口是 80 。HTTPS
协议的默认端口是 443, HTTP 服务开发常用端口有 3000,8080,8090,9000 等
想要获取请求的数据,需要通过 request
对象
//1. 导入 http 模块
const http = require('http');
//2. 创建服务对象
const server = http.createServer((request, response) => {
//获取请求的方法
console.log(request.method);
response.end('http'); //设置响应体
});
//3. 监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
返回的结果:
//1. 导入 http 模块
const http = require('http');
//2. 创建服务对象
const server = http.createServer((request, response) => {
//获取 HTTP 协议的版本号
console.log(request.httpVersion);
response.end('http'); //设置响应体
});
//3. 监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
//1. 导入 http 模块
const http = require('http');
//2. 创建服务对象
const server = http.createServer((request, response) => {
//获取请求的 url
console.log(request.url);// 只包含 url 中的路径与查询字符串
response.end('http'); //设置响应体
});
//3. 监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
返回的结果:
测试地址:
http://127.0.0.1:9000/search?name=hh&password=123
注意:
request.url
只能获取路径以及查询字符串,无法获取 URL 中的域名以及协议
的内容- 关于路径:如果访问网站的时候,只填写了 IP 地址或者是域名信息,此时请求的路径为
/
- 关于favicon.ico
:这个请求是属于浏览器自动发送的请求
//导入 http 模块
const http = require('http');
//1. 导入 url 模块
const url = require('url');
//创建服务对象
const server = http.createServer((request, response) => {
let res = url.parse(request.url);
console.log(res.pathname);
response.end('url');
});
//监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
测试地址:
http://127.0.0.1:9000/search?name=hh&password=123
我们先来看看url.parse(request.url)
不加第二个参数true的结果
//导入 http 模块
const http = require('http');
//1. 导入 url 模块
const url = require('url');
//创建服务对象
const server = http.createServer((request, response) => {
let res = url.parse(request.url);
console.log(res);
response.end('url');
});
//监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
let res = url.parse(request.url,true);
console.log(res);
query的值变成了一个对象,这样我们就可以通过
对象.
的形式拿到里面对应的值了
console.log(res.query.name);
parse(request.url)
来说是优化的。//导入 http 模块
const http = require('http');
//创建服务对象
const server = http.createServer((request, response) => {
//实例化 URL 的对象
let url = new URL(request.url, 'http://127.0.0.1');
console.log(url);
});
//监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
返回的结果:
URL {
href: 'http://127.0.0.1/search?name=hh&password=123',
origin: 'http://127.0.0.1',
protocol: 'http:',
username: '',
password: '',
host: '127.0.0.1',
hostname: '127.0.0.1',
port: '',
pathname: '/search',
search: '?name=hh&password=123',
searchParams: URLSearchParams { 'name' => 'hh', 'password' => '123' },
hash: ''
}
searchParams: URLSearchParams
查询字符串
,路径
的获取和之前是一样。//导入 http 模块
const http = require('http');
//创建服务对象
const server = http.createServer((request, response) => {
//实例化 URL 的对象
let url = new URL(request.url, 'http://127.0.0.1');
console.log(url);
//输出路径
console.log(url.pathname);
//输出 keyword 查询字符串
console.log(url.searchParams.get('name'));
response.end('url new');
});
//监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
//1. 导入 http 模块
const http = require('http');
//2. 创建服务对象
const server = http.createServer((request, response) => {
//获取 HTTP 的请求头
console.log(request.headers);
response.end('http'); //设置响应体
});
//3. 监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
返回的是一个对象
{
host: '127.0.0.1:9000',
connection: 'keep-alive',
'cache-control': 'max-age=0',
'sec-ch-ua': '"Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'sec-fetch-site': 'none',
'sec-fetch-mode': 'navigate',
'sec-fetch-user': '?1',
'sec-fetch-dest': 'document',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9'
}
{
host: '127.0.0.1:9000',
connection: 'keep-alive',
'sec-ch-ua': '"Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
'sec-ch-ua-platform': '"Windows"',
accept: 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'no-cors',
'sec-fetch-dest': 'image',
referer: 'http://127.0.0.1:9000/search?name=hh&password=123',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9'
}
注意事项:
request.headers
将请求信息转化成一个对象
,并将属性名都转化成了小写
既然返回的是一个对象,我们就可以通过
对象.
的形式获取其中的值。
console.log(request.headers.host);
request.on('data', function(chunk){})
request.on('end', function(){});
//1. 导入 http 模块
const http = require('http');
//2. 创建服务对象
const server = http.createServer((request, response) => {
//1. 声明一个变量
let body = '';
//2. 绑定 data 事件
request.on('data', chunk => {
body += chunk;
})
//3. 绑定 end 事件
request.on('end', () => {
console.log(body);
//响应
response.end('Hello HTTP');
});
});
//3. 监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
由于我们默认是
GET
请求,所以请求体默认是空的,我们通过form表单设置为POST
在请求一次。
//1. 导入 http 模块
const http = require('http');
//2. 创建服务对象
const server = http.createServer((request, response) => {
//获取请求的方法
let {method} = request;
//获取请求的 url 路径
let {pathname} = new URL(request.url, 'http://127.0.0.1');
response.setHeader('content-type','text/html;charset=utf-8');
//判断
if(method === 'GET' && pathname === '/login'){
//登录的情形
response.end('登录页面');
}else if(method === 'GET' && pathname === '/reg'){ // register 注册
response.end('注册页面');
}else{
response.end('Not Found');
}
});
//3. 监听端口 启动服务
server.listen(9000, () => {
console.log('服务已经启动.. 端口 9000 监听中....');
})
案例效果图:
//导入 http 模块
const http = require('http');
//创建服务对象
const server = http.createServer((request, response) => {
//1. 设置响应状态码
response.statusCode = 203
response.end('hh')
});
//监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
返回结果:
//导入 http 模块
const http = require('http');
//创建服务对象
const server = http.createServer((request, response) => {
//1. 设置响应状态码
response.statusCode = 404;
//2. 响应状态的描述
response.statusMessage = 'I love you'
response.end('hh')
});
//监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
返回的结果:
//导入 http 模块
const http = require('http');
//创建服务对象
const server = http.createServer((request, response) => {
//1. 设置响应状态码
// response.statusCode = 404;
// //2. 响应状态的描述
// response.statusMessage = 'I love you'
//3. 响应头
response.setHeader('content-type', 'text/html;charset=utf-8')
response.setHeader('Server','Node,js')
response.end('哈哈')
});
//监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
返回的结果:
头名有多个不同的头值
,那我们该如何设置呢?示例:
response.setHeader('coke','one,tow,three')
这样子设置是不行的,我们必须通过数组的形式来完成。
response.setHeader('coke',['one','tow','three'])
//导入 http 模块
const http = require('http');
//创建服务对象
const server = http.createServer((request, response) => {
//1. 设置响应状态码
// response.statusCode = 404;
// //2. 响应状态的描述
// response.statusMessage = 'I love you'
//3. 响应头
// response.setHeader('content-type', 'text/html;charset=utf-8')
// response.setHeader('Server','Node,js')
// response.setHeader('coke',['one','tow','three'])
//4. 响应体的设置
response.write('I')
response.write('love')
response.write('you')
response.end('hh')
});
//监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
write 和 end 的两种使用情况:
//1. write 和 end 的结合使用 响应体相对分散
response.write('xx');
response.write('xx');
response.write('xx');
response.end(); //每一个请求,在处理的时候必须要执行 end 方法的
//2. 单独使用 end 方法 响应体相对集中
response.end('xxx');
const http = require('http');
//创建服务对象
const server = http.createServer((request, response) => {
response.end(`
Document
`)//设置响应体
});
//监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
解决方法:
将响应体内容放在一个HTML文件中,通过fs模块来对此文件进行读取。
const http = require('http');
const fs = require('fs');
//创建服务对象
const server = http.createServer((request, response) => {
// 读取文件内容
let html = fs.readFileSync(__dirname + '/table1.html')
response.end(html)//设置响应体
});
//监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
没有
css样式
和js行为
,为什么会这样呢?
我们先来看看浏览器响应体返回的结果是什么
回调函数
,而回调函数里面只返回了一个html
,所以会导致这样的结果发生。const server = http.createServer((request, response) => {
// 读取文件内容
let html = fs.readFileSync(__dirname + '/table1.html')
response.end(html)//设置响应体
});
// 导入模块
const http = require('http')
const fs = require('fs')
const server = http.createServer((request, response) => {
// 获取路径
let { pathname } = new URL(request.url, 'http://127.0.0.1')
if (pathname === '/') {
let html = fs.readFileSync(__dirname + '/index.html')
response.end(html)
} else if (pathname === '/index.css') {
let css = fs.readFileSync(__dirname + '/index.css')
response.end(css)
} else if (pathname === '/index.js') {
let js = fs.readFileSync(__dirname + '/index.js')
response.end(js)
} else {
response.statusCode = 404
response.end('404,NOT FOUND')
}
})
server.listen(9000, () => {
console.log('服务已启动');
})
- 说到这其实又引申出另外一个问题,如果我们有很多个文件,那我们岂不是要对每个文件进行判断,这样是很麻烦的。
- 所以我们又想到了另外一种解决办法。
const http = require('http')
const fs = require('fs')
const path = require('path')
// 创建服务对象
const server = http.createServer((request, response) => {
//获取请求url的路径
let { pathname } = new URL(request.url, 'http://127.0.0.1')
//声明一个变量
let root = __dirname + '/page'
//拼接文件路径
let filePath = path.resolve(root + pathname)
//读取文件 fs 异步 API
fs.readFile(filePath, (err, data) => {
if (err) {
response.statusCode = 500
response.end('文件读取失败···')
return
}
response.end(data)
})
})
server.listen(9000, () => {
console.log('服务已启动');
})
循序渐进
的,首先获取 HTML
的内容, 然后解析 HTML 在发送其他资源的请求,如 CSS,Javascript,图片等。静态资源
是指 内容长时间不发生改变的资源 ,例如图片,视频,CSS 文件,JS文件,HTML文件,字体文件等动态资源
是指 内容经常更新的资源 ,例如百度首页,网易首页,京东搜索列表页面等 静态资源目录
,也称之为 网站根目录思考:vscode 中使用 live-server 访问 HTML 时, 它启动的服务中网站根目录是谁?
以打开的文件夹作为网站根目录
相对路径
与绝对路径
绝对路径可靠性强
,而且相对容易理解,在项目中运用较多第三种
域名
和ip
,用前两种方法会比较麻烦,一但改变,里面的链接路径全部要更改。域名
、ip
更改我都不受影响
。当前页面 URL 路径进行 计算
,得到完整 URL
后,再发送请求,学习阶 段
用的较多。http://www.atguigu.com/course/h5.html
在实际工作当中我们都是使用
绝对路径
,因为相对路径
是不可靠
,体现在于页面url的路径相关
,它是参照页面ur
l的,当页面url不正常
的时候,我们使用相对路径去获取资源就会异常
。
媒体类型
( 通常称为 Multipurpose Internet Mail Extensions 或 MIME 类型 )是一种标准,用来表示文档
、 文件
或字节流
的性质和格式。mime 类型结构: [type]/[subType]
例如: text/html text/css image/jpeg image/png application/json
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
类型,浏览器在遇到该类型的响应时,会对响应体内容进行独立存储,也就是我们常见的 下载 效果
我们来对之前的代码进行改进一下:
//导入 http 模块
const http = require('http');
const fs = require('fs');
const path = require('path');
//声明一个变量
let mimes = {
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'
}
//创建服务对象
const server = http.createServer((request, response) => {
if(request.method !== 'GET'){
response.statusCode = 405;
response.end('405 Method Not Allowed
');
return;
}
//获取请求url的路径
let {pathname} = new URL(request.url, 'http://127.0.0.1');
//声明一个变量
let root = __dirname + '/page';
// let root = __dirname + '/../';
//拼接文件路径
let filePath = root + pathname;
//读取文件 fs 异步 API
fs.readFile(filePath, (err, data) => {
if(err){
console.log(err);
//设置字符集
response.setHeader('content-type','text/html;charset=utf-8');
//判断错误的代号
switch(err.code){
case 'ENOENT':
response.statusCode = 404;
response.end('404 Not Found
');
case 'EPERM':
response.statusCode = 403;
response.end('403 Forbidden
');
default:
response.statusCode = 500;
response.end('Internal Server Error
');
}
return;
}
//获取文件的后缀名
let ext = path.extname(filePath).slice(1);
//获取对应的类型
let type = mimes[ext];
if(type){
//匹配到了 text/html;charset=utf-8
if(ext === 'html'){
response.setHeader('content-type', type + ';charset=utf-8');
}else{
response.setHeader('content-type', type);
}
}else{
//没有匹配到
response.setHeader('content-type', 'application/octet-stream');
}
//响应文件内容
response.end(data);
})
});
//监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
注:
1.响应头里面的字符集
权重比HTML页面中meta标签
的字符集
权重要高。
2.当css、js、img
等资源进入到网页当中去,会根据网页的字符集
进行处理,所以没必要给它们设置字符集。
GET 请求
的情况:
在地址栏直接输入 url 访问
点击 a 链接
link 标签引入 css
script 标签引入 js
img 标签引入图片
form 标签中的 method 为 get (不区分大小写)
ajax 中的 get 请求
POST 请求
的情况:
GET
和 POST
是 HTTP 协议请求的两种方式。
获取数据
,POST 主要用来提交数据
参数缀到 URL 之后
,在地址栏中输入 url 访问网站就是 GET 请求, POST带参数请求是将参数放到请求体中
一些,因为在浏览器中参数会
暴露`在地址栏有限制
,一般为 2K,而 POST 请求则没有大小限制
。尚硅谷2023版Node.js零基础视频教程,nodejs新手到高手