嗨我需要一些基本身份验证的帮助,而ajax get / post请求到python baseHTTPserver.
我能够在python脚本中更改一些代码行来发送CORS头.当我禁用http base authentification时,它在现代浏览器中工作正常.
如果启用了身份验证,我会收到501(不支持的方法(‘OPTIONS’))错误(i chrome).
我花了好几个小时寻找一个解决方案现在我认为我是一个好方法.正如我在下面的主题中读到的,HTTPRequestHandler可能会导致问题,但我的pyton技能不足以解决问题.
如果发现一些关于这个话题here和here的帖子,但我无法使用我的脚本运行它.有人可以帮助我让它运行吗?
任何帮助或想法都将受到高度赞赏.
# Copyright 2012-2013 Eric Ptak - trouch.com # # Licensed under the Apache License,Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing,software # distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import threading import re import codecs import mimetypes as mime import logging from webiopi.utils import * if PYTHON_MAJOR >= 3: import http.server as BaseHTTPServer else: import BaseHTTPServer try : import _webiopi.GPIO as GPIO except: pass WEBIOPI_DOCROOT = "/usr/share/webiopi/htdocs" class HTTPServer(BaseHTTPServer.HTTPServer,threading.Thread): def __init__(self,host,port,handler,context,docroot,index,auth=None): BaseHTTPServer.HTTPServer.__init__(self,("",port),HTTPHandler) threading.Thread.__init__(self,name="HTTPThread") self.host = host self.port = port if context: self.context = context if not self.context.startswith("/"): self.context = "/" + self.context if not self.context.endswith("/"): self.context += "/" else: self.context = "/" self.docroot = docroot if index: self.index = index else: self.index = "index.html" self.handler = handler self.auth = auth self.running = True self.start() def get_request(self): sock,addr = self.socket.accept() sock.settimeout(10.0) return (sock,addr) def run(self): info("HTTP Server binded on http://%s:%s%s" % (self.host,self.port,self.context)) try: self.serve_forever() except Exception as e: if self.running == True: exception(e) info("HTTP Server stopped") def stop(self): self.running = False self.server_close() class HTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler): logger = logging.getLogger("HTTP") def log_message(self,fmt,*args): self.logger.debug(fmt % args) def log_error(self,*args): pass def version_string(self): return VERSION_STRING def checkAuthentication(self): if self.server.auth == None or len(self.server.auth) == 0: return True authHeader = self.headers.get('Authorization') if authHeader == None: return False if not authHeader.startswith("Basic "): return False auth = authHeader.replace("Basic ","") if PYTHON_MAJOR >= 3: auth_hash = encrypt(auth.encode()) else: auth_hash = encrypt(auth) if auth_hash == self.server.auth: return True return False def requestAuthentication(self): self.send_response(401) self.send_header("WWW-Authenticate",'Basic realm="webiopi"') self.end_headers(); def sendResponse(self,code,body=None,type="text/plain"): if code >= 400: if body != None: self.send_error(code,body) else: self.send_error(code) else: self.send_response(code) self.send_header("Cache-Control","no-cache") self.send_header("Access-Control-Allow-Origin","*") self.send_header("Access-Control-Allow-Methods","POST,GET") self.send_header("Access-Control-Allow-Headers"," X-Custom-Header") if body != None: self.send_header("Content-Type",type); self.end_headers(); self.wfile.write(body.encode()) def findFile(self,filepath): if os.path.exists(filepath): if os.path.isdir(filepath): filepath += "/" + self.server.index if os.path.exists(filepath): return filepath else: return filepath return None def serveFile(self,relativePath): if self.server.docroot != None: path = self.findFile(self.server.docroot + "/" + relativePath) if path == None: path = self.findFile("./" + relativePath) else: path = self.findFile("./" + relativePath) if path == None: path = self.findFile(WEBIOPI_DOCROOT + "/" + relativePath) if path == None and (relativePath.startswith("webiopi.") or relativePath.startswith("jquery")): path = self.findFile(WEBIOPI_DOCROOT + "/" + relativePath) if path == None: return self.sendResponse(404,"Not Found") realPath = os.path.realpath(path) if realPath.endswith(".py"): return self.sendResponse(403,"Not Authorized") if not (realPath.startswith(os.getcwd()) or (self.server.docroot and realPath.startswith(self.server.docroot)) or realPath.startswith(WEBIOPI_DOCROOT)): return self.sendResponse(403,"Not Authorized") (type,encoding) = mime.guess_type(path) f = codecs.open(path,encoding=encoding) data = f.read() f.close() self.send_response(200) self.send_header("Content-Type",type); self.send_header("Content-Length",os.path.getsize(realPath)) self.end_headers() self.wfile.write(data) def processRequest(self): self.request.settimeout(None) if not self.checkAuthentication(): return self.requestAuthentication() request = self.path.replace(self.server.context,"/").split('?') relativePath = request[0] if relativePath[0] == "/": relativePath = relativePath[1:] if relativePath == "webiopi" or relativePath == "webiopi/": self.send_response(301) self.send_header("Location","/") self.end_headers() return params = {} if len(request) > 1: for s in request[1].split('&'): if s.find('=') > 0: (name,value) = s.split('=') params[name] = value else: params[s] = None compact = False if 'compact' in params: compact = str2bool(params['compact']) try: result = (None,None,None) if self.command == "GET": result = self.server.handler.do_GET(relativePath,compact) elif self.command == "POST": length = 0 length_header = 'content-length' if length_header in self.headers: length = int(self.headers[length_header]) result = self.server.handler.do_POST(relativePath,self.rfile.read(length),compact) else: result = (405,None) (code,body,type) = result if code > 0: self.sendResponse(code,type) else: if self.command == "GET": self.serveFile(relativePath) else: self.sendResponse(404) except (GPIO.InvalidDirectionException,GPIO.InvalidChannelException,GPIO.SetupException) as e: self.sendResponse(403,"%s" % e) except ValueError as e: self.sendResponse(403,"%s" % e) except Exception as e: self.sendResponse(500) raise e def do_GET(self): self.processRequest() def do_POST(self): self.processRequest()
解决方法
客户端应发出两个请求,首先是一个OPTIONS,然后是GET请求.所提出的解决方案并非最佳,因为我们正在回答带有内容的OPTIONS请求.
def do_OPTIONS(self): self.sendResponse(200) self.processRequest() # not good!
我们应该正确回答OPTIONS请求.如果我们这样做,客户端将在收到正确答案后发出GET请求.
我得到了由CORS引起的501 Unsupported方法(‘OPTIONS’))并请求“Content-Type:application / json; charset = utf-8”.
为了解决这个错误,我在do_OPTIONS中启用了CORS,并使客户端能够请求特定的内容类型.
我的解决方案
def do_OPTIONS(self): self.send_response(200,"ok") self.send_header('Access-Control-Allow-Origin','*') self.send_header('Access-Control-Allow-Methods','GET,OPTIONS') self.send_header("Access-Control-Allow-Headers","X-Requested-With") self.send_header("Access-Control-Allow-Headers","Content-Type") self.end_headers() def do_GET(self): self.processRequest()