Denis Gladkikh
Russian   |  English

RESTful WCF Service - Узнаем версию браузера в методе сервиса

Сначала опишу задачу, которую мы решали в нашем проекте. У нас есть Silverlight клиентская часть и серверная часть, состоящая из набора WCF сервисов. Причем задумка у нас такая, что изначально мы разрабатываем эти сервисы с возможностью хостинга их вне веб-сервера, потому ASP.NET Compatible mode у нас отключен (зачем я так и не понял, ведь реально инсталлируем всегда на веб-сервер, ну усложнили себе жизнь - пускай). Следовательно, до значения HttpContext.Current нам никак не добраться из методов WCF сервисов. Один из WCF сервисов – это RESTful сервис, который отдает файлы (отчеты), то есть умеет обрабатывать GET запросы, все по стандартному:

WebOperationContext context = WebOperationContext.Current;
context.OutgoingResponse.ContentLength = reportBytes.Length;
context.OutgoingResponse.ContentType = "application/pdf";
context.OutgoingResponse.Headers.Set("content-disposition", "attachment;filename=" + fileName);

Но тут у нас возникала проблема. Имена файлов содержат пробелы, и разные браузеры обрабатывают их по-разному. Первая проблема: если filename содержит имя вроде “filename with spaces.pdf”, то FireFox нам покажет только filename, решается она просто, нужно имя файла заключить в кавычки (уже в который раз пишешь один и тот же код, а все равно можно напороться на уже известную проблему):

context.OutgoingResponse.Headers.Set("content-disposition", "attachment;filename=\"" + fileName + "\"");

А вот Internet Explorer 8 обрабатывает по-другому, он заменяет пробелы на ‘_’. В голову сразу пришла идея

filename = HttpUtility.UrlPathEncode(filename);

Теперь вместо “filename with spaces.pdf” на сервере в переменной filename мы имеем “filename%20with%20spaces.pdf” и Internet Explorer теперь сохраняет файл с тем именем что нужно (с пробелами), а вот FireFox наоборот оставляет %20 вместо пробелов. Решил найти способ, как определить какой браузер использует пользователь:

private bool IsInternetExplorer()
{
  WebOperationContext context = WebOperationContext.Current;
  if (context != null)
  {
    string userAgentInfo = context.IncomingRequest.Headers["User-Agent"];
    if (userAgentInfo != null)
      return userAgentInfo.Contains("MSIE");
  }
  return false;
}

А точнее сделал затычку именно для Internet Explorer:

if (IsInternetExplorer())
  filename = HttpUtility.UrlPathEncode(filename);

Удивило, что решение, как определить браузер я шустро не нашел в интернете, хотя знал, что ну должен быть способ найти user-agent строчку из запроса. Поначалу написал реализацию, которая использовала OperationContext:

private bool IsInternetExplorer()
{
  OperationContext context = OperationContext.Current;
  HttpRequestMessageProperty httpRequest = context.IncomingMessageProperties["httpRequest"] as HttpRequestMessageProperty;
  if (httpRequest != null)
  {
    string userAgentInfo = httpRequest.Headers["User-Agent"];
    if (userAgentInfo != null)
      return userAgentInfo.Contains("MSIE");
  }
  return false;
}

Хорошо, что позже вспомнил про WebOperationContext, вообще в WCF все как-то ну не очень прозрачно. Постараюсь в будущем осилить эту тему посильнее, и может попробовать сдать сертификационный экзамен.

Больше чем уверен, что Макс скажет еще о каких-либо проблемах, который этот код еще содержит и как их решить, насколько я знаю – он с этими отдачами файлов в свое время очень намучался с подобной реализацией, правда на ASP.NET, но разницы-то нет.


Вас также может заинтересовать

rss twitter

Комментарии (11)

force ( ) #
avatar
Раз уж я проходил мимо, могу сказать, что основная бяка начинается с русскими именами. С энкодингом для них там всё мутно.
Самое простое решение, как всё вылечить — сделать фейковый url где хвостик имя файла (вместо getfile.aspx?id=3 сделать getfile.aspx/3/filename%20with%20spaces.pdf). Этого браузеру хватит, чтобы корректо подцепить нужное имя, но тут есть полтора недостатка, первый в том, что надо следить за тем, чтобы пробелы не превратились в плюсы (тогда у IIS начинается паранойя, и её надо лечить настройками), и второй, что файл не будет передаваться как аттачмент, что в некоторых случаях не есть хорошо, ибо надо насильно протолкнуть файл пользователю на загрузку.
Denis Gladkikh ( ) #
avatar
force, спасибо Макс. Ох, как хорошо что я уже забыл все проблемы с кириллицей :) Надеюсь мы нашим заказчикам в Средней Азии не будем переводить все на арабский, а то чувствую там отгребем по полной. ;)
Calabonga ( ) #
avatar
ВСё хорошо и толково за исключением того, что неверная орфография режет глаз. Частица "то" в слова "как-то", "разницы-то" пишется через дефис. Написание слов можно проверить, например, на http://www.gramota.ru/
Denis Gladkikh ( ) #
avatar
Уважаемый Calabonga, мой редактор force пропустил текст в печать :) А вас своим редактором я сделать не могу так как: "ВСё" должна иметь одну заглавную, и "в слова" немного не связано. Но все же пожелания я учту. ;)
Ilya ( ) #
avatar
Да, помню такую проблему, правда очень давно это было. У Вас не возникла проблема с ограничением максимальной длины имени файла в IE?
force ( ) #
avatar
Теперь я стал персональным редактором и мне стало стыдно за ошибки, которые я не указал :)
Ну да ладно, это всё мелочи (отсутствующие запятые и чуть-чуть стилистика). Текст больше технический, в нём код важнее :)
Главное, что с тся/ться всё в порядке :)
Denis Gladkikh ( ) #
avatar
Ilya, вообще припоминаю что-то такое, проблема точно была, а вот как мы это лечили не вспомнить, скорее всего просто урезанием имени. Или может быть проблема была только в IE меньше 8-й версии.

force, "Текст больше технический, в нём код важнее" - золотые слова :) Не стихи пишем. Но все же, русский язык подтягивать нужно - с этим я согласен.
Аноним ( ) #
avatar
Почему бы не определить браузер на клиенте и не передать как дополнительный параметр в сервис?
Denis Gladkikh ( ) #
avatar
Аноним, это запрос вида http://site.com/GetReport/2010-08-13/ReportType_Type/, такой url можно сохранить в избранном, этот запрос даже никак не связан с Silverlight.

Если бы ссылка давалась только из Silverlight приложения, то конечно можно хранить в сессии пользователя информацию о его браузере - это тоже вариант, но хотелось бы чтобы пользователи могли обмениваться ссылками и т.п.
KLUBS ( ) #
avatar
А действительно. Можно же через JS определить браузер и передать в Silverlight.
Denis Gladkikh ( ) #
avatar
KLUBS, можно, если бы была изначально страница или еще чего, а так это просто так скажем прямая ссылка на документ.
Добавить комментарий

Если вы хотите получать уведомления о новых комментариях к данному топику, укажите, пожалуйста, email и отметьте соответствующий пункт в форме. Если вы хотите добавить код в тексте комментария, то заключите его внутри тега [code]...[/code], более того можно уточнить язык, на котором написан данный код при помощи [code cs]...[/code], где вместо cs могут быть cs, html, xml, java, js, php, sql, cpp, css.

 

busy