nodejs+mongodb笔记------第四章(HTTP请求、express框架、路由、中间件)

GET请求与POST请求

前言

HTTP请求,最初设定了八种方法(也称为“动作”)。这八种方法本质上没有任何区别。只是让请求,更加有语义而已。
八种方法分别为:OPTIONS、HEAD、GET、POST、PUT、DELETE、TRACE、CONNECT
这八种方法最终经过“岁月沉淀”后,常用的只有两种,即:GET和POST

GET

1. 含义:从指定的资源获取数据(一种“索取”的感觉)。
2. 什么时候使用GET请求较为合适?
    (1)单纯获取数据的时。
    (2)请求中不包含敏感数据时。

POST

1.含义:向指定的资源提交要被处理的数据(一种“交差”的感觉)。
2.什么时候使用POST请求较为合适?
    (1)传送相对敏感数据时。
    (2)请求的结果有持续性的副作用,例如:传递的数据要作为数据源写入数据库时。
备注:使用了POST不代表的绝对的安全。

常见的GET请求:

1.浏览器地址栏输入网址时(浏览器请求网页时时GET请求,且不可更改)
2.可以请求外部资源的html标签,例如:<img> <a> <link> <script>
3.发送Ajax时明确指出了使用GET请求
4.form表单提交时没有指明方式,默认使用GET

常见的POST请求:

1.发送Ajax时明确指出了使用POST方式
2.使用第三方发送Ajax请求库时明确指出用POST时
3.form表单提交时明确指出使用POST方式

二者的区别

express服务器

//1.引入express
let express = require('express')

//2.创建app服务对象
let app = express()
//隐藏服务器的具体实现
app.disable('x-powered-by')

//3.设置路由(这里配置的是后端路由)   路由可以理解为:key-value的组合,响应路由是一个匹配的过程。

//根路由
app.get('/',(request,response)=>{
  response.send('<h2>我是主界面</h2>')
})

//一级路由
app.get('/meishi',(request,response)=>{
  /*
  什么样的请求能交给这个回调函数处理?
      1.发送的请求必须为GET请求
      2.访问的URL中关键词是meishi
  备注:使用request.query只能获取查询字符串参数
  */
  console.log(request.query);
  response.send('<h2>我是美食界面</h2>')
})

//二级路由
app.get('/meishi/huoguo',(request,response)=>{
  /*
  什么样的请求能交给这个回调函数处理?
      1.发送的请求必须为GET请求
      2.访问的URL中关键词是meishi
  */
  response.send('<h2>我是美食---火锅界面</h2>')
})

app.post('/demo',(request,response)=>{
  /*
  * 备注:使用request.body能够获取POST请求过来的请求体中的参数,但是需要借助一个中间件。
  * */
  console.log(request.body);
  response.send('你发来的post请求,我收到了,这是我给你的响应')
})

app.get('/demo',(request,response)=>{
  response.send('你发来的get请求,我收到了,这是我给你的响应')
})

//4.绑定端口监听
app.listen(3000,(err)=>{
  if (!err) console.log('服务器启动成功了!')
  else console.log(err)
})

http协议

  • http协议是什么:超文本传输协议(属于应用层协议)
  • 特点:无状态,现在cookie解决了无状态的问题(早期网页开发时,用cookie解决,现在是cookie和session配合使用)
  • 作用:规定了服务器和客户端传递信息的规则(统称为报文,分为:请求报文、响应报文。)
  • 版本:
    • http 1.0 (老版本) ---------- 不支持长连接
    • http 1.1 (主流版本)--------- 优点:支持长连接,弊端:同时发送资源的数量过小。
    • http 2.0 (最新版) ---------- 同时发送资源的数量稍有提升。
  • 报文(请求报文、响应报文)的组成:
    1.报文首行
    2.报文头
    3.空行(仅仅作为一个分割)
    4.报文体

http报文与http状态码

1.分析GET请求报文(给服务器看的)

GET http://localhost:3000/meishi HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
DNT: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
空行
空行

报文首行

GET http://localhost:3000/meishi HTTP/1.1
    -请求类型 协议名://主机名:端口号/路由关键词 使用协议的版本

