【问题标题】:Funny error using cherrypy wsgi server使用cherrypy wsgi服务器的有趣错误
【发布时间】:2013-11-20 07:23:20
【问题描述】:

我正在使用cherrypy 的wsgi 服务器为我的django 项目提供服务。服务器提供来自 django 项目的所有静态和媒体文件。代码如下

#create basic django WSGI app
app = WSGIHandler()
path = {'/': app}

#create app that handles static files from static root and put it in path
path[static_url] = StaticFileWsgiApplication(static_root)

#create app that handles /media and put it in path
path[media_url] = StaticFileWsgiApplication(media_root)

#dispatch the applications
dispatcher = wsgiserver.WSGIPathInfoDispatcher(path)

if __name__ == '__main__':
    print 'Close window to exit'

    #create wsgi cherrypy server
    server = wsgiserver.CherryPyWSGIServer(
        ('0.0.0.0', 8000),
        dispatcher,
        server_name='cherrypy-django',
        numthreads=20
    )
    try:
        server.start()
    except KeyboardInterrupt:
        server.stop()

和静态文件应用程序

class StaticFileWsgiApplication(object):

    #init function to get the directory of collectstatic (STATIC_ROOT)
    def __init__(self, static_file_root):
        self.static_file_root = os.path.normpath(static_file_root)

    #Every wsgi app must be callable. It needs 2 arguments
    #eviron: wsgi environ path
    #start_response:Function creates the response


    def __call__(self, environ, start_respone):

        def done(status, headers, output):
            #usefull for debugging
            #returns the output(actual static file)
            #also it produces the response using the start_response()
            start_respone(status, headers.items())
            return output

        #get the path_info see PEP 333
        path_info = environ['PATH_INFO']

        #remove leading '/' from path (URI)
        if path_info[0]=='/':
            path_info = path_info[1:]

        #actual file path in filesystem
        file_path = os.path.normpath((os.path.join(self.static_file_root, path_info)))

        #prevent escaping out of paths bellow media root (e.g via '..')
        if not file_path.startswith(self.static_file_root):
            status = '401 UNAUTHORIZED'
            headers = {'Content_type':'text/plain'}
            output = ['Permission denied. Illegal  Path']
            return done(status, headers, output)

        #Only allow GET and HEAD requests not PUT, POST, DELETE
        if not (environ['REQUEST_METHOD'] == 'GET' or environ['REQUEST_METHOD'] == 'HEAD'):
            status = '405 METHOD NOT ALLOWED'
            headers = {'Content_type':'text/plain'}
            output = SimpleResponse(['405:Method not allowed'])
            output.status_code = 405
            return done(status, headers, output)

        if not (os.path.exists(file_path)):
            status = "404 NOT FOUND"
            headers = {'Content_type':'text\plain'}
            output = SimpleResponse(['Page not found %s' %file_path])
            output.status_code = 404
            return done(status, headers, output)

        try:
            fp = open(file_path, 'rb')
        except IOError:
            status = '401 UNAUTHORIZED'
            headers = {'Content_type':'text/plain'}
            output = SimpleResponse(['Permission denied %s' %file_path])
            output.status_code = 401
            return done(status, headers, output)

        #This is a very simple implementation of conditional GET with
        #the Last-Modified header. It makes media files a bit speedier
        #because the files are only read off disk for the first request
        #(assuming the browser/client supports conditional GET).

        #mimetype needs to be ascii not uinicode, as django is all unicode, need to do conversion

        mtime = http_date(os.stat(file_path)[stat.ST_MTIME]).encode('ascii','ignore')
        if environ.get('HTTP_IF_MODIFIED_SINCE', None) == mtime:
            headers = {'Last-Modified':mtime}
            status = '304 NOT MODIFIED'
            output = SimpleResponse()
            output.status_code = 304
        else:
            status = '200 OK'
            mime_type = mimetypes.guess_type(file_path)[0]
           if mime_type:
                headers = {'Content_type':mime_type}
           output = BlockIteratorResponse(fp)

       return done(status, headers,output)

我在 call 中收到关于在使用前未初始化标头的错误...可能是因为所有标头都在 if 中。错误是

UnboundLocalError:Local variable "headers" referenced before assignment

有趣的是,我只在 Mozilla 上收到此错误,但网络工作正常,一切正常,并且在我登录我的网页后,它不再显示错误。

我应该在调用开始时初始化 headers 变量吗

headers = {}

为什么它只发生在 Firefox 中?

【问题讨论】:

  • 为什么不使用 CherryPy 的内置功能来提供静态内容?
  • 意义?我尝试了一些方法,但没有奏效......也许是一个例子?

标签: django wsgi cherrypy


【解决方案1】:

如果您想使用 CherryPy 提供静态内容,您应该使用内置工具 - 请参阅 Serving Static Content。我想您需要创建一个仅提供静态内容的 CherryPy 应用程序,然后将其与您的 Django 应用程序与WSGIPathInfoDispatcher(...) 结合使用。

但是你真正应该考虑的是使用 nginx + uWSGI -- 见Setting up Django and your web server with uWSGI and nginx

【讨论】:

  • cherrypy 服务器导出为 exe,并在本地运行 django,因此cherrypy 是一个简单快速的解决方案。我不明白为什么使用chrome时没有错误。
  • 根据代码,它必须是If-Modified-Since 标头和缺少影响它的 MIME 类型。浏览器以不同方式处理这些缓存标头,因此存在差异。不确定缺少 MIME 是否可以,但初始化 headers 完全有意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-06
  • 1970-01-01
  • 2020-01-10
  • 2019-08-17
  • 1970-01-01
  • 1970-01-01
  • 2015-11-15
相关资源
最近更新 更多