服务器基础概念
URL
URL,统一资源定位符
URL组成
传输协议://服务器IP或域名:端口/资源所在位置标识
http:超文本传输协议,提供了一种发布和接受HTML页面的方法
域名:由于IP地址难于记忆,所以产生了域名的概念,所谓域名就是平时上网所使用的网址。
http://www.itheima.com => http://124.165.219.100/
虽然在地址栏中输入的是网址, 但是最终还是会将域名转换为ip才能访问到指定的网站服务器
浏览器自动加默认端口
先通过IP或域名找到服务器,再根据端口找需要的服务
客户端服务器端说明
客户端服务器端使用同一台电脑
客户端:浏览器
服务器端:node
本机域名:localhost
本地IP:127.0.0.1
创建web服务器
系统模块 http
const http = require('http');
const app = http.createServer();
//当客户端有请求来时
app.on('request',(req, res)=>{
//获取请求方式
// req.method
console.log(req.method);
res.end('<h2>hello user</h2>');
})
//监听端口向外界发送服务
app.listen(3000);
console.log('网站服务器启动成功')
HTTP协议
规定了客户端和服务器端请求和响应的标准
规定了客户端和服务器端如何进行请求响应
超文本:除了文字以外包含图片视频音频(html)
报文
请求和响应的过程中传递的数据块叫报文,
浏览器开发者工具 network 可以查看报文信息
请求报文
1.请求方式
- GET 请求数据
- POST 发送数据
浏览器输入网址是GET请求
发送post请求需要form表单
form.html:
<!--form标签的属性
method: 指定当前表单提交方式 (不写默认是GET)
action: 指定当前表单提交的地址
-->
<form method="post" action="http://localhost:3000">
<input type="submit" name = "">
</form>
req.method 返回 POST/GET
可以根据不同的请求方式做不同的事
app.on('request', (req, res) => {
req.headers // 获取请求报文
req.url // 获取请求地址 端口后的 '/index'
req.method // 获取请求方法
});
根据不同的url执行不同程序
req.url 在不输入请求地址的时候返回 ‘/’
req.headers 返回对象 中括号写键值获取特定报文
请求报文和响应报文都是键值对
响应报文
writeHead设置状态码和响应头信息
- HTTP状态码:
- 200 请求成功 (默认值)
- 404 请求的资源没有被找到
- 500 服务器端错误
- 400 客户端请求有语法错误
status是状态码
res参数是响应对象
res.writeHead 设置状态码 不写默认是200
app.on('request',(req,res)=>{
res.writeHead(404);
})
- 内容类型
根据返回内容设置
- text/html
- text/css
- application/javascript
- image/jpeg
- application/json
writeHead
还有第二个参数是响应报文信息(对象)
res.writeHead(200,{
'content-type': 'text/plain' // 纯文本 不指定的默认值
});
编码也加到content-type 中
'content-type': 'text/html;charset=utf8'
后面整体不能有空格!!
(都是字符串加引号)
HTTP请求与响应处理
请求参数
客户端向服务器发送请求时需要携带信息,即通过请求参数的形式传递到服务器端
GEt请求
请求地址后有个问号 后面就是传递参数的键值对
http://localhost:3000/index?name=zhangsan&age=20
name和age两个键 &号隔开
上面这个链接的 req.url = ‘/index?name=zhangsan&age=20’
可以用字符串方法截取问号后面的数据但是麻烦
引入url模块↓
参数获取需要借助系统模块url,url模块用来处理url地址
const url = require('url');
url下面有parse方法
url.parse(req.url, true)返回一个对象
方法接收两个参数:
1. 要解析的url地址
2. 将查询参数解析成对象形式
返回的对象有一项是query 加true后 query变成对象
{
name: zhangsan,
age: 20
}
不加true的query是"name=zhangsan&age=20"(字符串)
/index?name=li&age=20 (req.url)
url.parse(req.url)的返回结果:
Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '?name=li&age=20',
query: 'name=li&age=20',
pathname: '/index',
path: '/index?name=li&age=20',
href: '/index?name=li&age=20'
}
url.parse(req.url,true)的返回结果:
Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '?name=li&age=20',
query: [Object: null prototype] { name: 'li', age: '20' },
pathname: '/index',
path: '/index?name=li&age=20',
href: '/index?name=li&age=20'
}
区别在query
req.url既包含了请求地址(req.pathname)又包含了请 求参数
POST请求
GET请求的参数是在地址栏中的 而POST请求的参数在请求报文当中
通过form表单进行POST请求
chrome network中的formdata
参数格式和GET请求一样 name=li&age=20
由于参数不在地址栏不能用req.url接收
post参数由事件接收: data end
const http = require('http');
const app = http.createServer();
app.on('request',(req, res)=>{
//post参数可以无上限 所以不是一次接收
let postParams =''; // 用于拼接每次传递的参数
//借助两个事件:
//data 有请求参数传输的时候
//end 参数传输完成的时候
req.on('data',(params)=>{
postParams += params;
})
req.on('end',()=>{
console.log(postParams);
}
//客户端的每次请求服务器端都得有响应 (end) 否则客户单处于等待的状态
res.end('ok');
})
//监听端口向外界发送服务
app.listen(3000);
现在的postParams是name=li&age=20
这样的字符串
于是又提供了一个内置模块来处理这种字符串
querystring模块
该模块的parse方法用来解析字符串成对象
const querystring = require('querystring');
...
console.log(querystring.parse(postParams));
现在输出的结果是
{name:'li', age:'20'}
- POST小结
- 参数被放置在请求体中进行传输(请求报文)
- 获取POST参数需要使用data事件和end事件 (多次data事件)
- 使用querystring系统模块将参数转换为对象格式
路由
路由代码是指客户端请求地址与服务器端程序代码的对应关系,即请求什么响应什么(判断代码)
通过req.url和url模块的parse方法获取不带参数的地址(pathname)
// 1.引入系统模块http
// 2.创建网站服务器
// 3.为网站服务器对象添加请求事件
// 4.实现路由功能
const http = require('http');
const app = http.createServer();
const url = require('url');
// 1.获取客户端的请求方式
// 2.请求地址
app.on('request',(req,res)=>{
const method = req.method.toLowerCase(); // req.method返回的是大写 用方法转换成小写方便判断
const pathname = url.parse(req.url).pathname;
res.writeHead(200,{
'content-type': 'text/html;charset=utf8'
});
if(method == 'get'){
if(pathname =='/' || pathname =='/index'){
res.end('欢迎来到首页');
})else if(pathname == '/list'){
res.end('欢迎来到列表页');
}else{
res.end('您访问的页面不存在');
}
}else if (method == 'post'){
//...
}
});
app.listen(3000);
console.log('服务器启动成功');
资源访问
静态资源
服务器不需要处理,可以直接响应给客户端的资源,比如html、css、image等
eg. http://www.baidu.com/images/logo.png
请求路径(网址)只是字符串 和服务器的文件目录可以不一样
所以需要拼接路径 补全没有的文件夹 拼接成实际的硬盘路径
成功时响应报文类型需要规定 但不知道具体类型是什么 有可能是各种类型
第三方模块mime:
根据当前请求路径分析出文件类型
mine.getType(路径)
方法返回文件类型 ‘text/html’
const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
const mime = require('mime');
const app = http.createServer();
app.on('request', (req, res) => {
// 获取用户的请求路径
let pathname = url.parse(req.url).pathname;
pathname = pathname == '/' ? '/default.html' : pathname;
// 将用户的请求路径转换为实际的服务器硬盘路径
let realPath = path.join(__dirname, 'public' + pathname);
let type = mime.getType(realPath) //文件类型
// 读取文件
fs.readFile(realPath, (error, result) => {
// 如果文件读取失败
if (error != null) {
res.writeHead(404, {
'content-type': 'text/html;charset=utf8'
})
res.end('文件读取失败');
return;
}
// 成功时要告诉资源类型
res.writeHead(200, {
'content-type': type
})
res.end(result); //读取内容返回给浏览器
});
});
app.listen(3000);
console.log('服务器启动成功')
如果不申明 network中就没有’content-type’
新的浏览器没有也能显示正常 但旧版可能是隐患
所以必须要指定返回类型
动态资源
相同的请求的请求地址 传递不同的请求参数
http://www.a.com/title?id=1
http://www.a.com/title?id=2
两个的参数id不一样返回的也不一样