415 исключение Cherrypy webservice


Я пытаюсь построить веб-сервис 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)

Я добавил строку конфигурации под инструментами.Корс.на линии, но это не сработало. Затем я попробовал это:

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

Не сработал эйтер..далее я попытался реализовать это прямо над методом findRelated:

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

Все реализации дали мне ошибку 500, я очень ценю, если кто-то может мне помочь. Заранее спасибо!

2 4

2 ответа:

Я понял, что на самом деле речь идет о Корс предполетный запрос. Спецификация CORS определяет следующее условие для простого запроса CORS:

  • метод: GET, HEAD, POST
  • заголовки: Accept, Accept-Language, Content-Language, Content-Type
  • значение заголовка типа Cotent: application/x-www-form-urlencoded, multipart/form-data, text/plain

В противном случае запрос CORS не прост, и используйте запрос preflight OPTIONS перед фактическим запросом, чтобы убедиться, что он подходит. Вот хороший Корс как-к .

Поэтому, если вы хотите, чтобы все было просто, вы можете вернуться к нормальному 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)

В общем, если вы выбрали инструмент, то вам лучше использовать его, а не бороться с ним. CherryPy сообщает вам, что для ввода JSON он ожидает запрос с типом содержимого application/json или text/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'})
 });