Наверняка любой разработчик, который пишет для web сталкивался с проблемой кэширования контента. При этом проблема может делится на две части: кэширование динамического контента и кэширование статического контента. Эти проблемы связанные с оптимизацией времени загрузки сайта. Я же подойду к этой проблеме с другой стороны: проблемы с кэшированием при разработке. Ведь все мы сталкивались с тем, что при изменении всего-лишь одной строке в javascript или изменении класса в CSS приходится в очередной раз очищать кэш браузера. И никакие магические сочетания Ctrl + F5 не помогают. В случае с Internet Explorer не всегда помогает даже очиситка кэша (Ctrl+R) в IE Developer Tools - приходится это делать стандартными средствами браузера.

Для себя я нашел следующий выход из сложившейся ситуации: отключение кэширования на стороне веб-сервера на момент разработки. Почему это нельзя сделать в браузере? Одна из причин - потому что, как правило, сайт разрабатывается для более чем одного браузера. Другая - можно забыть включить обратно и работы с другими сайтами будет не такая комфортная.

Уточню, что я редко использую веб-сервер, встроенный в Visual Studio, т.к. считаю что IIS, встроенный в Windows Vista/7 более приближен к тому решению, которое будет на stage и production environments. Из этих же соображений некоторый используют Windows Server на своих рабочих станциях.

Как всегда, сначала немного теории о том, как это всё кэширование работает.

В заголовке ответа от веб-сервера (Response) может быть поле Cache-Control, которое говорит браузеру о том, как нужно кэшировать полученный файл. Самые используемые поля это:

  • no-cache - отключить кэширование;
  • max-age - максимальное время (в секундах) жизни закешированного ресурса;
  • min-fresh - минимальное время (в секундах) жизни закешированного ресурса.


Так же часто используется параметр “Expires”, который содержит дату, когда необходимо будет заново загрузить ресурс с сервера.
Подробнее о всём это можно почитать в спецификации HTTP протокола по адресу http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html.

Ещё одним способом указать браузеру, что запрашиваемый ресурс не изменился является ответ с кодом “304 Not Modified” (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html).

Таким образом, для отключения кэширования нам достаточно всегда добавлять поле no-cache в заголовок response. Побочных эффектов я тут вижу два:

  • нужно не забыть потом включить кэширование;
  • при отключенном кэшировании производительность сайта может значительно упасть, но я надеюсь, что у всех нас имеются достаточно мощные ПК для комфортной работы/разработки.

Подробно всё уже давно описано на сайтах Technet (http://technet.microsoft.com/en-us/library/cc770661(WS.10).aspx) и IIS (http://www.iis.net/ConfigReference/system.webServer/staticContent/clientCache), поэтому повторяться не буду, тем более что у меня вряд ли получится написать лучше. Скажу только пару слов о моём решении.

Кэширование я отключаю с момощью комманды “appcmd set config /section:staticContent /clientCache.cacheControlMode:DisableCache”, которую я поместил в bat-файл discache.bat.

Включаю аналогичной командой “appcmd set config /section:staticContent /clientCache.cacheControlMode:NoControl” из файла encache.bat.

Ярлыки для этих файлов можно поместить в удобное для вас место либо добавть путь к ним в переменную окружения PATH для быстрого доступа к ним из любого места. Таким образом включение/выключение кэширования происходит одним кликом/одной строчкой в командной строке и позволяет избавиться от всех проблем при активной работе с javascript, CSS и картинками.


Люди всегда делились на две категории: одни всегда "впереди планеты всей", вторые их догоняют. В мире программирования всё происходит аналогично: одни используют новое ПО начиная с ранних CTP (Community Technology Preview) версий, вторые переходят на них, в лучшем случае, уже после релиза. Так сложилось и в ноём текущем проекте: .net framework 4.0 вышел достаточно давно, но перейти на него получилось только сейчас, и то не полностью.

Несмотря на то, что, как правило, переход на новую версию .net framework не вызывает особых проблем (код с 3.5 успешно работает в среде 4.0), некоторые нюансы всё же есть. Нише привожу описание шагов и проблем, с которыми столкнулся.

1. Открываем наш solution (project) в Visual Studio 2010. Тут не должно быть никаких проблем: IDE сама предложит сконвертировать ваши проекты в новый формат. Если в solution нет никаких специфических проектов (например, проекты, которые создаются какими-то плагинами и их поддержки нет из коробки), то вскоре вы увидете отчёт об успешной конвертации.

2. В свойствах каждого проекта меняем Target Framework на .NET Framework 4 и пробуем всё это скомпилировать. Честно говоря, я не слышал ещё, что бы у кого-то были проблемы с этим шагом.

3. Двигаемся дальше и пробуем запусти Web Application. Вот здесь нас и поджидают первые сюрпризы. Я рассматриваю вариант, когда сайт живёт на полноценном IIS7, а не на том веб-сервере, который встроен в Visual Studio. И если при конвертации проекта не был выбран пункт “Автоматически перевести все проекты на .NET 4.0” (такое вполне вероятно когда сначала был произведен переход на Visual Studio 2010, а смена версии .NET Framework производилась посже), то вместо привычного вида своего сайта видна серверная ошибка:

error

Ошибка появляется из-за того, что немного поменялся формат файла web.config и он стал несовместимым с предыдущей версией. Решение простое - поменять web.config. И тут, как всегда, есть два способа: ручной и автоматический. Я для себя выбрал более простой и быстрый способ №2 и его советую всем. Способ заключается в запуске комнсольной утилиты из поставки IIS7 appcmd.exe. Синткаксис выглядит так:

%systemroot%\system32\inetsrv\APPCMD.EXE migrate config "Default Web Site/YourSiteName"

Более подробно о всех ключах можно почитать в помощи:

%systemroot%\system32\inetsrv\APPCMD.EXE /?

4. После этих манипуляций должно всё заработать. Но я пошел немного дальше и захотел использовать Url Routing, который есть в ASP.NET 4 (более подробно о нем можно почитать в блоге Scott Guthrie) и при открытии страниц получал 404-ю ошибку - страница не найдена. Немного покопавшись в MSDN я понял, что причина проблемы - application pool, а вернее его Managed Pipilene Mode. После выбора пункта “Integrated” всё заработало. Более подробно почитать можно на сайте IIS7.

Я описал лишь те проблемы, которые были у меня. Возможно, у вас они будут другие или вам повезёт и всё пройдет так, как рассказывает Microsoft - 2-3 клика и проект работает без проблем. Мне не повезло :(.


Всё началось с того, что на один из тестовых серверов поставили 64-х битную ОС. Особых проблем это не вызвало, за исключением того, что обна из сборок использует COM -объекты и появилась необходимость её сборки для платформы x86. Вот сдесь уже начали появляться первые подводные камни.

Первым делом я в свойствах проекта поменял свойство Platform Target (посже оказалось что это нужно сделать для всех проектов в solution):

 

 
Но после этих изменений ошибка "Can not load assembly ..." не пропала.  Следующим шагом пошел смотреть свойства solution и...
 
 
... и, с удивлением, обнаружил, что в качестве Target Platform для всех проектов стоит "Any CPU", вместо необходимого "x86". Пришлось идти в Configuration Manager и создавать ноую платформу для текущего solution.
 
 
Но и после всех этих танцев с бубном сайт не заработал. Тут я решил заглянуть в настройки IIS, попутно вспониная создателей Microsoft, .NET и их родственников: и это вы называете кроссплатформенной средой? Здесь меня тоже ждал сюрприз в качестве одного из параметров Application Pool'а, а именно - Enable 32-Bitt Applications, который, по умолчанию, чтоб его конечно же равен False.
 
 
Хорошо что хоть это помогло, иначе я уже был готов перустанавливать Windows и/или избавляться от старой библиотеки, которая использует COM(в нашем случае можно обойтись без этого).
 
P.S. Немного оффтопа. Стало интересно, как это всё сможет работать на x64 системах, который вовсю сейчас продвигаются Microsoft. Ещё интересно где обещанная мультиплатформенность .NET? IMHO, считаю что её нет она находится в зачаточном состоянии, т.к. всё ещё можно скомнилить сборку толоько под x86 или x64. Понимаю, что это делалось только для "нормального" запуска exe-файлов, но зачем же это было делать такой ценой?

PlatrormTestWeb.zip (10.01 kb)


Всё началось с того, что в спецификации к проекту написали примерно такое: "Время продолжительности сэссии пользователя на сайте должно составлять 120 минут". После чего, в web.config была добавлена следующая строка: 

<sessionState mode="InProc" cookieless="false" timeout="120" />

А на страницу был добавлен такой мета-тег:

<meta http-equiv="Refresh" content="7200; URL=/EzRc/Pages/LogOn/SessionExpired.aspx" />

Следует упомянуть конфигурацию тестовых серверов: Windows Vista/2008, IIS7, .NET 3.5. Ничто не предвещало беды. Но, как и полагается, в один "прекрасный" день всеми людимые QA написали баг следующего содержания: "Session expiration occurs prior to 30 min (and as little as 10 min)." При этом повторить его было достаточно просто:

  • залогиниться на сайт
  • оставить браузер в покое на 30 минут

После этих нетрудный манипуляций при попытке перейти по любой ссылке мы оказывались на странице, в которой радостно говорилось об окончании сэссии и предлагалось перейти на страницу логина. Естественно, этот баг повторялся не всегда.

Ещё раз убедившись в правильности настроек сэссии в web.config я реши воспроизвести этот баг на локальном (dev) компьютере. Как ни странно, но баг воспроизводился в 100% случаев. "Странно" - подумал я и налил ещё чашку кофе.

Запустив Fiddler2 и залогинившись на сайт я снова оставил его в покое на 30 минут. Через это время, убедившись, что cookie приходят валидные, я наал смотреть логи. Напервый взгляд всё было хорошо, но присмотревшись внимательно, увидел что отрабатывает событие ApplicationStart. Теперь понятно почему заканчивается сэссия. Осталось разобраться почему перезапускается приложение.

Из логов IIS:

Event code: 1002
Event message: Application is shutting down. Reason: Hosting environment is shutting down.
Event time: 6/8/2009 1:50:21 PM
Event time (UTC): 6/8/2009 10:50:21 AM
Event ID: 80d0faffb34547fea6299cfff8cf1c6f
Event sequence: 4
Event occurrence: 1
Event detail code: 50002
 
Application information:
    Application domain: /LM/W3SVC/1/ROOT/Web-1-128889305624881519
    Trust level: Full
    Application Virtual Path: /EzRc
    Application Path: C:\Src\Sites\Web\
    Machine name: localdev
 
Process information:
    Process ID: 6364
    Process name: w3wp.exe
    Account name: NT AUTHORITY\NETWORK SERVICE

 

 

После прочтения статьи из MSDN ASP.NET Application Life Cycle Overview ответ на интересующий вопрос не был получен, из чего был сделан вывод, что проблема находится уровне выше, а именно в IIS. Начал детально изучать настройки, которые могут повлиять на работу приложения и остановился на Application Pools. После чтения документации и нескольких неверных попыток был найден источник проблемы. Им оказался параметр Idle Time-out.

 



Оказалось, что при настройках по умолчанию, процесс, отвечающий за работу asp.net, тушится при условии, что к нему не обращаются в течении 20 минут. Это объясняло, почему проблему можно было не всегда воспроизвести на тестовом сервере.

Это же можно настроить и через файл machine.config. Подробнее описано здесь.


Уже немала было написано на эту тему, но в статье http://haacked.com/archive/2008/11/26/asp.net-mvc-on-iis-6-walkthrough.aspx пошли немного дальше: теперь url rewriting настроен таким образом, что нет необходимости в имени контроллера в пути прописывать расширение. Не буду утверждать что это что-то новое, но я до этого использовал пути вроде http://localhost/mvcsite/home.mvc.