【问题标题】:415 exception Cherrypy webservice415 异常 Cherrypy 网络服务
【发布时间】:2015-01-20 16:04:58
【问题描述】:

我正在尝试构建 Cherrypy/Python 网络服务。我已经花了一整天的时间研究如何使跨域 ajax 请求成为可能。这终于奏效了,但现在我有下一个问题。我想我已经知道解决方案,但我不知道如何实现它。问题是当我发送 ajax 请求时,Cherrypy 服务器响应:

415 Unsupported Media Type

Expected an entity of content type application/json, text/javascript

Traceback (most recent call last):  File "/Library/Python/2.7/site-packages/cherrypy/_cprequest.py", line 663, in respond    self.body.process()  File "/Library/Python/2.7/site-packages/cherrypy/_cpreqbody.py", line 996, in process    super(RequestBody, self).process()  File "/Library/Python/2.7/site-packages/cherrypy/_cpreqbody.py", line 538, in process    self.default_proc()  File "/Library/Python/2.7/site-packages/cherrypy/_cperror.py", line 411, in __call__    raise selfHTTPError: (415, u'Expected an entity of content type application/json, text/javascript')    

我找到并尝试测试的解决方案是将这一行添加到配置中:

'tools.json_in.force': False

所以我尝试在这段代码中实现它:

import cherrypy
import json
import sys

class RelatedDocuments:

def index(self):
    return "Hello World!"

@cherrypy.tools.json_out()
@cherrypy.tools.json_in()
def findRelated(self, **raw):
    #Get JSON message form request
    request = cherrypy.request.json
    result = []

    #SOME CODE...

    return result;

# Expose the index method through the web. CherryPy will never
# publish methods that don't have the exposed attribute set to True.
index.exposed = True
findRelated.exposed = True

def CORS():
    cherrypy.response.headers["Access-Control-Allow-Origin"] = "*"

import os.path
tutconf = os.path.join(os.path.dirname(__file__), 'webserver.conf')
config = {
    'global': {
        'server.socket_host':'127.0.0.1',
        'server.socket_port': 8080,
        'log.error_file' : 'Web.log',
        'log.access_file' : 'Access.log'
    },
    '/': {
        'tools.CORS.on': True
    }
}

if __name__ == '__main__':
    cherrypy.tools.CORS = cherrypy.Tool('before_finalize', CORS)

    cherrypy.quickstart(RelatedDocuments(),config=config)

我在 tools.CORS.on 行下添加了配置行,但这不起作用。接下来我尝试了这个:

cherrypy.config.update({
    'tools.json_in.force': False,
});

没有工作eiter..next我试图在findRelated方法上方实现这个:

@cherrypy.config(**{'tools.json_in.force': False})

所有的实现都给了我一个 500 错误,如果有人可以帮助我,我真的很感激。提前致谢!

