狀況是這樣的,項目用的restful規範,後端用的django+djangorestframework,前端用的vue-cli框架+webpack,前端與後端交互用的axios,而後再用戶登陸以後,axios添加token做爲axios的header並請求後端,後端部分用的djangorestframework的認證組件。html
以上的相關知識,不是本篇文章的重點,請移步:前端
先後端分離djangorestframework——認證組件vue
vue(9)—— 組件化開發 - webpack(3)webpack
在最開始寫好後端web
認證組件:vue-cli
視圖部分裝飾認證組件:django
vue部分,作了個判斷,若是用戶是登陸狀態,自動設置默認的headers:json
添加了請求攔截器以後,前端會自動對每一個請求以前請求一次,請求方式爲OPTIONS,以後再發出真實的請求axios
瀏覽器會對複雜請求進行處理,在發送真正的請求前, 會先發送一個方法爲OPTIONS的預請求(preflight request), 用於試探服務端是否能接受真正的請求,若是options得到的迴應是拒絕性質的,好比404\403\500等http狀態,就會中止post、put等請求的發出
有三種方式會致使這種現象:
- 請求方法不是GET/HEAD/POST。即複雜請求
- POST請求的Content-Type並不是application/x-www-form-urlencoded, multipart/form-data, 或text/plain。最多見的json就不是
- 請求設置了自定義的header字段。我這裏就設置了token做爲自定義的headers
用的postman作的測試:
返回結果:
而後在項目真實測試的時候:
前端用的vue-cli +webpack,啓動項目,並抓包查看時,請求狀態500
而且請求的參數並無給上header
後端就是報錯:
就是這個:
TypeError: cannot unpack non-iterable AuthenticationFailed object
而且前端的console部分還報同源策略的錯:
我跟你說,你要是跑去後端設置中間件的response的話,你就距離正確答案愈來愈遠了,個人中間件部分是這樣的:
而後根據前面用postman測試都沒問題,因此我斷定,必定不是中間件的問題
思來想去,根本的緣由就是拿不到token,因此我開始在這方面思考,而後我註釋了那段是否帶有token的判斷,再測試:
註釋掉驗證部分,只是打印看看request.META裏的數據
前端再次請求:
OPTIONS請求方式由於已經取消了驗證因此不報錯了:
第二次真實請求也出現了,而且返回狀態碼200
header部分也帶上了token:
也就是說,前端裝飾上了token的,可是後端沒有拿到,因此報錯了
最後看看,後端print(request.META)
由於有兩次請求,看看兩次到底有什麼不一樣(我已經刪除掉了一些無關精要的數據)
第一次請求時:
{'ALLUSERSPROFILE':'RUN_MAIN': 'true', 'SERVER_NAME': 'www.xmind.net', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PORT': '8000', 'REMOTE_HOST': '', 'CONTENT_LENGTH': '', 'SCRIPT_NAME': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'SERVER_SOFTWARE': 'WSGIServer/0.2', 'REQUEST_METHOD': 'OPTIONS', 'PATH_INFO': '/api/v1/', 'QUERY_STRING': '', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'text/plain', 'HTTP_HOST': '127.0.0.1:8000', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_ACCESS_CONTROL_REQUEST_METHOD': 'GET', 'HTTP_ORIGIN': 'http://localhost:8080', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3738.0 Safari/537.36 Edg/75.0.107.0', 'HTTP_ACCESS_CONTROL_REQUEST_HEADERS': 'authorization', 'HTTP_ACCEPT': '*/*', 'HTTP_REFERER': 'http://localhost:8080/Course', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7', 'wsgi.input': <_io.BufferedReader name=628>, 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, 'wsgi.version': (1, 0), 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.multithread': True, 'wsgi.multiprocess': False, 'wsgi.file_wrapper': <class 'wsgiref.util.FileWrapper'>}
第二次請求時:
{'ALLUSERSPROFILE': 'RUN_MAIN': 'true', 'SERVER_NAME': 'www.xmind.net', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PORT': '8000', 'REMOTE_HOST': '', 'CONTENT_LENGTH': '', 'SCRIPT_NAME': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'SERVER_SOFTWARE': 'WSGIServer/0.2', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/api/v1/', 'QUERY_STRING': '', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'text/plain', 'HTTP_HOST': '127.0.0.1:8000', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_ACCEPT': 'application/json, text/plain, */*', 'HTTP_ORIGIN': 'http://localhost:8080', 'HTTP_AUTHORIZATION': '626d34f7-2302-4bb0-a012-5daf0f588d8c', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3738.0 Safari/537.36 Edg/75.0.107.0', 'HTTP_REFERER': 'http://localhost:8080/Course', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7', 'wsgi.input': <_io.BufferedReader name=632>, 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, 'wsgi.version': (1, 0), 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.multithread': True, 'wsgi.multiprocess': False, 'wsgi.file_wrapper': <class 'wsgiref.util.FileWrapper'>}
發現前面一大段都相同,就從請求方式開始有不一樣的,因此我從新設置了認證組件。
由於一個請求方式是OPTIONS,一個是GET,而且看前端部分,當請求方式是OPTIONS的時候由於是瀏覽器自動發的預請求,因此沒有按咱們設定的帶上自定義的header,也就沒有Authorization-token這個參數了
終於發現了問題所在,最後在後端作了個判斷,當請求方式是OPTIONS的時候,跳過驗證,而後完美解決:
前端請求:沒問題,都是200
而且後端確實能夠打印出對應的用戶名和token:
好的,踩坑完畢,感謝查看