Мы разрабатываем приложение под несколько платформ, всю платформо независимую часть создаем с помощью PCL, стараясь добиться максимального уровня пере использования компонентов. Для коммуникаций с сервером используется класс HttpWebRequest который реализует всю необходимую от транспортного уровня функциональность.
Недавно протокол был оптимизирован, в нем появилась возможность сократить объем передаваемых данных указывая горизонт актуальности. Реализована данная оптимизация с помощью обработки HTTP заголовка IfModifiedSince, однако у объекта HttpWebRequest в PCL не доступного свойство IfModifiedSince. Это не казалось проблемой заголовок был добавлен напрямую в коллекцию заголовков запроса.
request.Headers[HttpRequestHeader.IfModifiedSince] = timeStamp.ToString();
Протестировав решение на WindowsPhone приложении я убедился в его работоспособности, но все юнит тесты транспортного уровня выдали ошибки. Изучив проблему обнаружил, что реализация WebHeaderCollection для платформы Windows, не позволяет напрямую задавать этот заголовок и требует, чтобы разработчики использовали свойство HttpWebRequest.IfModifiedSince которое не доступно в сборках PCL.
Для решения этой я создал метод расширения для класса HttpWebRequest реализующий следующую логику работы:
При первом обращении к методу, пытаемся добавить заголовок в коллекцию заголовков запроса, если это не удается, с помощью отражения ищем у объекта запроса поле IfModifiedSince и создаем и кешируем делегат для установки значения свойства.
При последующих обращениях к методу, используем кэшированный делегат для установки значения заголовка IfModifiedSince.
namespace System.Net
{
public static class NetExtensions
{
private static Action<HttpWebRequest, DateTime> _setIfMofifiedSince;
public static void SetIfModifiedSince(this HttpWebRequest request, DateTime value)
{
Guard.NotNull(request);
if (_setIfMofifiedSince != null)
{
_setIfMofifiedSince(request, value);
return;
}
try
{
request.Headers[HttpRequestHeader.IfModifiedSince] = value.ToString();
return;
}
// ReSharper disable once EmptyGeneralCatchClause
catch
{
}
var property = request.GetType().GetRuntimeProperty("IfModifiedSince");
if (property != null
&& property.CanWrite)
{
var method = property.SetMethod;
if (method != null)
{
_setIfMofifiedSince = (Action<HttpWebRequest, DateTime>)method.CreateDelegate(typeof(Action<HttpWebRequest, DateTime>));
}
}
if (_setIfMofifiedSince == null)
{
throw new Exception("Unable to set IfModifiedSince");
}
_setIfMofifiedSince(request, value);
}
}
}
Используя этот метод расширения код остается простым для чтения и понимания.
request.SetIfModifiedSince(timeStamp);
Автор: Viacheslav01