it-swarm.com.de

Wie kann ich eine Antwort von Browsern zwischenspeichern lassen?

Ich arbeite an einer Drupal Controller-Implementierung, die ein generiertes Image als Antwort zurückgibt. Ich habe Symfony's Response verwendet und versucht, einen ETag-Header hinzuzufügen:

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class MyController extends ControllerBase {

  public static function viewGraph(string $id, Request $request) {
    $response = new Response();
    $response->setEtag($etag); // some etag genrated from $id
    if ($response->isNotModified($request)) {
      return $response;
    }
    // ...
    return $response;
  }

}

Ich habe erwartet, dass der Header "ETag" In meiner Antwort angezeigt wird. Aber da ist nichts. Ich habe sogar versucht CacheableResponse und es hat nicht so gut funktioniert.

Was ist los mit meinem Ansatz? Was ist der richtige Ansatz?

1
Koala Yeung

Dank der Antwort von 4k4 . Ich habe mich eingehender mit dem Thema befasst. Hier ist etwas, das anderen zugute kommen könnte, die das Gleiche tun möchten.

ETag-Header-Einstellung

Die Klasse FinishResponseSubscriber prüft, ob die Antwort zwischengespeichert werden kann . Wenn ja, können Sie loslegen. Dann wird es auch überprüfen Sie, ob die Antwort angepasst ist indem Sie überprüfen, ob Sie Setup "Cache-Control" Header zu "public":

  • Wenn es "angepasst" ist, wird davon ausgegangen, dass Sie Ihren Header selbst richtig eingerichtet haben.
  • Wenn nicht, wird setResponseCacheable aufgerufen und der ETag-Header mit dem Zeitstempel neu geschrieben, den er für geeignet hält (entweder Ihr "Last-Modified" Headerwert oder Anforderungszeit).

Benutzerdefinierter Header

Wie 4k4 hervorhob, können Sie das ETag-Setup folgendermaßen erreichen:


use Drupal\Core\Cache\CacheableResponse;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;

class MyController extends ControllerBase {

  public static function viewGraph(string $id, Request $request) {
    // ... generate $etag
    $response = new CacheableResponse();
    $response
      ->setPublic()
      ->setMaxAge(60)
      ->setEtag($etag);
    if ($response->isNotModified($request)) {
      return $response;
    }
    return $response;
  }

}

Hände weg zu Drupal


use Drupal\Core\Cache\CacheableResponse;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;

class MyController extends ControllerBase {

  public static function viewGraph(string $id, Request $request) {
    // ... get $last_modified
    $response = new CacheableResponse();
    $response->setLastModified($last_modified);
    if ($response->isNotModified($request)) {
      return $response;
    }
    return $response;
  }

}

Wenn sich herausstellt, dass die Antwort nicht zwischengespeichert werden kann, wird FinishResponseSubscriber :: setResponseNotCacheable aufgerufen und der ETag-Header deaktiviert.

Wenn Sie also FinishResponseSubscriber nicht überschreiben, können Sie sich nur an das von Drupal generierte ETag halten.

Und der ETag-Header wird von Ihrem Last-Modified Header automatisch.

Warten Sie, es gibt noch mehr ...

Oh, richtig. Selbst wenn Sie den Header richtig verstanden haben, bedeutet dies nicht , dass Ihre Antwort zwischengespeichert werden kann ! Dies liegt daran, dass dieser Cache-Header nicht die einzige Voraussetzung ist, um ETag zu sehen und über einen Browser-Cache zu verfügen.

Ab Drupal 8.7, um eine zwischengespeicherte Antwort zu erhalten, überprüft FinishResponseSubscriber sowohl die Anforderung als auch die Antwort, um festzustellen, ob beide zwischengespeichert werden können.

Die folgenden Punkte sind die zusätzlichen Kriterien, damit der Browser-Cache funktioniert.

Browser-Cache einrichten Max. Alter

Wie bereits erwähnt ( 4k4 ), müssen Sie das maximale Alter des Browser-/Proxy-Cache auf einen Wert ungleich Null einstellen, damit ein Browser-Cache vorhanden ist. Die Einstellung befindet sich unter "Start> Administration> Konfiguration> Entwicklung> Leistung".

Stellen Sie sicher, dass Ihre Anfrage zwischengespeichert werden kann

