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 ответа:
Я понял, что на самом деле речь идет о Корс предполетный запрос. Спецификация 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'}) });