开发者 · 2020年 5月 18日

flask踩坑记录

作为一个身经百战的面向谷歌和GitHub编程的程序员,对各种技术似乎总是缺乏一点敬畏心的。
然而今天在玩一套前端代码的时候,差点心态崩掉可谓一次罕见的经历,记录下来以飨读者。

因为优秀的前端是在太少(太贵),我不得不开始亲自研究前端的东西。调研了一下热门技术后,我决定从Vue下手,GitHub上搞了几套代码准备照猫画虎,边学边做看看。之前的几个纯前端的模版还挺顺利,轻松都跑起来了。我于是想着结合后段的一起试着跑一把,最终选定一套Flask+Vue的模板。然而复制粘贴一堆命令服务都起来了,页面也出来了。当我漫不经心的输入信息提交post的时候,居然没有按照预期的出现结果。凭着我十几年前对JavaScript的残存记忆,我不慌不忙的打开“开发”面板,查看控制台输出:

[Error] Preflight response is not successful
[Error] Fetch API cannot load http://localhost:3000/api/users/login due to access control checks.
[Error] Failed to load resource: Preflight response is not successful (login, line 0)
[Error] Unhandled Promise Rejection: TypeError: Preflight response is not successful

嗯,谷歌搜了一下,无一例外都说是跟跨站有关,解决方法无非是加各种配置。我照着一通改发现结果依旧,stackoverflow上的各种方案试了个遍也没用任何变化。于是开始怀疑是不是浏览器的问题,正巧突然搜到一个回答就是说Safari有个什么什么bug会导致类似的问题,心中不禁窃喜,然后美滋滋的去下载了Firefox。

当然,这并没有什么卵用,Firefox确实不错,仅限于错误信息更加详细(也更加具有误导性)。Firefox的控制台输出了如下错误:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:3000/api/users/login. (Reason: CORS preflight response did not succeed).
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:3000/api/users/login. (Reason: CORS request did not succeed).
TypeError: NetworkError when attempting to fetch resource.

于是我更加坚定是跨站访问的问题,从前端到后端各种配置和代码都仔仔细细撸了一遍,能加的各种允许跨站的配置和代码全部搞了一圈。
然而,还是没有卵用。

最终面对谷歌里一片被点过点链接我心态有点崩了,没想到初涉前端居然就遇到这么大的困难。而且跨站按理说根本不算啥复杂的问题,我居然都搞不定,不禁陷入了自我否定。不过痛定思痛还是多年的跳坑经验拯救了我,根据经验:如果一个bug非常奇怪,而且你尝试过各种方法都没能完美解决,那么它的root cause必然非常简单而且非常愚蠢。

于是我开始试着调试后端的每一行内容,很快便发现从request里拿到的数据是空的。然而顺着“请求的body是空的”这个线索一路找下去,又绕了个弯儿,找了很多什么Flask get_json None之类的问题也是毫无帮助。知道最终我心一横,直接把各种返回的400全都改成了200,一点居然成功登陆了。再看后台log用户名密码也正常。仔细观察发现一次请求居然有两个request:一个OPTIONS一个POST。

这可来到了我知识的荒原,POST请求我知道,这个OPTIONS是啥玩意??原来这是浏览器自己加的一个请求,用来验证当前的请求是否被服务器允许。而GitHub上下载的这套代码不知道为何没有处理OPTIONS请求,而是直接当作POST请求来获取body信息,自然为空了。

至此真相大白,再回头去看看一开始浏览器的错误提示,其实是非常明确的:Preflight response is not successful,就是OPTIONS请求没有通过。然而因为我缺乏相应的基本知识,同时也因为root cause实在太过于简单,导致网上各种对这个错误代码的分析里几乎没人提出这种可能性。
后面我又查了一下OPTIONS请求相关的内容,发现确实不少人被这东西坑,感觉到这次猜坑经历还是很有意义的,以为记。