浏览器请求与跨域问题

Q1:什么场景下会在一次请求里发起两次请求

在某些情况下,浏览器会发起两次请求,这通常与跨域资源共享有关,例如当前端页面的通过 XMLHttpRequest 或者 Fetch API 发起跨域请求时,浏览器会先发送一个预检请求(OPTIONS 请求),然后再发送(GETPOST)请求。这种预检请求主要用于检查服务器是否支持跨域请求。

Q2:两次请求中第一次和第二次请求分别是什么,有什么区别

在一次跨域请求中,第一次和第二次的请求具体如下:

  • 第一次请求:这是一个预检请求(Preflight Request),通常采用OPTIONS方法。这次请求的目的是为了检查实际请求是否安全,是否被服务器所允许。预检请求会发送一些头信息给服务器,比如Access-Control-Request-Method(真实请求将使用的HTTP方法),Access-Control-Request-Headers(真实请求将携带的自定义头信息列表),以检查服务器是否允许跨域请求、是否允许使用这些方法或携带这些头信息。

  • 第二次请求:这是实际的请求,可以是GET、POST等HTTP方法。这次请求是根据预检请求的响应结果来决定是否可以发送的。如果预检请求的响应显示服务器接受这些条件(如允许跨域、允许使用特定方法和头信息),浏览器将继续发起这次实际请求,携带真实的数据与服务器进行交互。

两次请求的区别主要在于请求的目的和方法。第一次是为了安全性的预检,不携带实际的交互数据,而第二次是根据第一次的预检结果进行的实际数据交互请求。

Q3:为什么浏览器有同源策略,而服务器没有

浏览器实施同源策略主要是为了安全考虑,防止恶意网站访问或操作另一个网站的数据,如读取敏感信息、执行脚本等,造成跨站脚本攻击(XSS)或跨站请求伪造(CSRF)。同源策略确保了一个源的文档或脚本无法与另一个源的资源进行交互,除非两者之间有明确的授权(如CORS)。

服务器端没有同源策略的限制,是因为服务器端通常掌握着资源控制权和校验机制,能够决定是否处理来自不同源的请求。服务器端的安全策略更加灵活且主动,可以根据业务需求实施相应的安全措施,如认证、授权、输入校验等,来保障系统的安全性。在处理跨域请求时,服务器通过识别请求的来源并返回适当的CORS(跨源资源共享)响应头来声明是否允许该跨域请求,从而实现跨域资源的安全共享。

Q4:服务器如何配置 cors。 nginx 里如何配置,如果是 nodejs 如何配置,例如koa、egg

在nginx中可以使用一下配置

// Some code
location /api {
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,DELETE';
    add_header 'Access-Control-Allow-Headers' 'Origin,x-Requested-With, Content-Type';
    if ($request_method = 'OPTIONS') {
        return 204;
    }
}

在Koa框架中配置CORS,可以使用koa-cors@koa/cors中间件。以下是一个基于@koa/cors的配置示例:

const Koa = require('koa');
const cors = require('@koa/cors');

const app = new Koa();

// 配置CORS
app.use(cors({
  origin: function(ctx) { // 设置允许来自指定域名请求
    if (ctx.url === '/test') {
      return '*'; // 允许来自所有域名请求
    }
    return 'http://example.com'; //只允许http://example.com这个域名的请求
  },
  exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'],
  maxAge: 5,
  credentials: true,
  allowMethods: ['GET', 'POST', 'DELETE'],
  allowHeaders: ['Content-Type', 'Authorization', 'Accept'],
}));

// 省略其他路由和中间件配置...

app.listen(3000);

Q5: 为什么本地使用 webpack进行 dev开发时,访问的是 127.0.0.1,但却依然能在不需要服务器端配置的情况下访问到线上接口

在使用webpack进行本地开发时,能够访问到线上接口,主要是因为webpack提供了代理(Proxy)功能,这个功能允许你在开发环境中,将特定的API请求转发到另一个服务器(例如线上的服务器)。这样做的主要目的是为了解决开发过程中遇到的跨域问题。

例如,可以在webpack.config.js文件中配置devServer的proxy选项:

module.exports = {
  // 其他配置...
  devServer: {
    proxy: {
      '/api': {
        target: 'http://example.com',
        changeOrigin: true,
        pathRewrite: { '^/api': '' },
      },
    },
  },
};

这个配置的意思是,当你在本地开发环境中请求以/api开头的路径时,webpack-dev-server会自动将这个请求转发到http://example.com,并且由于设置了changeOrigintrue,服务器会认为这个请求是从http://example.com发出的,从而避免了跨域问题。这样,即便是在没有后端支持CORS的情况下,前端在开发环境中也可以正常调用线上接口进行开发和测试。

最后更新于