谈谈前后端跨域,http原理等问题

01-跨域

1.1-跨域介绍

  • 1.跨域固定报错格式

    • 只要是出现跨域问题,浏览器就会出现一个固定格式(没有之一)的报错信息
      • Access to XMLHttpRequest at '服务器url地址' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
  • 2.什么是跨域?

    • 浏览器使用 ajax,如果请求了的**接口地址** 和 当前**打开的页面** 地址 不同源 称之为跨域
      • (1)ajax : 浏览器只有使用ajax发送请求才会出现跨域。 href属性与src属性不会出现跨域
      • (2) 接口地址 : ajax请求的url
      • (3)打开的页面:当前页面的window.location.href
      • (4)不同源 : 浏览器使用ajax,向不同源的接口发送请求,称之为 跨域访问
  • 3.什么是同源?

    • MDN官方文档传送门:https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy
    • 同源定义 : 两个url地址的 协议主机端口 均一致 (&&)
      • 协议:http , https , file
      • 主机 : 域名或者ip地址 (127.0.0.1)
      • 端口 :3000, 4399
    • 不同源定义: 两个url地址,协议 主机 端口任何一个不一致 (||)
      • http:127.0.0.1:3000/abc
      • http:127.0.0.1:3000/efg
      • https:127.0.0.1:3000/efg
      • http:127.0.0.1:3000/hero/add
      • http:127.0.0.1:4399/hero/add
      • http:127.0.0.1:4399/hero/all
  • 4.为什么要有同源与不同源?

    • 出于安全考虑,浏览器不允许,页面向不同源的接口请求数据,因为如果 接口 和 网页不同源,浏览器认为是2个不同的 服务器,
    • 不同的服务器中内容是不可控的,不允许访问了
    • 总结说人话: 浏览器为了保护你的电脑安全
      • 举个栗子: 你去肯德基店里点餐,店员只允许你点肯德基的产品(炸鸡,可乐,上校鸡块),如果此时你在肯德基店里面点麦当劳的产品,浏览器会认为你是坏人,就会让保安把你赶出去
  • 5.跨域解决方案介绍

    • 跨域是前端工作中不可避免的问题:我们经常会出现请求不同源接口的情况,为了能够获取数据,解决跨域的问题方案也有很多,但是常用的就两种
    • 第一种 : CORS
      • 目前的主流方案,也是最简单的方案
    • 第二种:JSONP
      • 曾经的跨域杀手,专治各种跨域问题。现在慢慢的淡出历史舞台
      • PS:面试官特别喜欢问这个,因为这个有一定的技术难度,也能体现一个人的实际开发经验
<!-- 

1.跨域固定报错格式
    * 只要是出现跨域问题,浏览器就会出现一个固定格式(没有之一)的报错信息
    * Access to XMLHttpRequest at '服务器url地址' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

2.什么是跨域
    * 浏览器使用 ajax时,如果请求了的接口地址 和  当前打开的页面 地址  不同源 称之为跨域
        * ajax : 浏览器只有使用ajax发送请求才会出现跨域。href属性与src属性不会出现跨域
    * 接口地址 : ajax请求的url
    * 打开的页面:当前页面的window.location.href
    * 不同源  : 浏览器使用ajax,向不同源的接口发送请求,称之为 跨域访问 

3.什么是同源?
    * 同源定义 : 两个url地址的 协议 与 主机 与 端口 均一致 (&&)
        * 协议:http , https , file
        * 主机 : 域名或者ip地址 (127.0.0.1)
        * 端口 :3000, 4399
    * 不同源定义: 两个url地址,协议 主机 端口任何一个不一致 (||)
        * 当前页面:http:127.0.0.1:3000/abc
        * 同源(协议 ip 端口一致):http:127.0.0.1:3000/efg
        * 不同源(协议不一致): https:127.0.0.1:3000/efg
        * 不同源(ip不一致): http:192.0.0.1:3000/efg
        * 不同源(端口一致): http:127.0.0.1:4399/abc

4.为什么要有同源与不同源?
    * 浏览器为了用户安全 : 不允许,页面向不同源的接口请求数据,
    因为如果 接口 和 网页不同源,浏览器认为是2个不同的 服务器,

5.跨域的几种解决方案
    * 常用: CORS
    * 面试官喜欢问:JSONP
    * 了解即可: 浏览器主动设置允许跨域(需要用户主动设置,只对当前设置浏览器生效)
 -->
  • 前端代码
<!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>
</head>