报文头

Host: localhost:3000
      --访问的主机名(地址,仅仅包含主机名+端口号)
      --防盗链、广告计费
Connection: keep-alive
      --告诉服务器,浏览器端支持长连接
Upgrade-Insecure-Requests: 1
      --告诉服务器,浏览器端支持https协议
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
      --用户代理,告知服务器你的浏览器内核以及品牌,早期的时候用于判断用户的浏览器是哪一个品牌,现在不可用了。
DNT: 1
      --禁止跟踪,告知服务器禁止跟踪,并不是写了该字段服务器就一定遵守。
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
      --告知服务器浏览器能接受的文件类型,q是资源的优先级,取值范围是0-1,1的权限最高,默认是1
Accept-Encoding: gzip, deflate, br
      --告诉服务器浏览器能支持的文件压缩格式
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
      --告诉服务器,浏览器能够接受的语言

空行

报文体

GET 请求没有报文体

2.分析POST请求报文(给服务器看的)

POST http://localhost:3000/demo HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Content-Length: 16
Cache-Control: max-age=0
Origin: http://localhost:63342
Upgrade-Insecure-Requests: 1
DNT: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng, */ *;q=0.8,application/signed-exchange;v=b3
Referer: http://localhost:63342/node/day04/1.express%E6%9C%8D%E5%8A%A1%E5%99%A8/demo.html?_ijt=tjfnb0cpos62ql8umjmm9v24ve
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: Webstorm-9af2238=09820128-3adb-43e4-8242-a6f65c9e523a

name=kobe&age=18

报文首行

POST http://localhost:3000/demo HTTP/1.1
    -

报文头

Host: localhost:3000
    --访问的主机名(地址,仅仅包含主机名+端口号)
Connection: keep-alive
    --告诉服务器,浏览器端支持长连接
Content-Length: 16
    --请求体的长度
Cache-Control: max-age=0
    --用于控制强缓存
Origin: http://localhost:63342
    --当前所处位置(主机位置+端口位置)
Upgrade-Insecure-Requests: 1
    --告诉服务器,浏览器端支持https协议
DNT: 1
    --禁止跟踪,告知服务器禁止跟踪,并不是写了该字段服务器就一定遵守。
Content-Type: application/x-www-form-urlencoded
    --标识该请求是来自于一个form表单,并且以urlencoded形式进行编码
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
    --用户代理,告知服务器你的浏览器内核以及品牌,早期的时候用于判断用户的浏览器是拿一个品牌,现在不可用了。
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,* / *;q=0.8,application/signed-exchange;v=b3
    --告知服务器浏览器能接受的文件类型,q是资源的优先级,取值范围是0-1,1的权限最高,默认是1
Referer: http://localhost:63342/node/day04/1.express%E6%9C%8D%E5%8A%A1%E5%99%A8/demo.html?_ijt=tjfnb0cpos62ql8umjmm9v24ve
    --在当前url下发出去的请求,是一个完整url,也可以做防盗链、同时也可以做广告计费
Accept-Encoding: gzip, deflate, br
    --告诉服务器浏览器能支持的文件压缩格式
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
    --告诉服务器,浏览器能够接受的语言
Cookie: Webstorm-9af2238=09820128-3adb-43e4-8242-a6f65c9e523a
    --Webstorm帮你“种”的一个cookie

空行

报文体

name=kobe&age=18
    --携带过去的数据,以urlencoded进行编码

3.分析响应报文(给浏览器看的)

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 27
ETag: W/"1b-q8c2w67PUz7P4t0CNbDw9xqw6bo"
Date: Tue, 23 Jul 2019 06:20:18 GMT
Connection: keep-alive

<h2>我是美食界面</h2>

报文首行

HTTP/1.1 200 OK
协议名/协议版本 状态码

报文头

X-Powered-By: Express
    -服务器所使用的框架
Content-Type: text/html; charset=utf-8
    -告诉浏览器解析文件的方式;文件编码方式
Content-Length: 27
    -响应体的长度
