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

    • Silverlight
    • .NET
    • C#
    • Internet Explorer 8
    • PDF
    • Reports
    • WCF
    • RESTful
    • FireFox
  • modified:
  • reading: 2 minutes

Сначала опишу задачу, которую мы решали в нашем проекте. У нас есть 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, но разницы-то нет.

See Also