我有一个App Engine服务,实现了一些方法,我在app.yaml中使用login: admin选项限制所有路由。
对我的服务发出POST请求有效:
fetch('http://localhost:8081/api/foo', {
credentials: 'include'});
但是发出PUT请求失败了
await fetch('http://localhost:8081/api/foo', {
credentials: 'include',
method: 'PUT',
body: 'hi there'});
出现以下错误:
Response to preflight request doesn't pass access control check:
Redirect is not allowed for a preflight request.
我理解这是因为我的请求以某种方式未经过身份验证,服务器将我的请求重定向到登录页面。我不明白的是如何验证它。
我正在使用webapp2来处理请求,并设置以下标头:
self.response.headers['Access-Control-Allow-Credentials'] = 'true'
self.response.headers['Content-Type'] = 'application/json'
# This feels wrong, but I still don't clearly understand what this header's purpose is...
self.response.headers['Access-Control-Allow-Origin'] = self.request.headers['Origin']
我认为更深层次的问题是我不明白这个登录功能是如何工作的(它是基于cookie的吗?为什么它适用于GET而不是PUT?...),我也不是真正理解CORS。
谢谢你的帮助!
login: admin
配置基于Users API,仅在第一代标准环境中可用。不是CORS问题。从login
表中的handlers element行:
当具有除optional之外的登录设置的URL处理程序与URL匹配时,处理程序首先检查用户是否已使用其authentication option登录到应用程序。如果不是,默认情况下,用户将被重定向到登录页面。您还可以使用
auth_fail_action
将应用程序配置为简单地拒绝未经正确身份验证的用户对处理程序的请求,而不是将用户重定向到登录页面。
要使用用户API,用户必须在发出PUT
请求之前逐字登录。先做一个GET
请求,它会将你重定向到登录页面,执行登录,然后发出PUT
请求。
如果这不是你可以实现的,那么你需要使用a different authentication mechanism,而不是基于login: admin
的那个。
更新:
以上情况属实,但无关紧要,因为用户API身份验证已得到解决 - 您确实提到了对同一URL的其他请求方法有效。
你得到的错误确实与CORS有关,请参阅Response to preflight request doesn't pass access control check。但我建议不要专注于接受的答案(这只是围绕CORS工作),而是关注this one,这是关于正确地做CORS。
因此,在与Dan Cornilescu讨论之后,我提出了解决方案(感谢Dan!)
他们没有让我的类继承webapp2.RequestHandler,而是继承了这个自定义的HandlerWrapper。最大的区别在于,当收到“OPTIONS”请求(即预检)时,不需要登录。这就是造成我问题的原因:我无法通过身份验证获得预检请求,所以现在不需要。
CORS也在那里处理,其中包含允许的来源列表
class HandlerWrapper(webapp2.RequestHandler):
def __init__(self, request, response):
super(HandlerWrapper, self).__init__(request, response)
self.allowed_origins = [
r'http://localhost(:\d{2,})?$', # localhost on any port
r'https://\w+-dot-myproject.appspot.com' # all services in the app engine project
]
self.allowed_methods = 'GET, PUT, POST, OPTIONS'
self.content_type = 'application/json'
# login mode: either 'admin', 'user', or 'public'
self.login = 'admin'
def dispatch(self):
# set the Allow-Origin header.
if self.request.headers.has_key('origin') and match_origin(self.request.headers['Origin'], self.allowed_origins):
self.response.headers['Access-Control-Allow-Origin'] = self.request.headers['Origin']
# set other headers
self.response.headers['Access-Control-Allow-Methods'] = self.allowed_methods
self.response.headers['Content-Type'] = 'application/json'
self.response.headers['Access-Control-Allow-Credentials'] = 'true'
# Handle preflight requests: Never require a login.
if self.request.method == "OPTIONS":
# For some reason, the following line raises a '405 (Method Not Allowed)'
# error, so we just skip the dispatch and it works.
# super(HandlerWrapper, self).dispatch()
return
# Handle regular requests
user = users.get_current_user()
if self.login == 'admin' and not users.is_current_user_admin():
self.abort(403)
elif self.login == 'user' and not user:
self.abort(403)
else:
super(HandlerWrapper, self).dispatch()
def match_origin(origin, allowed_origins):
for pattern in allowed_origins:
if re.match(pattern, origin): return True
return False