关于HTTP跨域请求的预请求和更多配置

跨域请求限制揭秘:自定义头与预请求策略

关于跨域请求的其他限制

  • 并不是所有情况的跨域都可以设置Access-Control-Allow-Origin
  • 我们还是通过例子来说明

自定义的头信息

1 ) 准备程序

  • server1.js

    const http = require('http');
    const fs = require('fs');
    
    http.createServer((req, res) => {
        console.log('req come: ', req.url);
        const html = fs.readFileSync('test.html', 'utf8');
        // 默认是下面这个writeHead设置,不写也可以
        res.writeHead(200, {
        'Content-Type': 'text/html'
        });
        res.end(html);
    }).listen(8000, () => {
        console.log('server is running on port 8000');
    });
    
  • server2.js

    const http = require('http');
    
    http.createServer((req, res) => {
        console.log('req come: ', req.url);
        // 在writeHead时候添加跨域支持
        res.writeHead(200, {
            'Access-Control-Allow-Origin': '*'
        });
        res.end('req come: ' + req.url);
    }).listen(8001, () => {
        console.log('server is running on port 8001');
    });
    
  • test.html

    <script>
        fetch('http://localhost:8001', {
            method: 'POST',
            headers: {
                'X-Test-Cors': '123'
            }
        });
    </script>
    

2 ) 启动两个程序

  • $ node server1.js
  • $ node server2.js
  • 打开浏览器访问:http://localhost:8000/

3 ) 出现问题

  • 检查页面节点,已经正常加载
  • 浏览器debug后,发现报错了
  • 此时就会报错
    Access to fetch at 'http://localhost:8001/' from origin 'http://localhost:8000' has been blocked by CORS policy: Request header field x-test-cors is not allowed by Access-Control-Allow-Headers in preflight response.
    

4 ) 问题分析

  • 4.1 ) 上面报错信息是说,我们自己加上这个头信息X-Test-Cors在跨域请求中是不被允许的
  • 注意,一定要注意是在跨域请求中,如果是同域请求是被允许的,这就属于CORS预请求所研究的范畴了
  • 具体的请求头限制可以参考:https://fetch.spec.whatwg.org/#cors-safelisted-request-header
  • 4.2 ) 而在跨域的时候默认允许的方法只有三种:GET、POST、HEAD这三种
  • 其他方法都是不被允许的,浏览器会用预请求的方式来验证
  • 4.3 ) 同时允许的Content-Type也有限制,只允许:text/plain、multipart/form-data、application/x-www-form-urlencoded这三种
  • 其他的Content-Type同样也需要验证
  • 4.4 ) 其他限制
    • 4.41 ) XMLHttpRequestUpload对象均没有注册任何事件监听器
    • 4.42 ) 请求中没有使用ReadableStream对象
    • 上面这两个很少会被用到

