Pull to refresh

Безопасное использование HTTP заголовка IfModifiedSince в PCL библиотеках

Reading time 2 min
Views 2.9K
Неожиданно связь прервалась Мы разрабатываем приложение под несколько платформ, всю платформонезависимую часть создаем с помощью 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);
Tags:
Hubs:
+4
Comments 3
Comments Comments 3

Articles