<body>
    <a href="http://127.0.0.1:3000/abc">点我发请求</a>
    <br>
    <button>点我发请求</button>

    <script>
        document.querySelector('button').addEventListener('click', function () {
            //(1).实例化ajax对象
            let xhr = new XMLHttpRequest()
            //(2).设置请求方法和地址
            //get请求的数据直接添加在url的后面 格式是 url?key=value
            xhr.open('get', 'http://127.0.0.1:3000/abc')
            //(3).发送请求
            xhr.send()
            //(4).注册回调函数
            xhr.onload = function () {
                console.log(xhr.responseText)
            }
        })
    </script>
</body>

</html>
  • 服务器代码
//1.导入模块
const express = require('express')

//2.创建服务器
/* express() 相当于http模块的http.createServer() */
const app = express()


//3.接收客户端请求

app.get('/abc',(req,res)=>{
    //响应客户端数据
    res.send({
        status:200,
        msg:'请求成功',
        data:{name:'张三',age:20}
    })
})



//4.开启服务器
app.listen(3000,()=>{
    console.log('服务器启动成功')
})

1.2-跨域解决方案一: CORS

  • 注意:CORS技术在实际工作中由后端人员来实现,前端不需要做任何事情

1-CORS工作原理介绍

  • CORS :全称cross origin resource share (资源共享)
  • 工作原理: 服务器 在返回响应报文的时候,在响应头中 设置一个允许的header
    • res.setHeader('Access-Control-Allow-Origin', '*');
      • 这行代码的意思是:服务器对浏览器说:老铁我是个好人,不要拒绝我!

2-express使用中间件cors : 给所有的res添加默认请求头Access-Control-Allow-Origin

  • express有一个自带的中间件cors,它的作用是自动给每一个res设置默认请求头

    • 这样就不用我们自己每一个接口都要设置一次了
  • https://www.npmjs.com/package/cors

  • 用法介绍

  • cors中间件是最简单的中间,底层原理如下

app.use((req, res, next) => {//任何请求都会进入这个use中间件
  res.setHeader('Access-Control-Allow-Origin', '*')//设置响应头
  next()//执行下一个中间件
})

1.3-跨域解决方案二:原生jsonp

  • 了解jsonp原理,首先一定要明白script标签的src属性做了什么事情

    • (1)scr属性会给服务器发送请求, 请求一个js文件
    • (2)浏览器会解析执行这个js文件里面的代码
      • 如果浏览器直接返回js代码,浏览器会立即执行
  • 1.JSONP的核心原理:如果script标签的src属性的请求,服务器返回的是一个函数调用。则浏览器会执行这个函数

    • 这是浏览器script标签的一个的漏洞(历史遗留问题)
  • 2.实际开发中JSONP的工作流程

    • (1)设置script标签的src属性,向一个不同源的接口发送一个get请求
      • JSONP只支持get请求,不支持post
    • (2)src属性发送请求时,在参数中额外携带一个callback的参数,参数值是一个在页面中预先定于好的函数名
      • callback : 这是发明jsonp技术的人提出的一个君子之约,只要是jsonp前端程序员都统一将参数名定义为callback
        • PS:别的参数也行,只要和服务器协商好
      • callback属性值:预先定义的函数名,这个函数必须要在script标签之前定义
    • (3)服务器接收到请求之后,获取callback的参数值
    • (4)服务器将要响应的数据拼接成 函数调用格式,通过传参的方式将响应数据返回给浏览器
  • 3.JSONP与CORS区别

    • CORS:
      • 服务器返回响应头,前端无需任何处理
      • 简单快捷,支持所有请求方式
    • JSONP
      • 浏览器:自定义响应回调函数,使用script标签的src请求
        • 利用浏览器的src属性没有跨域这一限制特点
      • 服务器:接收callback参数,返回函数调用
      • 处理复杂,并且只支持get请求
        • 原因:get请求参数直接在url后面拼接,而post请求参数是放在请求体中

02-HTTP原理

1.1-协议

  • 1.协议概念: 制定客户端与服务器之间的通讯规则。不同的协议的作用也不同。

  • 2.http协议

    ● HTTP(HyperText Transfer Protocol) 超文本传输协议。

    ● 协议双方: 浏览器与web服务器

    ● 请求由浏览器发起的

    ● HTTP 协议中明确规定了请求数据和响应数据的格式(报文)

    ○ 浏览器 请求 资源 要遵守 http 协议: 请求报文(请求行,请求头,请求体)

    ○ 服务器 返回 资源 要遵守 http 协议: 响应报文(响应行,响应头,响应体)

1.2-端口

  • 1.一个IP地址的端口可以有65536个,范围是从[0,65535])。不同的端口被不同的软件占用,以提供不同的服务。
    • ip地址: 找到你的电脑 ( 一台电脑只有一个ip )
    • 端口号: 找到电脑上具体的软件 ( 一台电脑可以有很多个端口号 )
  • 2.一台电脑可以通过安装多个服务器端软件来提供服务,比如Web服务、FTP服务、SMTP服务等。显然,仅仅通过ip地址是无法区分不同的服务的,这里就需要用到 “IP地址+端口号”来区分不同的服务
  • 3.通俗的来讲:
    • 一台电脑只有一根网线,只会有一个ip. 所有的网络传输都是通过这根网线传输的, 但是电脑上有很多软件,你的操作系统是怎么知道这个网络请求是发给哪一个软件的。所以每一个软件都会有一个端口号。
    • 端口: 一个软件服务对应一个端口
    • 通过netstat -a -n -o 查看端口使用情况

1.3-Content-Type

  • 1.content-type作用
    • 在http协议中,content-type用来告诉对方本次传输的数据的类型是什么。
  • 2.请求头中的content-type
    • 在请求头中设置content-type来告诉服务器,本次请求携带的数据是什么类型的
  • 3.响应头中的content-type
    • 在响应头中设置content-type来告诉服务器,本次返回的数据是什么类型的
  • 4.常见的conteng-type
    • .html:res.setHeader('content-type', 'text/html;charset=utf8')
    • .css:res.setHeader('content-type', 'text/css;charset=utf8')
    • .js:res.setHeader('content-type', 'application/javascript')
    • .png:res.setHeader('content-type', 'image/png')
    • json数据:res.setHeader('content-type', 'application/json;charset=utf-8')

其它类型,参考这里:https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types

1.4-statuCode

  • 开发中,关于接口文档的状态码,一般有两种处理方式(都可以,前提是保持风格一致。 类似于js中的分号,要么都加,要么都不加)

    • 方式一: 状态码放在响应行里面 res.sendStatus(400)

      • 前端影响: 如果是2xx,则会执行axios的then方法。 如果是4xx,则会执行axios的catch方法。并且浏览器还会爆红。
    • 方式二:状态码放在响应体中, 这种方式前端axios只会then方法。 前端需要自行判断状态码来写业务逻辑。

  • 1.statuCode作用: 响应状态码

1.5-拓展介绍-RESTful接口(了解)

  • 1.后台接口需要有一个统一的规范

    • 网络应用程序,分为前端和后端两个部分。当前的发展趋势,就是前端设备层出不穷(手机、平板、桌面电脑、其他专用设备…)。因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信。这导致API构架的流行,甚至出现"APIFirst"的设计思想。RESTful API是目前比较成熟的一套互联网应用程序的API设计理论。
  • 以前的规范: 只有get和post

//------------下面是普通的api设计---------------

app.get('/getarticle',(req,res)=>{
    res.send('获取')
})

app.post('/addarticle',(req,res)=>{
    res.send('添加')
})

app.post('/delarticle',(req,res)=>{
    res.send('删除')
})
app.post('/updatearticle',(req,res)=>{
    res.send('编辑')
})
  • 2.REST(Representational State Transfer): 指定增删改查规范
    • 表述性状态转换,REST指的是一组架构约束条件和原则。 如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。REST本身并没有创造新的技术、组件或服务,而隐藏在RESTful背后的理念就是使用Web的现有特征和能力, 更好地使用现有Web标准中的一些准则和约束。
接口名:localhost:8080/articles
类型:get
功能:获取文章信息

接口名:localhost:8080/articles
类型:post
功能:添加新文章

接口名:localhost:8080/articles
类型:delete
功能:删除文章

接口名:localhost:8080/articles
类型:put/patch
功能:编辑文章

RESTful设计是:

● 通过URL设计资源。接口名一般都是名词,不包含动词。
● 请求方式(get,post,delete,put)决定资源的操作类型

03-nodejs实现服务端重定向[课外拓展]

  • 服务端重定向常见于某些网站引导登陆注册的功能(当我们访问网站首页的时候,会跳转到登陆注册的界面)

  • 服务端的重定向功能主要由响应头的302状态码来实现

  • nodejs原生重定向和express重定向实现方式,原理相同,但是代码略有区别(两者只能二选一,不能混用)

/* 服务器重定向原理: 响应返回302状态码 + 响应头设置location修改浏览器地址 */
//(1)原生写法
// //设置响应状态码+响应头
// res.writeHead(302,{
//     'location':'http://127.0.0.1:3000'
// })
// //结束响应
// res.end()

//(2)express写法
res.setHeader('location','http://127.0.0.1:3000')//响应头
res.status(302).send()//设置302重定向 然后 结束响应
//1.导入模块
const http = require('http');

const fs = require('fs');

const path = require('path');