5 ) 关于预请求与解决方案

  • 浏览器是根据什么来判断请求是否被允许呢?这就是根据header来判断
  • 如果程序需要添加头信息,服务端在响应的时候需要返回一个头告诉浏览器这个头是被允许的
  • 所以这时候,我们只需要添加
    res.writeHead(200, {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Headers': 'X-Test-Cors' // 新添加这行
    });
    
  • 添加之后,就可以正确接收了, 但是我们可以发现, 比刚才多了一个OPTIONS请求, 这个就是预请求
  • 通过OPTIONS请求来获得服务端允许的认可, 然后再次发送POST请求
  • 这就是浏览器对于跨域请求预请求的一个操作
  • 注意,如果之前test.html中的fetch函数用的是GET,同样会发送两个请求,一个是OPTIONS,一个是GET
  • 什么时候需要预请求呢?一般自定义的请求需要进行预请求来进行验证,此处不再展开
  • 注意,OPTIONS不是POST请求的专属,一般的GET,POST是没有必要进行OPTIONS请求的
  • 同样,也不是使用fetch这个api的原因,就是单纯的HTTP规则
  • 注意,如果使用没有定义的方法,如PUT来请求,如下
    <script>
        fetch('http://localhost:8001', {
            method: 'PUT'
        });
    </script>
    
  • 会出现问题
    Access to fetch at 'http://localhost:8001/' from origin 'http://localhost:8000' has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.
    
  • 这个问题说明当前不被允许,所以,这个问题需要解决
  • 我们想要实现RESTful API的时候,需要添加语义化的请求方法,如:PUT,DELETE,PATCH等
  • 这个时候,我们需要添加对各类方法的支持,注意GET,POST,OPTIONS不必添加,默认支持
    res.writeHead(200, {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Headers': 'X-Test-Cors',
        'Access-Control-Allow-Methods': 'PUT, DELETE, PATCH' // 新添加这行,一般GET,POST,OPTIONS不必添加,默认支持
    });
    
  • 添加支持后,同样,我们可以看到有两次请求,一个是OPTIONS请求,一个是新添加的请求方法
  • 注意,如果你用其他未支持的方法,比如自定义的字符来作为请求方法的话,如定义一个字符串或字符’X’作为请求方法
  • 那么这时候即使服务端添加了对’X’方法的支持,结果仍只有一个OPTIONS预请求,最终依然无法通过
  • 因为这已经超越规则了,不被HTTP协议所允许
  • 浏览器通过这种机制来保证跨域访问的安全性
  • 关于预请求,还有一个常用的设置Access-Control-Max-Age, 通过这个设置,浏览器可以在设置的时间内不会再次发送预请求
    res.writeHead(200, {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Headers': 'X-Test-Cors',
        'Access-Control-Allow-Methods': 'PUT, DELETE, PATCH',
        'Access-Control-Max-Age': 1000 // 新添加这行, 1000s内不会再次发送预请求
    });
    
### 使用Axios进行请求配置多个URL 为了实现请求以及管理多个URL,在JavaScript中可以利用`axios`库的强大功能来简化这一过程。通过创建自定义实例,能够更方便地设置基础URL其他默认参数。 #### 创建带有基础URL的Axios实例 可以通过指定一个或多个基础URL的方式来初始化不同的Axios实例,这有助于更好地管理维护API端点: ```javascript // 定义第一个API的基础路径 const instanceOne = axios.create({ baseURL: 'https://api.example-one.com', }); // 定义第二个API的基础路径 const instanceTwo = axios.create({ baseURL: 'https://api.example-two.org', }); ``` 当设置了baseURL之后,所有的相对路径都会自动附加到这个基础上去构建完整的请求地址[^2]。 #### 处理源资源共享(CORS) 对于问题,服务器端通常需要支持CORS(Cross-Origin Resource Sharing)。如果目标服务已经启用了CORS,则客户端无需做额外处理即可正常发起请求;但如果遇到限制,可能还需要调整服务器配置或是寻找其他解决方案比如JSONP(仅限GET请求),或者是代理服务器的方式绕过浏览器的安全策略[^3]。 然而,在某些情况下,即使服务器不完全开放CORS头信息,也可以尝试使用`withCredentials`选项允许发送凭证(cookie, authorization headers),但这要求对方服务器明确指定了可信任的来源名列表。 ```javascript instanceOne.get('/resource', { withCredentials: true, }).then((response) => { console.log(response.data); }).catch((error) => { console.error('Error:', error); }); ``` 另外值得注意的是,虽然可以在前端代码里做一些优化措施减轻带来的麻烦,但是从根本上解决问题还是依赖于后端的服务提供商正确配置其响应头部以兼容更多类型的访问需求[^4]。 #### 发起具体请求 一旦有了上述准备好的Axios实例,就可以轻松地向不同主机名下的资源发出HTTP GET/POST等各类请求了。下面是一个简单的例子展示如何调用两个不同的API接口获取数据: ```javascript async function fetchData() { try { const [responseOne, responseTwo] = await Promise.all([ instanceOne.get('/endpoint'), instanceTwo.post('/another-endpoint', { key: 'value' }) ]); console.log('Response One:', responseOne.data); console.log('Response Two:', responseTwo.data); } catch (error) { console.error('Failed to fetch data:', error.message); } } fetchData(); ``` 此段代码展示了并发执行来自两个不同源头的数据抓取操作,并且采用了ES7+的新特性——异步函数(`async`)等待表达式(`await`)让逻辑更加清晰易读。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Wang's Blog

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

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

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

打赏作者

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

抵扣说明:

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

余额充值