本文介绍了chrome中带有python baseHTTPserver 501(不支持的方法('OPTIONS'))的CORS的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

您好,在向 python baseHTTPserver 发送 ajax 获取/发布请求时,我需要一些基本身份验证方面的帮助.

Hi I need some help with base authentification while a ajax get/post request to a python baseHTTPserver.

我能够更改用于发送 CORS 标头的 python 脚本中的一些代码行.当我禁用 http 基本身份验证时,它在现代浏览器中运行良好.

I was able to change some lines of code in the python script for sending CORS headers. It works fine in modern browsers when I disable http base authentification.

如果启用了身份验证,我会收到 501(不支持的方法('OPTIONS'))错误(i chrome).

If authentification is enabled i get a 501 (Unsupported method ('OPTIONS')) error (i chrome).

我花了几个小时寻找解决方案,现在我认为我的方法很好.正如我在下面的主题中阅读的那样,HTTPRequestHandler 可能会导致问题,但我的 pyton 技能不足以解决问题.

I spend hours with finding a solution an now i think iam on a good way. As i read in the topics below the HTTPRequestHandler might cause the problem but my pyton skills are not good enough to solve the problem.

如果发现有关此主题的一些帖子这里这里 但我无法让它运行我有的脚本.有人可以帮我让它运行吗?

If found some post about this topic here and here but iam not able to get it running with the script i have. Can someone help me to get it running?

我们将不胜感激任何帮助或想法.

Any help or ideas are would be highly appreciated.

    #   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, fmt, *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, None)

                (code, body, type) = result

                if code > 0:
                    self.sendResponse(code, body, 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 请求.

The client should issue two requests, first one OPTIONS and then the GET request. The solution presented is not optimal, since we are answering the OPTIONS request with contents.

def do_OPTIONS(self):
            self.sendResponse(200)
            self.processRequest() # not good!

我们应该正确回答 OPTIONS 请求.如果我们这样做,客户端将在收到正确的答案后发出 GET 请求.

We should answer the OPTIONS request properly. If we do so, the client will issue the GET request after receiving a proper answer.

我收到了由 CORS 和请求Content-Type: application/json; charset=utf-8"引起的 501 Unsupported method ('OPTIONS')).

I was getting the 501 Unsupported method ('OPTIONS')) caused by CORS and by requesting the "Content-Type: application/json; charset=utf-8".

为了解决这个错误,我在 do_OPTIONS 中启用了 CORS,并允许客户端请求特定的内容类型.

To solve the error, I enabled CORS in do_OPTIONS and enabled clients to request a specific content type.

我的解决方案:

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()

这篇关于chrome中带有python baseHTTPserver 501(不支持的方法('OPTIONS'))的CORS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-06 23:24