Наиболее безопасный javascript метод JSON Inline
Я использую varnish+esi для возврата внешнего содержимого json из RESTFul API. Этот метод позволяет мне управлять запросами и обновлять данные без использования ресурсов веб-сервера для каждого запроса.
например:
<head>
....
<script>
var data = <esi:include src='apiurl/data'>;
</script>
...
После включения ESI лак вернет:
var data = {attr:1, attr2:'martin'};
Это работает нормально, но если API возвращает ошибку, этот метод сгенерирует ошибку разбора.
var data = <html><head><script>...api js here...</script></head><body><h1 ... api html ....
Я решил эту проблему, используя скрытый div для анализа и обнаружения ошибки:
...
<b id=esi-data style=display:none;><esi:include src='apiurl/data'></b>
<script>
try{
var data = $.parseJSON($('#esi-data').html());
}catch{ alert('manage the error here');}
....
Я также пытался использовать скрипт типа text / esi, но браузер отображает HTML внутри тега скрипта (wtf), например:
<script id=esi-data type='text/esi'><esi:include src='apiurl/data'></script>
Вопрос:
Есть ли какая-то причина, чтобы обернуть тег и избежать его разбора браузером?
5 ответов
Позвольте мне расширить на iframe
предложение, которое я сделал в своем комментарии - это не совсем то, что вы думаете!
Подход почти такой же, как то, что вы уже делаете, но вместо использования обычного элемента HTML, такого как div
Вы используете iframe
,
<iframe id="esi-data" src="about:blank"><esi:include src="apiurl/data"></iframe>
var $iframe = $('#esi-data');
try {
var data = $.parseJSON($iframe.html());
} catch (e) { ... }
$iframe.remove();
#esi-data { display: none; }
Чем это отличается от вашего решения? Два пути:
Страница данных / ошибок действительно скрыта от ваших посетителей.
iframe
имеет встроенную модель контента, что означает, что любой контент внутри<iframe>…</iframe>
теги полностью заменяются в DOM, но вы все равно можете получить исходный контент, используяinnerHTML
,Это действительный HTML5... вроде. В HTML5 разметка внутри элементов iframe обрабатывается как текст. Конечно, вы должны быть в состоянии проанализировать его как фрагмент, и он должен содержать только фразы (и нет
script
элементы!), но это, по сути, просто обрабатывается как текст валидатором - и браузерами.Скрипты со страницы ошибок не запускаются. Содержимое анализируется как текст и заменяется в DOM другим документом - никаких шансов для любого
script
элементы для обработки.
Посмотрите на это в действии. Если вы закомментируете строку, где я удаляю iframe
Элемент и осмотреть DOM, вы можете подтвердить, что содержимое HTML заменяется пустым документом. Также обратите внимание, что встроенный script
тег никогда не запускается.
Важно: этот подход может все еще сломаться, если третья сторона добавит iframe
элемент в их страницу ошибки по какой-то причине. Маловероятно, что это может быть, вы можете пуленепробиваемый подход немного больше, сочетая вашу технику с этим: окружить iframe
со скрытым div
что вы удалите, когда вы закончите анализ.
Здесь я иду с другой попыткой.
Хотя я полагаю, что у вас уже есть, возможно, лучшее решение для этого, я могу только представить, что вы обойдете его с довольно низкоэффективным методом вызова esi:insert
в отдельном окне HTML, а затем получить содержимое, как если бы вы использовали AJAX на сервере. Возможно, похоже на это? Затем проверьте содержимое, которое вы получили, возможно, с помощью json_decode
и в случае успеха сгенерируйте ошибку в формате JSON.
Самый большой недостаток, который я вижу в этом, заключается в том, что я считаю, что это будет очень трудоемким и, скорее всего, даже задерживает ваши запросы, так как отдельная страница вызывается так, как если бы ваш сервер сам был клиентом, анализировался и затем отправлялся обратно.
Я бы честно придерживался вашего текущего решения.
Это довольно сложная проблема без реального элегантного решения, если не без решения вообще
Я спросил вас, был ли это документ HTML(5) или XHTML(5), потому что в последнем случае раздел CDATA можно использовать для переноса содержимого, слегка изменив свое решение на что-то вроде этого:
...
<b id='esi-data' style='display:none;'>
<![CDATA[ <esi:include src='apiurl/data'> ]]>
</b>
<script>
try{
var data = $.parseJSON($('#esi-data').html());
}catch{ alert('manage the error here');}
....
Конечно, это решение работает, если:
- вы используете XHTML5 и
- ошибка не содержит раздела CDATA (поскольку вложение раздела CDATA невозможно).
Я не знаю, является ли переключение с одной сериализации на другую вариантом, но я хотел уточнить цель моего вопроса. Надеюсь, это поможет вам:).
<script>var data = 999;</script>
<script>
data = <esi:include src='apiurl/data'>;
</script>
<script>
if(data == 999) alert("there was an error");
</script>
Если есть ошибка и "данные" не являются JSON, то будет выдана ошибка javascript. Следующий блок скрипта подхватит это.
Разве вы не можете просто изменить свой API, чтобы вернуть JSON { "error":"error_code_or_text" }
по ошибке? Вы даже можете сделать что-то значимое в своем интерфейсе, чтобы предупредить пользователя об ошибке, если вы сделаете это таким образом.