ETag: W/"1b-NFYx6TA4AihYceTsWYDlBLJferg"
    -协商缓存(资源唯一标识)
Date: Tue, 23 Jul 2019 06:20:18 GMT
    -日期
Connection: keep-alive
    -告诉浏览器,服务器支持长连接

空行

报文体

<h2>我是美食界面</h2>

4.http状态码(服务器给客户端的东西)

作用:

  • 告诉客户端,当前服务器处理请求的结果

http状态码的分类

  • 1xx : 服务器已经收到了本次请求,但是还需要进一步的处理才可以。
  • 2xx : 服务器已经收到了本次请求,且已经分析、处理等........最终处理完毕!
  • 3xx : 服务器已经接收到了请求,还需要其他的资源,或者重定向到其他位置,甚至交给其他服务器处理。
  • 4xx :一般指请求的参数或者地址有错误, 出现了服务器无法理解的请求(一般是前端的锅)。
  • 5xx :服务器内部错误(不是因为请求地址或者请求参数不当造成的),无法响应用户请求(一般是后端人员的锅)。

常见的几个状态码

  • 200 :成功(最理想状态)
  • 301 :重定向,被请求的旧资源永久移除了(不可以访问了),将会跳转到一个新资源,搜索引擎在抓取新内容的同时也将旧的网址替换为重定向之后的网址;
  • 302 :重定向,被请求的旧资源还在(仍然可以访问),但会临时跳转到一个新资源,搜索引擎会抓取新的内容而保存旧的网址。
  • 304 :请求资源重定向到缓存中(命中了协商缓存)。
  • 404 :资源未找到,一般是客户端请求了不存在的资源。
  • 500 :服务器收到了请求,但是服务器内部产生了错误。
  • 502 :连接服务器失败(服务器在处理一个请求的时候,或许需要其他的服务器配合,但是联系不上其他的服务器了)。

经典面试题

问题:从用户输入URl按下回车,一直到用户能看到界面,期间经历了什么?

一、DNS解析(缓存):
    1.找浏览器DNS缓存解析域名
    2.找本机DNS缓存:ipconfig/displaydns > C:/dns.txt
    3.找路由器DNS缓存
    4.找运营商DNS缓存(百分之80的DNS查找,到这一步就结束)
    5.递归查询,(最不愿意看到的事,查询的是全球13台DNS根服务器中的一个)

二、进行TCP(协议)连接,三次握手(根据上一步请求回来的ip地址,去联系服务器)
    第一次握手:由浏览器发给服务器,我想和你说话,你能“听见”嘛?
    第二次握手:由服务器发给浏览器,我能听得见,你说吧!
    第三次握手:由浏览器发给服务器,好,那我就开始说话。

三、发送请求(请求报文)

四、得到响应(响应报文)

五、浏览器开始解析html
      --预解析:将所有外部的资源,发请求出去
      --解析html,生成DOM树
      --解析CSS,生成CSSOM树
      --合并成一个render树
      --js是否操作了DOM或样式
          --有:进行重绘重排(不好,1.尽量避免;2.最小化重绘重排)
          --没有:null
      --最终展示界面

六、断开TCP连接,四次挥手(确保数据的完整性)
      第一次挥手:由浏览器发给服务器,我的东西接受完了,你关闭吧。
      第二次挥手:由服务器发给浏览器,我还有一些东西没接收完,你等一会,我接收好了我告诉你
      第三次挥手:由服务器发给浏览器,我接收完了,你断开吧
      第四次挥手:由浏览器发给服务器,好的,那我断开了。

路由的使用

//1.引入express
let express = require('express')

//2.创建app服务对象
let app = express()

