Учим MS SQL Server Reporting Services 2008 R2 показывать HTML в отчетах

Источник: habrahabr
Cyr

Проблема


Недавно столкнулся с необходимостью показать в отчете SSRS 2008 R2 HTML-таблицы, хранящиеся в базе данных.
И здесь на сцену статисты выносят заботливо подготовленные "грабли". Все дело в том, что Report Manager в SSRS 2008 R2 поддерживает только ограниченное количество тегов HTML, и табличные в их число не входят.
Согласно официальной документации, поддерживаются только следующие теги:
  • Ссылки: A HREF
  • Шрифт: FONT
  • Заголовки, блочные элементы: H{n}, DIV, SPAN,P, LI
  • Форматирование текста: B, I, U, S
  • Списки: OL, UL, LI

Список атрибутов CSS также сильно урезан.

После продолжительного поиска в интернете, стало понятно, что ни один из вариантов, предлагаемых сообществом разработчиков, меня не устроит.

Исследование


Варианты предлагались следующие:
  • Рендерить HTML в картинку на стороне сервера с помощью специально разработанного компонента (Custom Report Items)
  • Заменить теги на поддерживаемые с симуляцией таблиц (нужно писать скрипт в самом отчете)

В одном из обсуждений натолкнулся на подсказку - Javascript на стороне клиента. Готового решения не было.
Начал с попыток проанализировать какой код выполняется на стороне клиента при отображении отчета. Подсмотрел в код отчета в браузере и нашел внедренный скрипт ReportingServices.js:
<script language="JScript" src="/Reports_SQLEXPRESS/js/ReportingServices.js" type="text/Javascript"></script>
Сам скрипт у меня на сервере лежал здесь:
c:\Program Files\Microsoft SQL Server\MSRS10_50.SQLEXPRESS\Reporting Services\ReportManager\js\ReportingServices.js
Нашел пару статей на тему Javascript-инъекции с помощью данного скрипта, но основная проблема была в том, что мои модификации выполнялись только один раз при отображении первой страницы отчета, и при переходе на следующие страницы интерфейсные элементы уже не перерисовывались (AJAX).
Попытки глубже разобраться в принципах работы SSRS продолжились изучением фрагментов кода основных DLL-ей сервера с помощью DevExtras CodeReflect и препарированием отчетов в IE9 developer tool/Opera Dragonfly.
В результате нашел следующее:
  • Для WebForms которые используют Ajax, есть ряд событий, определяющих жизненный цикл страницы
  • Одно из таких событий - Load, вызывается каждый раз, при загрузке страницы и всех вызовов Ajax
  • Подробности в статье Ajax Client Life-Cycle Events: http://msdn.microsoft.com/en-us/library/bb386417.aspx
Для того чтобы добавить свой обработчик - нужно создать JS-функцию pageLoad, которая автоматически будет выполняться при возникновении события Load:function pageLoad(sender, args) { //здесь наш код, например alert("Работает!"); } Вставляю данную конструкцию в самое начало скрипта ReportingServices.js. Работает!

Решение


Собственно, дальше уже головоломка собралась и получилось следующее решение:
  1. Добавляем в начало скрипта %SSRS Install Dir%\Reporting Services\ReportManager\js\ReportingServices.js следующую функцию:
    function pageLoad() { var toDecode = document.getElementsByTagName("div"); // получаем все DIVы for (var i = 0; i < toDecode.length; ++i){ // перебираем if(toDecode[i].innerHTML.indexOf("HTMLInject:") == 0){ // DIV содержит префикс? Decoded = toDecode[i].innerText // toDecode[i].textContent; // поддержка IE, Opera, FF toDecode[i].innerHTML = Decoded.slice(11); // вырезаем префикс & TXT --> HTML } } }
  2. Теперь, чтобы вывести поле как HTML, в свойствах placeholder'а в поле Value надо написать следующее:
    ="HTMLInject:"+Fields!FieldName.Value , где FieldName - имя поля датасета, содержащего HTML.

    При этом свойство "General\Markup type" самого поля нужно поставить в значение "None - Plain text only".

  3. При необходимости изменения стилей HTML - можно внести правки в этот CSS: %SSRS Install Dir%\ReportingServices\ReportManager\Styles\ReportingServices.css Однако, мне повезло и мои HTML таблицы были отформатированы классами, которые я и добавил в CSS-файл.

А теперь примеры


Так по-умолчанию показывается поле ("None - Plain text only"):

Так показывает таблицы, если тип разметки поставить в "HTML - Interpret HTML tags as styles":

Мой вариант:

Про/контра такого решения


Достоинства:
  • Работает
  • Легко и быстро сделать

Недостатки:
  • Работает только при отображении отчетов в веб-интерфейсе. В других форматах отчетов (Excel, PDF, Word...) отображается весь HTML с тегами. Как обходное решение можно использовать несколько столбцов для одного поля и признак видимости "Show or hide based on an expression" с формулой "=Globals!RenderFormat.Name"(то есть показывать только один столбец, в зависимости от формата отчета). Далее, в отчетах для других форматов уже можно разработать функцию для плейсхолдера, чтобы как-то причесать HTML (например заменой на поддерживаемые теги, или отображением как картинки).
  • Слетает навигация по меню отчета (DocumentMap) - то есть якоря в документе расставляются некорректно и для отрендеренных в HTML элементов может происходить смещение на величину, составляющую разницу между тем, как собирался показать SSRS наше поле, и как в итоге показал.
  • Быстродействие на сложных отчетах и медленных клиентах
  • XSS

Выводы


Вот так, с помощью нехитрых приспособлений, можно научить SSRS показывать HTML… Но ЗАЧЕМ?!
Сотни постов, от растерянных до гневных, переполняют интернеты из-за такой мелочи.
Даже BIRT это уже давно умеет, а новейшая версия SSRS2012 еще нет (пруфлинк: http://technet.microsoft.com/en-us/library/ff519562.aspx).
Есть мысли, что MS реализовала показ HTML в таком ограниченном виде чтобы не пострадала безопасность/стабильность системы отчетности, но похоже, что все альтернативные решения напоминают пресловутый "троллейбус".

UPD:
Про рендеринг HTML в картинку - вот на основе этого можно попробовать разработать Custom report Item.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=35797