PATCH 方法和 CORS 预检请求存在问题

Posted

技术标签:

【中文标题】PATCH 方法和 CORS 预检请求存在问题【英文标题】:Problem with PATCH method and CORS preflight request 【发布时间】:2019-08-10 12:42:03 【问题描述】:

我正在开发的应用程序有问题。 API 使用 php Symfony 4,应用程序使用 Ionic 4。

我在执行 PATCH 请求时遇到问题。该请求在 POSTMAN 以及我的手机上运行良好 但是在尝试使用浏览器(Google Chrome、Firefox)时,我遇到了 CORS 问题。

我已经询问并且我了解 PATCH 请求首先通过飞行前请求,因此发送请求 使用我的 API 的 OPTIONS 方法。

我管理了这个方法并返回了一个代码 200 然后我在我的 index.php 文件中允许了正确的标题,如下所示:

header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
header("Access-Control-Allow-Methods: GET, HEAD, PATCH, OPTIONS, POST, PUT");

一开始,我遇到了第一个错误,表明请求没有返回代码 200,所以我在我的 API 中管理了 OPTIONS 方法。 然后我有以下错误:

No 'Access-Control-Allow-Origin' header is present on the requested resource.

这很奇怪,因为之前没有出现错误,并且所有其他请求都有效。

此外,我尝试使用 POSTMAN 并且 OPTIONS 方法返回正确的标题。

你能帮帮我吗?

对不起,英语粗鲁。

【问题讨论】:

您是否也在实际请求中添加了标头?据我所知,您不必支持所有 http 方法。 我应该在哪个查询上添加标题?在前面? 应该在所有允许跨域的请求上添加 cors-headers。不仅仅是头部或选项。 是的,我将它们添加到我的 Symfony 项目根目录下的 index.php 文件中 请求的资源(您应该看到访问了哪个资源)是否是静态文件(位于 public/web 文件夹或子文件夹中)?因为这些通常是直接交付的,没有被 index.php 转发(但你可以强制这样的事情......) 【参考方案1】:

对于将来发现此问题的任何人,我已经重现了这些确切的症状,并解决了我的问题。简短的回答是,当在客户端指定方法时(例如在fetch 选项中),PATCH 特别区分大小写,而其他方法则不区分。需要大写。

根据我所能推断的最好的解释是,即使RFC7230 声明方法应该是大写的,它们也是区分大小写的。浏览器通过自动大写其中几个来平滑这一点,我相信基于RFC7231,它定义了许多常用方法,但没有定义 PATCH。考虑以下从客户端发起的请求。

fetch('https://***.com', 
   method: 'post', 
    body: JSON.stringify(message: 'testing a cors issue'), 
    headers:  'Content-Type': 'application/json'
  
)

至少对于 Chromium 浏览器而言,正常行为是自动将 CORS 预检的 Access-Control-Request-Method 标头中的方法大写,而不管我的 javascript 在 fetch 调用中使用的大小写。

同样,如果方法是DELETE,浏览器也会在预检中自动大写。

但是,我发现PATCH 方法(稍后在RFC5789 中定义)被视为自定义HTTP 方法,因为它没有被强制大写。因此它是区分大小写的,这在 CORS 预检期间可以观察到。如果客户端应用程序以小写 patch 指定它,则它在 Access-Control-Request-Method 标头中显示,如果它是大写的 PATCH,它在标头中显示为大写。

当您在 Postman 中选择 PATCH 方法时,它总是大写,这掩盖了问题的根本原因。尽管错误消息与 Access-Control-Allow-Origin 标头有关,但我看到的根本原因是该方法区分大小写。

【讨论】:

正确,根据 HTTP 规范——以及所有其他 RFC——all HTTP 方法是区分大小写的。另见***.com/a/27427356/441757。因此,根据 HTTP 和其他 RFC,patch 和 PATCH 是不同的方法 - 而get 与 GET 等也是不同的方法。因此,为了严格遵守 HTTP/RFC 要求,HTTP 服务器和 HTTP 客户端都不能将 patch 方法视为真正的 PATCH,或将 get 等方法视为 GET 等. 所以这就是大多数 HTTP 服务器实际上所做的。但 HTTP 客户端在实践中所做的是另一回事。 浏览器实际上违反了 HTTP 规范对方法不区分大小写的要求。见github.com/whatwg/fetch/issues/50#issuecomment-188241506。但是他们以标准方式违反了它,定义在fetch.spec.whatwg.org/#concept-method-normalize,它说,“为了规范化一个方法,如果它是一个不区分大小写的 DELETE、GET、HEAD、OPTIONS、POST 或 PUT 匹配, 字节大写。” 因此,由于 PATCH 不在该列表中,因此浏览器从不大写 patch — 也没有任何其他方法,除了该列表中的方法。浏览器为列出的浏览器这样做的唯一原因是向后兼容性。 @sideshowbarker 这是很好的背景信息。很好的补充。【参考方案2】:
Environement info-
   ionic (Ionic CLI)             : 4.0.0 
   Ionic Framework               : @ionic/angular 4.0.0
   @angular-devkit/build-angular : 0.8.6
   @angular-devkit/schematics    : 0.8.6
   @angular/cli                  : 6.2.6
   @ionic/angular-toolkit        : 1.1.0

要修复 CORS 错误,您应该按照以下步骤操作:

    创建 到您的 Ionic 项目(在根目录中)一个文件调用它: proxy.conf.json 在里面写这个(复制/粘贴):
"/api/*":   
    "target": "http://myremoteserver/api",  
    "secure": false,  
    "logLevel": "debug"  

    打开您的angular.json 文件并将proxyConfig": "proxy.conf.json" 行设置为"serve"

    "serve": 
              "builder": "@angular-devkit/build-angular:dev-server",
              "options": 
                "browserTarget": "app:build",
                proxyConfig": "proxy.conf.json"
              ,
              "configurations": 
                "production": 
                  "browserTarget": "app:build:production"
    

    打开您的package.json 并将这一行添加到脚本 数组:

    "start": "ng serve --port 8100 --proxy-config proxy.conf.json" 
    

    你应该得到这样的东西(见下文):

    "scripts": 
        "ng": "ng",
        "start": "ng serve --port 8100 --proxy-config proxy.conf.json",
        "build": "ng build",
        "test": "ng test",
        "lint": "ng lint",
        "e2e": "ng e2e"
      
    
    在命令行中运行此命令后:npm run start

如果您按照这些步骤操作应该是OK

就我而言,它正在工作!!!

最好的问候

【讨论】:

以上是关于PATCH 方法和 CORS 预检请求存在问题的主要内容,如果未能解决你的问题,请参考以下文章

Angular $http.delete CORS 错误(预检请求)

CORS 预检请求返回带有 Windows 身份验证的 HTTP 401

PouchDB 身份验证触发 CORS 预检请求

预检时间的 CORS 请求是不是耗时?

使用 CORS 避免预检 OPTIONS 请求

CORS HEADERS 仅在预检或每个请求中出现