//3.设置路由
//根路由
app.get('/',(request,response)=>{
  /*
  * request:
  *   request.query 获取查询字符串的参数,拿到的是一个对象
  *   request.params    获取参数路由的参数,拿到的是一个对象
  *   request.body  获取post请求体,拿到的是一个对象(要借助一个中间件)
  *   request.get(xxxx) 获取请求头中指定key对应的value
  *
  * response:
  *     response.send() 给浏览器做出一个响应
        response.end()  给浏览器做出一个响应(不会自动追加响应头,容易乱码)
        response.download() 告诉浏览器下载一个文件(相对路径)
        response.sendFile() 给浏览器发送一个文件(绝对路径)
        response.redirect() 重定向到一个新的地址(url)
        response.set(header,value)  自定义响应头内容
        response.get()  获取响应头指定key对应的value
        res.status(code)    设置响应状态码

  * */
  //console.log(request.query);
  //console.log(request.get('host'))
  response.set('demo',123)
  console.log(response.get('demo'));
  response.send('ok')
  //response.download('./public/队列.jpg')
  //response.sendFile(__dirname+'/public/demo.html')
  //response.sendFile(__dirname+'/public/demo.zip')
  //response.redirect('https://www.baidu.com')
})

//一级路由
app.get('/meishi',(request,response)=>{
  console.log(request.params);
  response.send('我是美食路由的反馈')
})

//二级路由
app.get('/meishi/huoguo',(request,response)=>{
  console.log(request.params);
  response.send('我是美食--火锅路由的反馈')
})


//参数路由
app.get('/meishi/:id',(request,response)=>{
  console.log(request.params);
  response.send('我是参数路由的反馈')
})

//4.绑定端口监听
app.listen(3000,(err)=>{
  if (!err) console.log('服务器启动成功了')
  else console.log(err)
})

中间件

/*
 中间件:
     概念:本质上就是一个函数,包含三个参数:request、response、next

 作用:
        1)  执行任何代码。
        2)  修改请求和响应对象。
        3)  终结请求-响应循环。
        4)  调用堆栈中的下一个中间件或路由。
  分类:
        1)  应用(全局)级中间件(过滤非法的请求,例如防盗链)
              --第一种写法:app.use((request,response,next)=>{}
              --第二种写法:使用函数定义
        2)  第三方中间件(通过npm下载的中间件,例如body-parser)
              --app.use(bodyParser.urlencoded({extended:true}))
        3)  内置中间件(express内部封装好的中间件)
              --app.use(express.urlencoded({extended:true}))
              --app.use(express.static('public'))
        4)  路由器中间件 (Router)
              --后面会说
   备注:
        1.在express中,定义路由和中间件的时候,根据定义的顺序(代码的顺序),将定义的每一个中间件或路由,
        放在一个类似于数组的容器中,当请求过来的时候,依次从容器中取出中间件和路由,进行匹配,如果匹配
        成功,交由该路由或中间件处理。
        2.对于服务器来说,一次请求,只有一个请求对象,和一个响应对象,其他任何的request和response都是对二者的引用。
 */

let express = require('express')
let bodyParser = require('body-parser')

let app = express()
//使用body-parser中间件解析post请求过来的请求体参数为一个对象,随后挂载到request上
//app.use(bodyParser.urlencoded({extended:true}))

//内置中间件 ---- 解析post请求过来的请求体参数为一个对象,随后挂载到request上
app.use(express.urlencoded({extended:true}))

//内置中间件 ---- 暴露静态资源
app.use(express.static('public'))

//全局中间件的第一种写法
/*app.use(function (request,response,next) {
 if(request.get('host') !== 'localhost:3000'){
   response.send('禁止发送非法请求')
 }else{
   next() //让下一个匹配的路由或中间件生效
 }
})*/

//全局中间件的第二种写法
function myMiddleWare(request,response,next) {
  if(request.get('host') !== 'localhost:3000'){
    response.send('禁止发送非法请求')
  }else{
    request.demo = 123
    next() //让下一个匹配的路由或中间件生效
  }
}

app.get('/',myMiddleWare,(request,response)=>{
  console.log(request.demo);
  response.send('我是根路由的响应')
})

app.get('/meishi',(request,response)=>{
  response.send('我是美食路由的响应')
})

app.post('/demo',(request,response)=>{
  console.log(request.body)
  response.send('post请求得到回应了')
})

/*app.get('/index',(request,response)=>{
  response.sendFile(__dirname+'/public/index.html')
})*/


app.listen(3000,(err)=>{
  if(!err)console.log('ok')
  else console.log(err)
})
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。