【问题讨论】:

    标签: python ajax cross-domain cherrypy


    【解决方案1】:

    我意识到问题实际上是关于CORS preflight request。 CORS specification defines 简单 CORS 请求的以下条件:

    • 方法:GETHEADPOST
    • 标题:AcceptAccept-LanguageContent-LanguageContent-Type
    • Cotent 类型的头值:application/x-www-form-urlencoded, multipart/form-data, text/plain

    否则 CORS 请求并不简单,并且在实际请求之前使用 preflight OPTIONS 请求以确保它符合条件。这里是good CORS how-to

    因此,如果您想保持简单,您可能需要恢复为正常的application/x-www-form-urlencoded。否则,您需要正确处理预检请求。这是一个工作示例(不要忘记添加 localhost 别名)。

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    '''
    Add localhost alias, `proxy` , in /etc/hosts.
    '''
    
    
    import cherrypy
    
    
    config = {
      'global' : {
        'server.socket_host' : '127.0.0.1',
        'server.socket_port' : 8080,
        'server.thread_pool' : 8
      }
    }
    
    
    def cors():
      if cherrypy.request.method == 'OPTIONS':
        # preflign request 
        # see http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0
        cherrypy.response.headers['Access-Control-Allow-Methods'] = 'POST'
        cherrypy.response.headers['Access-Control-Allow-Headers'] = 'content-type'
        cherrypy.response.headers['Access-Control-Allow-Origin']  = '*'
        # tell CherryPy no avoid normal handler
        return True
      else:
        cherrypy.response.headers['Access-Control-Allow-Origin'] = '*'
    
    cherrypy.tools.cors = cherrypy._cptools.HandlerTool(cors)
    
    
    class App:
    
      @cherrypy.expose
      def index(self):
        return '''<!DOCTYPE html>
          <html>
          <head>
          <meta content='text/html; charset=utf-8' http-equiv='content-type'>
          <title>CORS AJAX JSON request</title>
          <script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'></script>
          <script type='text/javascript'>
            $(document).ready(function()
            {
              $('button').on('click', function()
              {
                $.ajax({
                  'type'        : 'POST',
                  'dataType'    : 'JSON',
                  'contentType' : 'application/json',
                  'url'         : 'http://proxy:8080/endpoint',
                  'data'        : JSON.stringify({'foo': 'bar'}),
                  'success'     : function(response)
                  {
                    console.log(response);  
                  }
                });
              })
            });
          </script>
          </head>
          <body>
            <button>make request</button>
          </body>
          </html>
        '''
    
      @cherrypy.expose
      @cherrypy.config(**{'tools.cors.on': True})
      @cherrypy.tools.json_in()
      @cherrypy.tools.json_out()
      def endpoint(self):
        data = cherrypy.request.json
        return data.items()
    
    
    if __name__ == '__main__':
      cherrypy.quickstart(App(), '/', config)
    

    【讨论】:

    • 非常感谢,你让我过得愉快!现在一切正常,甚至从未听说过预检请求。
    【解决方案2】:

    一般来说,如果你选择了一个工具,那么你最好使用它,而不是对抗它。 CherryPy 告诉您,对于 JSON 输入,它需要 application/jsontext/javascript 内容类型的请求。

    这里是cherrypy.lib.jsontools.json_in的代码:

    def json_in(content_type=[ntou('application/json'), ntou('text/javascript')],
                force=True, debug=False, processor=json_processor):
    
        request = cherrypy.serving.request
        if isinstance(content_type, basestring):
            content_type = [content_type]
    
        if force:
            if debug:
                cherrypy.log('Removing body processors %s' %
                             repr(request.body.processors.keys()), 'TOOLS.JSON_IN')
            request.body.processors.clear()
            request.body.default_proc = cherrypy.HTTPError(
                415, 'Expected an entity of content type %s' %
                ', '.join(content_type))
    
        for ct in content_type:
            if debug:
                cherrypy.log('Adding body processor for %s' % ct, 'TOOLS.JSON_IN')
            request.body.processors[ct] = processor
    

    force 除了删除现有的车身处理器外,没有做任何其他事情。如果您将force 设置为False,那么您需要告诉 CherryPy 如何处理您发送给它的请求正文。

    或者,更好的是,使用 CherryPy 并告诉它正确的内容类型。使用 jQuery 很简单:

     jQuery.ajax({
        'type'        : 'POST',
        'dataType'    : 'JSON',
        'contentType' : 'application/json',
        'url'         : '/findRelated',
        'data'        : JSON.stringify({'foo': 'bar'})
     });
    

    【讨论】:

    • 是的,我知道...我已经看过这个文件,并且我的 contentType 首先是 application/json。但后来我的 HTTP 请求从 POST 更改为 OPTIONS 方法,但我明确地将方法设置为 POST。我找到了一些关于此的信息,它可能与跨域 ajax 请求有关,但找不到解决方案。我只是想建立一个快速原型,所以我找到了另一个解决方案,并希望我能暂时快速解决这个问题。
    猜你喜欢
    • 2019-05-25
    • 2011-07-27
    • 1970-01-01
    • 2014-03-25
    • 1970-01-01
    • 1970-01-01
    • 2010-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多