CORS 预检请求(Preflight):为什么自定义 Header 会触发一次额外的 OPTIONS 请求?

技术讲座:CORS 预检请求(Preflight)与自定义 Header 的关系

引言

跨源资源共享(CORS)是一种机制,它允许一个资源(比如一个网页)从不同的源请求另一个源的资源。在实现这一机制的过程中,浏览器会发送一个预检请求(Preflight)来询问服务器是否允许实际的请求。本文将深入探讨为什么自定义 Header 会触发一次额外的 OPTIONS 请求,并提供一些工程级的代码示例。

CORS 预检请求(Preflight)

当浏览器需要从一个不同的源(源指的是协议+域名+端口)请求资源时,它会首先发送一个预检请求(OPTIONS)。这个请求的目的是询问服务器是否允许实际请求,以及哪些HTTP方法和头部信息可以被使用。

预检请求的格式如下:

OPTIONS /resource HTTP/1.1
Host: example.com
Origin: http://client.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-Custom-Header

在这个请求中,Access-Control-Request-Method 指定了实际请求将要使用的方法,而 Access-Control-Request-Headers 指定了实际请求将要使用的自定义头部。

自定义 Header 触发额外的 OPTIONS 请求

当预检请求中包含了自定义 Header 时,服务器需要检查这个头部是否被允许。由于服务器可能不认识这个自定义头部,它需要再次确认是否允许使用这个头部。这就导致了额外的 OPTIONS 请求。

示例:使用 PHP 处理预检请求

以下是一个使用 PHP 处理预检请求的示例:

<?php
// 检查是否是预检请求
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    // 允许的HTTP方法
    header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
    // 允许的头部信息
    header('Access-Control-Allow-Headers: X-Custom-Header, Content-Type');
    // 允许的来源
    header('Access-Control-Allow-Origin: http://client.com');
    // 预检请求的响应体为空
    header('Content-Length: 0');
    // 状态码
    http_response_code(204);
    exit;
}

// 处理实际的请求
// ...
?>

在这个示例中,当接收到一个预检请求时,服务器会设置一些响应头,包括允许的HTTP方法、头部信息和来源。如果服务器不认识 X-Custom-Header,它将再次发送一个预检请求来确认是否允许使用这个头部。

示例:使用 Python 处理预检请求

以下是一个使用 Python 处理预检请求的示例:

from flask import Flask, request, make_response

app = Flask(__name__)

@app.route('/resource', methods=['GET', 'POST', 'OPTIONS'])
def resource():
    if request.method == 'OPTIONS':
        response = make_response()
        response.headers['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
        response.headers['Access-Control-Allow-Headers'] = 'X-Custom-Header, Content-Type'
        response.headers['Access-Control-Allow-Origin'] = 'http://client.com'
        response.headers['Content-Length'] = '0'
        response.status_code = 204
        return response
    else:
        # 处理实际的请求
        # ...
        return 'Resource content'

if __name__ == '__main__':
    app.run()

在这个示例中,我们使用了 Flask 框架来处理预检请求。当接收到一个预检请求时,服务器会设置一些响应头,包括允许的HTTP方法、头部信息和来源。

总结

本文深入探讨了 CORS 预检请求(Preflight)与自定义 Header 的关系。当预检请求中包含了自定义 Header 时,服务器需要检查这个头部是否被允许,这可能导致额外的 OPTIONS 请求。通过提供 PHP 和 Python 的代码示例,我们展示了如何处理预检请求并设置相应的响应头。

在实际开发中,了解 CORS 预检请求的工作原理对于确保跨源资源共享的正确实现至关重要。希望本文能够帮助您更好地理解这一机制。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注