Skip to content

Commit

Permalink
Add docs explaining @MimeType and @originalFilename annotations (#540)
Browse files Browse the repository at this point in the history
* Support for configuring cache control headers

Fixes #503

* Add docs for @MimeType and @originalFilename
  • Loading branch information
paulcwarren authored May 6, 2021
1 parent bdc1b8a commit 0428f2f
Show file tree
Hide file tree
Showing 6 changed files with 383 additions and 0 deletions.
33 changes: 33 additions & 0 deletions spring-content-rest/src/main/asciidoc/rest-cachecontrol.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
== Cache Control

By using cache control headers effectively, we can instruct the
browser to cache resources and avoid network hops. This decreases
latency, and also the load on our applications.

Spring Content REST allows us to control these headers by configuring
a set of cache control rules applied, in the order they are defined,
to all GET requests for content.

One, or more, rules can be configured as follows:

====
[source, java]
----
@Configuration
class CustomContentRestMvcConfiguration {
@Bean
public ContentRestConfigurer configurer() {
return new ContentRestConfigurer() {
@Override
public void configure(RestConfiguration config) {
config
.cacheControl()
.antMatcher("/testEntities/*", CacheControl.maxAge(Duration.ofSeconds(60)));
}
};
}
}
----
====
1 change: 1 addition & 0 deletions spring-content-rest/src/main/asciidoc/rest-index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ include::{spring-versions-jpa-docs}/jpaversions-rest.adoc[leveloffset=+1]
include::rest-cors.adoc[leveloffset=+1]

include::rest-baseuri.adoc[leveloffset=+1]
include::rest-cachecontrol.adoc[leveloffset=+1]
include::rest-fullyqualifiedlinks.adoc[leveloffset=+1]
include::rest-storeresolver.adoc[leveloffset=+1]
include::rest-putpostresolver.adoc[leveloffset=+1]
19 changes: 19 additions & 0 deletions spring-content-rest/src/main/asciidoc/rest-store.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Spring Content REST officially supports:
- https://github.com/paulcwarren/spring-content/spring-content-s3[Spring Content S3]
- https://github.com/paulcwarren/spring-content/spring-content-mongo[Spring Content Mongo]
- https://github.com/paulcwarren/spring-content/spring-content-jpa[Spring Content JPA]
- https://github.com/paulcwarren/spring-content/spring-content-gcs[Spring Content GCS]
- https://github.com/paulcwarren/spring-content/spring-content-azure-storage[Spring Content Azure Storage]
= Getting Started

Expand Down Expand Up @@ -143,6 +145,9 @@ Assume the following `Entity` class, `Repository` and `Store` interfaces:
@MimeType
private String mimeType;
@OiginalFileName
private String originalFileName;
// getters and setters
}
Expand All @@ -166,6 +171,20 @@ HTTP methods map onto the methods of `ContentStore` as follows:-
- POST/PUT -> setContent
- DELETE -> unsetContent

==== Additional Annotations

Spring Content REST adds two annotations to the core set of
annotations provided by Spring Content Commons for capturing,
on the entity, information that is available from the HTTP requests
made by clients.

- `@MimeType`; captures the `Content-Type` header of POST/PUT requests
and is re-used on subsequent GET request responses

- `@OriginalFileName`; captures the filename sent by POST/PUT requests,
if available, and is re-used to set the content dispostion attachment filename
on subsequent GET requests

=== Property Resources

Property Resources are associated with the properties of Spring Data Entities, that themselves maybe Spring Data Entities.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.content.commons.storeservice.StoreResolver;
import org.springframework.content.commons.storeservice.Stores;
import org.springframework.content.rest.config.StoreCacheControlInterceptor.StoreCacheControlConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
Expand Down Expand Up @@ -78,6 +79,10 @@ public void addStoreResolver(String name, StoreResolver resolver) {
stores().addStoreResolver(name, resolver);
}

public StoreCacheControlConfigurer cacheControl() {
return storeHandlerInterceptor().configurer();
}

public DomainTypeConfig forDomainType(Class<?> type) {
DomainTypeConfig config = domainTypeConfigMap.get(type);
if (config == null) {
Expand All @@ -92,10 +97,16 @@ Stores stores() {
return new StoresImpl(context);
}

@Bean
StoreCacheControlInterceptor storeHandlerInterceptor() {
return new StoreCacheControlInterceptor();
}

@Bean
RequestMappingHandlerMapping contentHandlerMapping() {
ContentHandlerMapping mapping = new ContentHandlerMapping(stores(), this);
mapping.setCorsConfigurations(this.getCorsRegistry().getCorsConfigurations());
mapping.setInterceptors(storeHandlerInterceptor());
return mapping;
}

Expand All @@ -106,9 +117,12 @@ StoreByteRangeHttpRequestHandler byteRangeRestRequestHandler() {

@Override
public void afterPropertiesSet() throws Exception {

for (ContentRestConfigurer configurer : configurers) {
configurer.configure(this);
}

storeHandlerInterceptor().setBaseUri(baseUri);
}

@Configuration
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package org.springframework.content.rest.config;

import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.CacheControl;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.util.UrlPathHelper;

import internal.org.springframework.content.rest.utils.StoreUtils;
import lombok.Getter;

public class StoreCacheControlInterceptor extends HandlerInterceptorAdapter {

private Map<String, CacheControl> cacheControlMap = new HashMap<>();
private List<CacheControlRule> cacheControlRules = new ArrayList<>();
private URI baseUri;

public StoreCacheControlInterceptor() {

}

public StoreCacheControlConfigurer configurer() {
return new StoreCacheControlConfigurer(this);
}

public void addCacheControl(String mapping, CacheControl cacheControl) {
cacheControlMap.put(mapping, cacheControl);
}

public void addCacheControlRule(CacheControlRule cacheControlRule) {
cacheControlRules.add(cacheControlRule);
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {

if (!"GET".equals(request.getMethod())) {
return true;
}

UrlPathHelper pathHelper = UrlPathHelper.defaultInstance;

String lookupPath = pathHelper.getLookupPathForRequest(request);
String storeLookupPath = StoreUtils.storeLookupPath(lookupPath, baseUri);

CacheControl cacheControl = cacheControlMap.get(storeLookupPath);

for (CacheControlRule rule : cacheControlRules) {

if (rule.match(storeLookupPath)) {
response.addHeader("Cache-Control", rule.getCacheControl().getHeaderValue());
}
}

return true;
}

public void setBaseUri(URI baseUri) {
this.baseUri = baseUri;
}

@Getter
static class CacheControlRule {

private static AntPathMatcher matcher = new AntPathMatcher();

private String pattern;
private CacheControl cacheControl;

public CacheControlRule(String pattern, CacheControl control) {
this.pattern = pattern;
this.cacheControl = control;
}

public boolean match(String path) {
return matcher.match(pattern, path);
}
}

public static class StoreCacheControlConfigurer {

private final StoreCacheControlInterceptor interceptor;

public StoreCacheControlConfigurer(StoreCacheControlInterceptor interceptor) {
this.interceptor = interceptor;
}

public StoreCacheControlConfigurer antMatcher(String pattern, CacheControl cacheControl) {
interceptor.addCacheControlRule(new CacheControlRule(pattern, cacheControl));
return this;
}
}
}
Loading

0 comments on commit 0428f2f

Please sign in to comment.