Die Anforderung wird, sofern nicht anders konfiguriert/programmiert, von DefaultRequestPolicy überprüft, das zwei Regeln enthält:

  1. Drupal\Core\PageCache\RequestPolicy\CommandLineOrUnsafeMethod
    Verweigert den Cache, wenn Drupal wird in der CLI oder mit einer unsicheren Methode ausgeführt (d. H. Die Anforderungsmethode ist weder GET noch HEAD); und
  2. Drupal\Core\PageCache\RequestPolicy\NoSessionOpen
    Verweigert den Cache, wenn der Benutzer Drupal) eine Anmeldesitzung hat.

Stellen Sie sicher, dass Ihre Antwort auch zwischengespeichert werden kann

Die Antwort-Cache-Richtlinie in meiner Installation enthält die folgenden Regeln:

  1. Drupal\Core\PageCache\ResponsePolicy\KillSwitch
    Eine Richtlinie, die als statisch :: DENY ausgewertet wird, wenn der Kill-Schalter ausgelöst wurde.

  2. Drupal\Core\PageCache\ResponsePolicy\DenyNoCacheRoutes
    Cache-Richtlinie für Routen mit der Option 'no_cache'.
    Diese Richtlinienregel verweigert das Zwischenspeichern von Antworten, die für Routen generiert wurden, für die die Option 'no_cache' auf TRUE gesetzt ist.

  3. Drupal\Core\PageCache\ResponsePolicy\NoServerError
    Eine Richtlinie, die das Zwischenspeichern von Serverfehlerantworten (HTTP 5xx) verweigert.

  4. Drupal\image\PageCache\DenyPrivateImageStyleDownload
    Cache-Richtlinie für Bildvorschau-Seite.
    Diese Richtlinienregel verweigert das Zwischenspeichern von Antworten, die von der Route entity.image.preview generiert wurden.

  5. Drupal\node\PageCache\DenyNodePreview
    Cache-Richtlinie für die Knotenvorschau-Seite.
    Diese Richtlinienregel verweigert das Zwischenspeichern von Antworten, die von der Route entity.node.preview generiert wurden.

Außerdem müsste Ihr Response-Objekt auch CacheableResponseInterface implementieren. Der einfachste Weg, dies zu implementieren, wäre die Verwendung der Klasse CacheableResponse

Zusammenfassung

Zusammenfassen. Damit ein benutzerdefinierter Controller über einen Browser-Cache verfügt, müssen Sie die folgenden Kriterien erfüllen:

  1. Die Controller-Methode des Endpunkts muss ein Objekt zurückgeben, das die CacheableResponseInterface (z. B. eine CacheableResponse -Instanz) implementiert.
  2. Richtiges Cache-Header-Setup.
  3. Das maximale Alter des Caches ungleich Null haben.
  4. Lassen Sie den Browser/Client den Endpunkt des Controllers mit der Methode GET oder HEAD aufrufen.
  5. Stellen Sie sicher, dass während des Rendervorgangs kein Kill-Schalter oder Fehler ausgelöst wird.
  6. Die Route stimmt nicht mit der Vorschauseite des Knotens oder Bilds überein.

Wenn die Kriterien erfüllt sind, ist das ETag-Setup wirksam.


Update: Korrigieren Sie meine Behauptung "Sie können ETag nicht setzen". Danke 4k4 für die Korrektur.

0
Koala Yeung

FinishResponseSubscriber überschreibt den ETag-Header, es sei denn, Ihre Antwort erfüllt bestimmte Bedingungen. Das heißt, es muss zwischenspeicherbar sein und einen benutzerdefinierten Cache-Control-Header bereitstellen. Stellen Sie beim Testen des Codes sicher, dass das Browser-Caching aktiviert ist (in den Leistungseinstellungen).

Beispiel:

  public function view() {
    $response = new CacheableResponse();
    $response->headers->set('Cache-Control', 'public, max-age=60');
    $response->setEtag('123'); 
    return $response;
  }

Neben dem Browser-Caching möchten Sie möglicherweise auch Drupal Caching-Daten festlegen, um die zwischengespeicherte Antwort ungültig zu machen, wenn der Inhalt veraltet ist. Siehe z. B. Cache auf einem REST - Endpunkt ungültig machen .

6
4k4