//2.创建服务器
let server = http.createServer((req,res)=>{
    console.log(req.url);
    //请求路径
    let urlPath = req.url;
    //请求方法
    let  method = req.method;

    if(req.url === '/'){
        //302表示重定向
        //(1)设置重定向地址
        res.writeHead(302, {
            'Location': 'login'  //键值对,键表示客户端浏览器将进行重定向  值:表示客户端浏览器重定向的请求
            //add other headers here...
        });
        //(2)响应本次请求
        res.end();
    }
    //登陆页
    if(req.url === '/login'){
        fs.readFile(path.join(__dirname,'login.html'),function(err,data){
            if(err){
                throw err;
            }
            res.end(data);
        });
    };
});


//3.开启服务器
server.listen(3000,  ()=> {
    console.log('服务器启动成功');
});

04-前端刷新验证码案例

​ 验证码刷新思路

(1)浏览器发送ajax请求

(2)服务器返回图片路径

(3)浏览器将服务器返回的图片路径赋值给img标签的src属性

  • 服务器代码
//1.导入模块
const express = require('express')

//2.创建服务器
/* express() 相当于http模块的http.createServer() */
const app = express()

//托管静态资源
app.use(express.static('images'))

//3.缓存中间件 : 给每一个接口添加跨域响应头
const cors = require('cors')
app.use(cors())



//3.接收客户端请求


app.get('/code', (req, res) => {
    console.log(req.url)
    
    //随机返回图片
    let index = Math.ceil(Math.random()*10)
    res.send(`http://127.0.0.1:3000/0${index}.jpg`)
})



//4.开启服务器
app.listen(3000, () => {
    console.log('服务器启动成功')
})
  • 浏览器代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <img src="./images/01.jpg" alt="" width="200px">
    <br>
    <button>点我刷新验证码</button>

    <script>
        document.querySelector('button').addEventListener('click', function () {
            //(1).实例化ajax对象
            let xhr = new XMLHttpRequest()
            //(2).设置请求方法和地址
            //get请求的数据直接添加在url的后面 格式是 url?key=value
            xhr.open('get', 'http://127.0.0.1:3000/code')
            //(3).发送请求
            xhr.send()
            //(4).注册回调函数
            xhr.onload = function () {
                console.log(xhr.responseText)
                document.querySelector('img').src =xhr.responseText
            }
        })
    </script>
</body>

</html>

05-服务器相关面试题

1.1-为什么post请求会发送两次请求?

  • 1.第一次请求为options预检请求,状态码为:204
    • 作用:主要有两个,回答任意一个。或两个都回答。 都可以
      • 作用1: 询问服务器是否支持修改的请求头,如果服务器支持,则在第二次中发送真正的请求
      • 作用2: 检测服务器是否为同源请求,是否支持跨域
        • 相关资料:http://www.ruanyifeng.com/blog/2016/04/cors.html
  • 2.第二次为真正的post请求

1.2-说说几个前端web优化方案

  • 为什么面试官喜欢问这个?

    • 因为当一个网页的web请求太多的时候,会影响这个网页的加载速度。从而影响用户体验。 对于公司产品而言,优化web是非常有必要的
  • 1.减少HTTP请求数

    • 这是必须要放在第一个回答的,因为这是优化web最优方案
    • 具体示例 : 精灵图、多个文件合并成一个
      • 精灵图适用场景 : (1)图片不经常更换的,比如按钮图标 (2)图片不能太大
  • 2.资源压缩

    • 就是一般我们第三方包有一个min版本,就是通过压缩文件体积来优化web
  • 具体示例 :响应min文件

  • 3.合理利用浏览器缓存

    • 某些接口的数据是固定的,服务器就没有必要每一次都响应数据。可以让浏览器进行缓存。
    • 具体示例:例如省市县数据, 这种数据一般不会变化
//缓存接口
app.get('/province', (req, res) => {
    console.log(req.url)
    //(1)设置响应状态码
    res.statusCode = 200
    //(2)设置响应头
    //这个响应头就会让浏览器去缓存这个接口的数据  max-age=缓存时间(单位秒)
    res.setHeader("Cache-Control", 'max-age=10')
    res.send(['湖北省','湖南省','安徽省'])
})
  • 前端代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <button>点我加载省市县</button>
    <script>
        document.querySelector('button').addEventListener('click', function () {
            //(1).实例化ajax对象
            let xhr = new XMLHttpRequest()
            //(2).设置请求方法和地址
            //get请求的数据直接添加在url的后面 格式是 url?key=value
            xhr.open('get', 'http://127.0.0.1:3000/province')
            //(3).发送请求
            xhr.send()
            //(4).注册回调函数
            xhr.onload = function () {
                console.log(xhr.responseText)
            }
        })    
    </script>
</body>

</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

史蒂文·月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值