Как передать значения Unicode в качестве полезной нагрузки urlfetch?

#python-2.7 #google-app-engine #unicode

# #python-2.7 #google-app-engine #юникод

Вопрос:

Вот упрощенный код, который у меня есть:

 #coding=utf-8
...

def api_call(method, token, params=[], payload=None):
    ...
    headers = {
        'Content-Type': 'application/json; charset=utf-8'
    }
    try:
        response = urlfetch.Fetch(url, headers=headers, method=urlfetch.POST, payload=payload, deadline=60)
    ...

payload = '{"search":"%s"}' % ('тест') # UNICODE HERE!

result = api_call(method=method, token=MY_TOKEN, payload=payload) 
 

Этот код хорошо работает с символами, отличными от юникода, но когда используется юникод, я получаю:

   File "/Users/me/Documents/Dev/GAE/app.py", line 38, in api_call
    response = urlfetch.Fetch(url, headers=headers, method=urlfetch.POST, payload=payload, deadline=60)
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/urlfetch.py", line 271, in fetch
    return rpc.get_result()
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/apiproxy_stub_map.py", line 613, in get_result
    return self.__get_result_hook(self)
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/urlfetch.py", line 378, in _get_fetch_result
    rpc.check_success()
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/apiproxy_stub_map.py", line 579, in check_success
    self.__rpc.CheckSuccess()
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/apiproxy_rpc.py", line 157, in _WaitImpl
    self.request, self.response)
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py", line 201, in MakeSyncCall
    self._MakeRealSyncCall(service, call, request, response)
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py", line 219, in _MakeRealSyncCall
    request_pb.set_request(request.Encode())
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/net/proto/ProtocolBuffer.py", line 103, in Encode
    self.Output(e)
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/net/proto/ProtocolBuffer.py", line 347, in Output
    self.OutputUnchecked(e)
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/urlfetch_service_pb.py", line 478, in OutputUnchecked
    out.putPrefixedString(self.payload_)
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/net/proto/ProtocolBuffer.py", line 607, in putPrefixedString
    v = str(v)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 92-93: ordinal not in range(128)
 

Что здесь не так?

Комментарии:

1. Вы не можете отправлять текст в Юникоде по сетевому соединению без его кодирования. Закодируйте его явно.

2. @MartijnPieters, спасибо. Если я это сделаю urllib.quote( u'тест'.encode('utf-8') ) , я не получу исключения, но сторонний сервер не понимает передаваемый текст в кодировке url.

3. Это вполне может быть, но попытка отправить текст в Юникоде без кодирования не является решением.

4. @MartijnPieters, если я отправлю те же данные (без URL-кодирования) с помощью расширенного Rest-клиента (Chrome addon), тогда он будет работать хорошо. Не уверен, как отлаживать то, что отправляет ARC — кодирует ли он данные или нет.

Ответ №1:

полезная нагрузка = ‘{«search»:»%s»}’ % (‘тест’) # UNICODE ЗДЕСЬ!

Это не очень хороший способ создания сериализованного объекта в формате JSON. Помимо проблемы с кодировкой символов, у вас также возникают проблемы, если шаблонная строка содержит специальные символы в строковых литералах, такие как кавычки или обратная косая черта. Предлагаю использовать json модуль для создания сериализованной формы и одновременно позаботиться о кодировании:

 search = u'тест'
payload = json.dumps({'search': search})
 

‘Content-Type’: ‘application / json; charset=utf-8’

charset Для application/json типа носителя нет параметра; это ничего не делает. JSON всегда UTF, и по умолчанию UTF-8, в любом случае.