Cache-headere i Spring MVC

1. Oversigt

I denne vejledning lærer vi om HTTP-caching. Vi vil også se på forskellige måder at implementere denne mekanisme mellem en klient og en Spring MVC-applikation.

2. Introduktion til HTTP-caching

Når vi åbner en webside i en browser, downloader den normalt mange ressourcer fra webserveren:

I dette eksempel skal en browser f.eks. Downloade tre ressourcer til en /Log på side. Det er almindeligt, at en browser fremsætter flere HTTP-anmodninger for hver webside. Hvis vi nu anmoder om sådanne sider meget ofte, forårsager det meget netværkstrafik og det tager længere tid at betjene disse sider.

For at reducere netværksbelastningen tillader HTTP-protokollen browsere at cache nogle af disse ressourcer. Hvis aktiveret, kan browsere gemme en kopi af en ressource i den lokale cache. Som et resultat kan browsere betjene disse sider fra det lokale lager i stedet for at anmode om det via netværket:

En webserver kan dirigere browseren til at cache en bestemt ressource ved at tilføje en Cache-kontrol header i svaret.

Da ressourcerne cachelagres som en lokal kopi,der er en risiko for at servere uaktuelt indhold fra browseren. Derfor tilføjer webservere normalt en udløbstid i Cache-kontrol header.

I de følgende afsnit tilføjer vi denne overskrift i et svar fra Spring MVC-controlleren. Senere ser vi også Spring API'er for at validere de cachelagrede ressourcer baseret på udløbstiden.

3. Cache-kontrol i controllerens svar

3.1. Ved brug af SvarEnhed

Den mest ligefremme måde at gøre dette på er atbrug CacheControl bygherre klasse leveret af Spring:

@GetMapping ("/ hej / {navn}") @ResponseBody offentlig ResponseEntity hej (@PathVariable strengnavn) {CacheControl cacheControl = CacheControl.maxAge (60, TimeUnit.SECONDS) .noTransform () .mustRevalidate (); returnere ResponseEntity.ok () .cacheControl (cacheControl) .body ("Hej" + navn); }

Dette vil tilføje en Cache-kontrol header i svaret:

@Test ugyldig nårHome_thenReturnCacheHeader () kaster undtagelse {this.mockMvc.perform (MockMvcRequestBuilders.get ("/ hej / baeldung")) .andDo (MockMvcResultHandlers.print ()). OgExpect (MockMvcResultMatchers.) .Stat. andExpect (MockMvcResultMatchers.header () .string ("Cache-Control", "max-age = 60, must-revalidate, no-transform")); }

3.2. Ved brug af HttpServletResponse

Ofte skal controllerne returnere visningsnavnet fra behandlingsmetoden. Men denSvarEnhed klasse tillader os ikke at returnere visningsnavnet og behandle anmodningsorganet på samme tid.

Alternativt kan vi for sådanne controllere indstille Cache-kontrol header i HttpServletResponse direkte:

