YellowCataclysm
@YellowCataclysm
Личинка программиста

Почему django-rest-framework самостоятельно экранирует символы?

Использую django + django-rest-framework в качестве api, за ними стоит самописный демон на c++, общение производится через сокет. Демон на запросы отвечает уже готовой json-строкой, которую просто нужно отправить клиенту. Поскольку используется rest-framework, из представления возвращается экземпляр Response вместо стандартного HttpResponse.
Однако Response по какой-то причине автоматически экранирует символы двойной кавычки. То есть из
{ "foo":"bar" }
в ответе получаем
{ \"foo\":\"bar\" }

Любопытно то, что апостроф(одинарная кавычка) не экранируется.
Если из представления возвращать экземпляр HttpResponse, то в ответе json-строка выглядит так, как и должна. Но в таком случае ни о каком BrowsableAPI речи быть, разумеется, не может.
Пробовал возвращать Response с указанием content-type=application/json и некоторых других, но безрезультатно.
Как починить?
P.S. Решения из разряда "возвращай из демона строку с одинарными кавычками", пожалуйста, не предлагать
  • Вопрос задан
  • 882 просмотра
Решения вопроса 1
YellowCataclysm
@YellowCataclysm Автор вопроса
Личинка программиста
Разобрался, поковырявшись в исходниках и доках.
Экранирование производится во время вызова json.dumps внутри JSONRenderer.
Решена проблема была через создание своего Renderer-а на базе JSONRenderer, который может как сериализировать объекты, так и отдавать строку как есть.
Получившийся код рендерера запостил на codepad
Фактически, была лишь добавлена переменная requires_serializing (True по-умолчанию) и заменен блок
ret = json.dumps(
            data, cls=self.encoder_class,
            indent=indent, ensure_ascii=self.ensure_ascii,
            separators=separators
        )

на
if self.requires_serializing is True:
                ret = json.dumps(
                    data, cls=self.encoder_class,
                    indent=indent, ensure_ascii=self.ensure_ascii,
                    separators=separators
                )
elif type(data) is str:
        ret = data
else:
        raise TypeError('JSONStringPassRenderer::render \
        -> requires_serializing field set to FALSE, but passed @data argument type is not a string')

Полученный рендерер устанавливаем во вью, в котором хотим его использовать (я не питонист вообще ни разу, может быть если лучший способ передать свой рендерер, но так работает)
class CustomJSONView(ApiView):
    special_renderer = JSONStringPassRenderer
    renderer_classes = (special_renderer, BrowsableAPIRenderer)

Теперь при стандартном инстанцировании объект ведет себя точно так же, как JSONRenderer, а если указать перед return Response(...)
self.special_renderer.requires_serializing = False
и передать строку, то рендерер просто отправит строку как есть (разумеется, без вообще какой-либо валидации)
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@marazmiki
Укротитель питонов
А что если...
import json
    # ...
    return Response(json.loads('{ "foo":"bar" }'))


Попробуйте и сами поймёте, в чём ошибка :)
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы