Cache, ETag, Expires a pravděpodobná chyba v Nette

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Tomáš Jacík
Člen | 147
+
0
-

Rozhodl jsem se načítat strom kategorií AJAXem, cachovat jej a neodesílat jej zbytečně když se nezměnil. Vypadá to takto:

/** @var Nette\Http\Context @inject */
public $httpContext;

/** @var Nette\Caching\IStorage @inject */
public $cacheStorage;

public function renderMenu()
{
	$cache = new Cache($this->cacheStorage, 'App');

	$categoryTree = $cache->load('ajaxCategoryTree');
	if (!$categoryTree)
	{
		$categoryTree = $this->getCategoryTree();
		$cache->save('ajaxCategoryTree', $categoryTree, array(
			Cache::EXPIRE => '5 minutes'
		));
	}

	$etag = md5(serialize($categoryTree));
	if ($this->httpContext->isModified(NULL, $etag))
	{
		$this->payload->categories = $categoryTree;
		$this->sendPayload();
	}

	$this->getHttpResponse()->setContentType('application/json', 'UTF-8');
	$this->getHttpResponse()->setExpiration('+5 minutes');
	$this->terminate();
}

Takto mi to funguje skvěle, ale nezdá se mi jak to v Nette funguje.

  1. Response si získám pohodově z presenteru, ale Http\Context musím injectovat a to jen kvůli jedné metodě, kterou potřebuju použít. Nemělo by to být spíše někde pohromadě s metodou setExpiration?
  2. Když pouiju isModified, nenastaví se mi expiration a tím pádem je ten zasílaný ETag stejně v response zbytečný, tak to musím takto použít z obou míst.
  3. Když nenastavím natvrdo Content-Type na json, tak to v případě 304 posílá debug, to je imho chyba, response by měla být prázdná.
  4. Bylo by fajn mít v Nette na celou tuhle proceduru nějaký helper a nerozepisovat to takto.
Milo
Nette Core | 1283
+
0
-

Tomáš Jacík napsal(a):
Takto mi to funguje skvěle, ale nezdá se mi jak to v Nette funguje.

  1. Response si získám pohodově z presenteru, ale Http\Context musím injectovat a to jen kvůli jedné metodě, kterou potřebuju použít. Nemělo by to být spíše někde pohromadě s metodou setExpiration?

HTTP hlavičky ETag a Expire jsou dvě různé techniky kešování. Mají fungovat odděleně. Pro ETag validaci potřebuješ od browseru If-None-Match hlavičku. Tj. potřebuješ Request i Response, v Nette zabalené v Http\Context. U ETag techniky se request dostane vždy až do tvojí aplikace. Šetří se jen objem přenesených dat, serveru se neodlehčí.

U Expire poznačíš, jak dlouho response platí. Tj. browser se dalších X minut ani nemusí zeptat anebo odpoví cestou proxy a na server se request ani nedostane.

  1. Když pouiju isModified, nenastaví se mi expiration a tím pádem je ten zasílaný ETag stejně v response zbytečný, tak to musím takto použít z obou míst.

viz.1 Btw. Http\Context má metodu getResponse()

  1. Když nenastavím natvrdo Content-Type na json, tak to v případě 304 posílá debug, to je imho chyba, response by měla být prázdná.

Tracy to tak detekuje. Defaultní je totiž text/html a Tracy tak může přidat kus HTML. Ale nevím, jestli to považovat za bug, možná jo.

Tomáš Jacík
Člen | 147
+
0
-

Ahoj, díky za reakci. Vím že ETag a expire jsou dvě různé věci. Snažil jsem se poznamenat, že bez nastavení expire je to nastavení ETagu stejně k ničemu, expire je totiž defaultně v minulosti a tak se browser příště ptá bez etagu (304 se zobrazí jen jednou a vyruší ho ten expire).

Také vím že Context má Request a Response, ale ten kontext potřebuji kvůli nastavení ETagu, versus Response potřebuju kvůli nastavení Expire, což by po nastavení ETagu nemělo být nutné. Navíc se každá z těch věcí získává jinou metodou, což považuji za nekonzistentní.

Víceméně, pro mě už to funguje jak potřebuju. Hledám způsob, jak to udělat jednodušeji, nebo někoho postrčit, aby to v budoucnu jednodušší bylo.