@GetMapping (værdi = "/ hjem / {navn}") offentligt String hjem (@PathVariable String navn, endelig HttpServletResponse svar) {respons.addHeader ("Cache-Control", "max-age = 60, must-revalidate, no -transform "); vende hjem"; }

Dette tilføjer også en Cache-kontrol header i HTTP-svar svarende til det sidste afsnit:

@Test ugyldigt nårHome_thenReturnCacheHeader () kaster undtagelse {this.mockMvc.perform (MockMvcRequestBuilders.get ("/ home / baeldung")) .andDo (MockMvcResultHandlers.print ()). Og Expect (MockMvcResultMatchers.) .Stat. Status. andExpect (MockMvcResultMatchers.header () .string ("Cache-Control", "max-age = 60, must-revalidate, no-transform")). ogExpect (MockMvcResultMatchers.view (). navn ("hjem")); }

4. Cache-kontrol til statiske ressourcer

Generelt serverer vores Spring MVC-applikation mange statiske ressourcer som HTML-, CSS- og JS-filer. Da sådanne filer bruger meget netværksbåndbredde, er det vigtigt for browsere at cache dem. Vi aktiverer igen dette med Cache-kontrol header i svaret.

Foråret giver os mulighed for at kontrollere denne cache-opførsel ved ressourcekortlægning:

@Override public void addResourceHandlers (final ResourceHandlerRegistry registry) {registry.addResourceHandler ("/ resources / **"). AddResourceLocations ("/ resources /") .setCacheControl (CacheControl.maxAge (60, TimeUnit.SECONDS) .noTransform (). mustRevalidate ()); }

Dette sikrer, at alle ressourcerdefineret under/ ressourcer returneres med en Cache-kontrol header i svaret.

5. Cache-kontrol i Interceptors

Vi kan bruge interceptors i vores Spring MVC-applikation til at foretage for- og efterbehandling for hver anmodning. Dette er en anden pladsholder, hvor vi kan kontrollere applikationens cache-opførsel.

Nu i stedet for at implementere en brugerdefineret interceptor, bruger vi WebContentInterceptor leveret af Spring:

@Override public void addInterceptors (InterceptorRegistry registry) {WebContentInterceptor interceptor = ny WebContentInterceptor (); interceptor.addCacheMapping (CacheControl.maxAge (60, TimeUnit.SECONDS) .noTransform () .mustRevalidate (), "/ login / *"); registry.addInterceptor (interceptor); }

Her registrerede vi WebContentInterceptor og tilføjede Cache-kontrol header svarende til de sidste par sektioner. Især kan vi tilføje forskellige Cache-kontrol overskrifter til forskellige URL-mønstre.

I ovenstående eksempel for alle anmodninger, der starter med /Log på, vi tilføjer denne overskrift:

@Test er ugyldigt, nårInterceptor_thenReturnCacheHeader () kaster undtagelse {this.mockMvc.perform (MockMvcRequestBuilders.get ("/ login / baeldung")) .andDo (MockMvcResultHandlers.print ()) .andExpect (MockMvcResultMatch.). andExpect (MockMvcResultMatchers.header () .string ("Cache-Control", "max-age = 60, must-revalidate, no-transform")); }

6. Validering af cache i foråret MVC

Indtil videre har vi diskuteret forskellige måder at inkludere en Cache-kontrol header i svaret. Dette indikerer, at klienter eller browsere skal cache ressourcerne baseret på konfigurationsegenskaber som f.eks maks. alder.

Det er generelt en god idé at tilføje en cache-udløbstid for hver ressource. Som et resultat kan browsere undgå at levere udløbne ressourcer fra cachen.

Selvom browsere altid skal kontrollere udløb, er det muligvis ikke nødvendigt at hente ressourcen igen hver gang. Hvis en browser kan validere, at en ressource ikke har ændret sig på serveren, kan den fortsætte med at tjene den cachelagrede version af den. Og til dette formål giver HTTP os to svaroverskrifter:

  1. Etag - et HTTP-svarhoved, der gemmer en unik hash-værdi for at bestemme, om en cachelagret ressource er ændret på serveren - en tilsvarende Hvis-ingen-kamp anmodningsoverskrift skal have den sidste Etag-værdi
  2. Sidst ændret - et HTTP-svarhoved, der gemmer en tidsenhed, da ressourcen sidst blev opdateret - en tilsvarende Hvis-ikke-ændret-siden anmodningsoverskrift skal indeholde den sidst ændrede dato

Vi kan bruge et af disse overskrifter til at kontrollere, om en udløbet ressource skal hentes igen. Efter validering af overskrifterneserveren kan enten sende ressourcen igen eller sende en 304 HTTP-kode for at angive nogen ændring. I sidstnævnte scenario kan browsere fortsætte med at bruge den cachelagrede ressource.

Det Sidst ændret header kan kun gemme tidsintervaller op til sekunder nøjagtighed. Dette kan være en begrænsning i tilfælde, hvor en kortere udløb er påkrævet. Af denne grund anbefales det at bruge Etag i stedet. Siden Etag header gemmer en hash-værdi, er det muligt at oprette en unik hash op til mere finere intervaller som nanosekunder.

Når det er sagt, lad os se hvordan det ser ud til at bruge Sidst ændret.

Spring giver nogle hjælpemetoder til at kontrollere, om anmodningen indeholder en udløbsoverskrift eller ej:

@GetMapping (værdi = "/ productInfo / {name}") offentlig ResponseEntity-validering (@PathVariable String name, WebRequest anmodning) {ZoneId zoneId = ZoneId.of ("GMT"); long lastModifiedTimestamp = LocalDateTime.of (2020, 02, 4, 19, 57, 45) .atZone (zoneId) .toInstant (). til EpochMilli (); hvis (request.checkNotModified (lastModifiedTimestamp)) {return ResponseEntity.status (304) .build (); } returner ResponseEntity.ok (). body ("Hej" + navn); }

Foråret giver checkNotModified () metode til at kontrollere, om en ressource er blevet ændret siden den sidste anmodning:

@Test ugyldigt nårValidate_thenReturnCacheHeader () kaster undtagelse {HttpHeaders headers = nye HttpHeaders (); headers.add (IF_UNMODIFIED_SINCE, "Tir, 04. feb 2020 19:57:25 GMT"); this.mockMvc.perform (MockMvcRequestBuilders.get ("/ productInfo / baeldung"). headers (headers)) .andDo (MockMvcResultHandlers.print ()) .andExpect (MockMvcResultMatchers.status (). er (304)); }

7. Konklusion

I denne artikel lærte vi om HTTP-caching ved hjælp af Cache-kontrol svarhoved i Spring MVC. Vi kan enten tilføje overskriften i controllerens svar ved hjælp af SvarEnhed klasse eller gennem ressourcekortlægning for statiske ressourcer.

Vi kan også tilføje denne overskrift til bestemte URL-mønstre ved hjælp af Spring interceptors.

Som altid er koden tilgængelig på GitHub.