mirror of
https://gitee.com/dcren/initializr.git
synced 2025-07-15 22:58:01 +08:00
Prevent caching issues with browsers
The main metadata endpoint is available at the root "/" path, for JSON compatible media types. This endpoint is often requested by CLI and IDEs. Initializr is setting HTTP response headers to tell clients to cache the response body. With this current situation, several HTTP caching issues can happen. 1. Since many formats are available at the same path, proxies can cache the response body and redistribute it to many clients, even if they don't request the same media type. To fix that, we need to add a `Vary: Accept` response header; with that, proxies will cache responses but take into account that different Accept request headers might yield different responses. 2. Browsers have very specific caching implementations, and exposing that metadata endpoint on "/" and at the same time an HTML page will create issues related to HTTP caching. Navigation and refreshes might result with strange problems. To fix that, we need to reinstate the `/metadata/client` endpoint as a first class citizen (and not just a redirect). This way, Web UIs can freely use that path to request the metadata, without risking caching issues. See gh-914
This commit is contained in:
parent
ce150380c2
commit
ba73613050
@ -113,11 +113,6 @@ public class MainController extends AbstractInitializrController {
|
|||||||
return this.metadataProvider.get();
|
return this.metadataProvider.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping("/metadata/client")
|
|
||||||
public String client() {
|
|
||||||
return "redirect:/";
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(path = "/", produces = "text/plain")
|
@RequestMapping(path = "/", produces = "text/plain")
|
||||||
public ResponseEntity<String> serviceCapabilitiesText(
|
public ResponseEntity<String> serviceCapabilitiesText(
|
||||||
@RequestHeader(value = HttpHeaders.USER_AGENT,
|
@RequestHeader(value = HttpHeaders.USER_AGENT,
|
||||||
@ -152,19 +147,20 @@ public class MainController extends AbstractInitializrController {
|
|||||||
return builder.eTag(createUniqueId(content)).body(content);
|
return builder.eTag(createUniqueId(content)).body(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(path = "/", produces = "application/hal+json")
|
@RequestMapping(path = { "/", "/metadata/client" }, produces = "application/hal+json")
|
||||||
public ResponseEntity<String> serviceCapabilitiesHal() {
|
public ResponseEntity<String> serviceCapabilitiesHal() {
|
||||||
return serviceCapabilitiesFor(InitializrMetadataVersion.V2_1,
|
return serviceCapabilitiesFor(InitializrMetadataVersion.V2_1,
|
||||||
HAL_JSON_CONTENT_TYPE);
|
HAL_JSON_CONTENT_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(path = "/",
|
@RequestMapping(path = { "/", "/metadata/client" },
|
||||||
produces = { "application/vnd.initializr.v2.1+json", "application/json" })
|
produces = { "application/vnd.initializr.v2.1+json", "application/json" })
|
||||||
public ResponseEntity<String> serviceCapabilitiesV21() {
|
public ResponseEntity<String> serviceCapabilitiesV21() {
|
||||||
return serviceCapabilitiesFor(InitializrMetadataVersion.V2_1);
|
return serviceCapabilitiesFor(InitializrMetadataVersion.V2_1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(path = "/", produces = "application/vnd.initializr.v2+json")
|
@RequestMapping(path = { "/", "/metadata/client" },
|
||||||
|
produces = "application/vnd.initializr.v2+json")
|
||||||
public ResponseEntity<String> serviceCapabilitiesV2() {
|
public ResponseEntity<String> serviceCapabilitiesV2() {
|
||||||
return serviceCapabilitiesFor(InitializrMetadataVersion.V2);
|
return serviceCapabilitiesFor(InitializrMetadataVersion.V2);
|
||||||
}
|
}
|
||||||
@ -180,7 +176,8 @@ public class MainController extends AbstractInitializrController {
|
|||||||
String content = getJsonMapper(version).write(this.metadataProvider.get(),
|
String content = getJsonMapper(version).write(this.metadataProvider.get(),
|
||||||
appUrl);
|
appUrl);
|
||||||
return ResponseEntity.ok().contentType(contentType).eTag(createUniqueId(content))
|
return ResponseEntity.ok().contentType(contentType).eTag(createUniqueId(content))
|
||||||
.cacheControl(CacheControl.maxAge(7, TimeUnit.DAYS)).body(content);
|
.varyBy("Accept").cacheControl(CacheControl.maxAge(7, TimeUnit.DAYS))
|
||||||
|
.body(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InitializrMetadataJsonMapper getJsonMapper(
|
private static InitializrMetadataJsonMapper getJsonMapper(
|
||||||
|
@ -88,7 +88,7 @@ class MainControllerServiceMetadataIntegrationTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void metadataClientRedirect() {
|
void metadataClientEndpoint() {
|
||||||
ResponseEntity<String> response = execute("/metadata/client", String.class, null,
|
ResponseEntity<String> response = execute("/metadata/client", String.class, null,
|
||||||
"application/json");
|
"application/json");
|
||||||
validateCurrentMetadata(response);
|
validateCurrentMetadata(response);
|
||||||
|
Loading…
Reference in New Issue
Block a user