下面列出了怎么用org.springframework.http.codec.ResourceHttpMessageWriter的API类实例代码及写法,或者点击链接到github查看源代码。
@Override
public void afterPropertiesSet() throws Exception {
resolveResourceLocations();
if (logger.isWarnEnabled() && CollectionUtils.isEmpty(this.locations)) {
logger.warn("Locations list is empty. No resources will be served unless a " +
"custom ResourceResolver is configured as an alternative to PathResourceResolver.");
}
if (this.resourceResolvers.isEmpty()) {
this.resourceResolvers.add(new PathResourceResolver());
}
initAllowedLocations();
if (getResourceHttpMessageWriter() == null) {
this.resourceHttpMessageWriter = new ResourceHttpMessageWriter();
}
// Initialize immutable resolver and transformer chains
this.resolverChain = new DefaultResourceResolverChain(this.resourceResolvers);
this.transformerChain = new DefaultResourceTransformerChain(this.resolverChain, this.resourceTransformers);
}
private ResponseEntityResultHandler createHandler(HttpMessageWriter<?>... writers) {
List<HttpMessageWriter<?>> writerList;
if (ObjectUtils.isEmpty(writers)) {
writerList = new ArrayList<>();
writerList.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
writerList.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.textPlainOnly()));
writerList.add(new ResourceHttpMessageWriter());
writerList.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
writerList.add(new EncoderHttpMessageWriter<>(new Jackson2JsonEncoder()));
writerList.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.allMimeTypes()));
}
else {
writerList = Arrays.asList(writers);
}
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
return new ResponseEntityResultHandler(writerList, resolver);
}
private AbstractMessageWriterResultHandler initResultHandler(HttpMessageWriter<?>... writers) {
List<HttpMessageWriter<?>> writerList;
if (ObjectUtils.isEmpty(writers)) {
writerList = new ArrayList<>();
writerList.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
writerList.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.allMimeTypes()));
writerList.add(new ResourceHttpMessageWriter());
writerList.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
writerList.add(new EncoderHttpMessageWriter<>(new Jackson2JsonEncoder()));
}
else {
writerList = Arrays.asList(writers);
}
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
return new AbstractMessageWriterResultHandler(writerList, resolver) {};
}
/**
* Return writers that support specific types.
* @param forMultipart whether to returns writers for general use ("false"),
* or for multipart requests only ("true"). Generally the two sets are the
* same except for the multipart writer itself.
*/
@SuppressWarnings("unchecked")
final List<HttpMessageWriter<?>> getTypedWriters(boolean forMultipart) {
if (!this.registerDefaults) {
return Collections.emptyList();
}
List<HttpMessageWriter<?>> writers = new ArrayList<>();
writers.add(new EncoderHttpMessageWriter<>(new ByteArrayEncoder()));
writers.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
writers.add(new EncoderHttpMessageWriter<>(new DataBufferEncoder()));
writers.add(new ResourceHttpMessageWriter());
writers.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.textPlainOnly()));
// No client or server specific multipart writers currently..
if (!forMultipart) {
extendTypedWriters(writers);
}
if (protobufPresent) {
Encoder<?> encoder = this.protobufEncoder != null ? this.protobufEncoder : new ProtobufEncoder();
writers.add(new ProtobufHttpMessageWriter((Encoder) encoder));
}
return writers;
}
@Test
public void defaultWriters() {
List<HttpMessageWriter<?>> writers = this.configurer.getWriters();
assertEquals(11, writers.size());
assertEquals(ByteArrayEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ByteBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(DataBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ResourceHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertStringEncoder(getNextEncoder(writers), true);
assertEquals(ProtobufHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertEquals(Jackson2JsonEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jackson2SmileEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jaxb2XmlEncoder.class, getNextEncoder(writers).getClass());
assertSseWriter(writers);
assertStringEncoder(getNextEncoder(writers), false);
}
@Test
public void defaultWriters() {
List<HttpMessageWriter<?>> writers = this.configurer.getWriters();
assertEquals(11, writers.size());
assertEquals(ByteArrayEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ByteBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(DataBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ResourceHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertStringEncoder(getNextEncoder(writers), true);
assertEquals(MultipartHttpMessageWriter.class, writers.get(this.index.getAndIncrement()).getClass());
assertEquals(ProtobufHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertEquals(Jackson2JsonEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jackson2SmileEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jaxb2XmlEncoder.class, getNextEncoder(writers).getClass());
assertStringEncoder(getNextEncoder(writers), false);
}
@Override
public void afterPropertiesSet() throws Exception {
resolveResourceLocations();
if (logger.isWarnEnabled() && CollectionUtils.isEmpty(this.locations)) {
logger.warn("Locations list is empty. No resources will be served unless a " +
"custom ResourceResolver is configured as an alternative to PathResourceResolver.");
}
if (this.resourceResolvers.isEmpty()) {
this.resourceResolvers.add(new PathResourceResolver());
}
initAllowedLocations();
if (getResourceHttpMessageWriter() == null) {
this.resourceHttpMessageWriter = new ResourceHttpMessageWriter();
}
// Initialize immutable resolver and transformer chains
this.resolverChain = new DefaultResourceResolverChain(this.resourceResolvers);
this.transformerChain = new DefaultResourceTransformerChain(this.resolverChain, this.resourceTransformers);
}
private ResponseEntityResultHandler createHandler(HttpMessageWriter<?>... writers) {
List<HttpMessageWriter<?>> writerList;
if (ObjectUtils.isEmpty(writers)) {
writerList = new ArrayList<>();
writerList.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
writerList.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.textPlainOnly()));
writerList.add(new ResourceHttpMessageWriter());
writerList.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
writerList.add(new EncoderHttpMessageWriter<>(new Jackson2JsonEncoder()));
writerList.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.allMimeTypes()));
}
else {
writerList = Arrays.asList(writers);
}
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
return new ResponseEntityResultHandler(writerList, resolver);
}
private AbstractMessageWriterResultHandler initResultHandler(HttpMessageWriter<?>... writers) {
List<HttpMessageWriter<?>> writerList;
if (ObjectUtils.isEmpty(writers)) {
writerList = new ArrayList<>();
writerList.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
writerList.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.allMimeTypes()));
writerList.add(new ResourceHttpMessageWriter());
writerList.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
writerList.add(new EncoderHttpMessageWriter<>(new Jackson2JsonEncoder()));
}
else {
writerList = Arrays.asList(writers);
}
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
return new AbstractMessageWriterResultHandler(writerList, resolver) {};
}
/**
* Return writers that support specific types.
* @param forMultipart whether to returns writers for general use ("false"),
* or for multipart requests only ("true"). Generally the two sets are the
* same except for the multipart writer itself.
*/
@SuppressWarnings("unchecked")
final List<HttpMessageWriter<?>> getTypedWriters(boolean forMultipart) {
if (!this.registerDefaults) {
return Collections.emptyList();
}
List<HttpMessageWriter<?>> writers = new ArrayList<>();
writers.add(new EncoderHttpMessageWriter<>(new ByteArrayEncoder()));
writers.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
writers.add(new EncoderHttpMessageWriter<>(new DataBufferEncoder()));
writers.add(new ResourceHttpMessageWriter());
writers.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.textPlainOnly()));
// No client or server specific multipart writers currently..
if (!forMultipart) {
extendTypedWriters(writers);
}
if (protobufPresent) {
Encoder<?> encoder = this.protobufEncoder != null ? this.protobufEncoder : new ProtobufEncoder();
writers.add(new ProtobufHttpMessageWriter((Encoder) encoder));
}
return writers;
}
@Test
public void defaultWriters() {
List<HttpMessageWriter<?>> writers = this.configurer.getWriters();
assertEquals(11, writers.size());
assertEquals(ByteArrayEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ByteBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(DataBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ResourceHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertStringEncoder(getNextEncoder(writers), true);
assertEquals(ProtobufHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertEquals(Jackson2JsonEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jackson2SmileEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jaxb2XmlEncoder.class, getNextEncoder(writers).getClass());
assertSseWriter(writers);
assertStringEncoder(getNextEncoder(writers), false);
}
@Test
public void defaultWriters() {
List<HttpMessageWriter<?>> writers = this.configurer.getWriters();
assertEquals(11, writers.size());
assertEquals(ByteArrayEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ByteBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(DataBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ResourceHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertStringEncoder(getNextEncoder(writers), true);
assertEquals(MultipartHttpMessageWriter.class, writers.get(this.index.getAndIncrement()).getClass());
assertEquals(ProtobufHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertEquals(Jackson2JsonEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jackson2SmileEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jaxb2XmlEncoder.class, getNextEncoder(writers).getClass());
assertStringEncoder(getNextEncoder(writers), false);
}
@Before
public void createContext() {
final List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
messageWriters.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
messageWriters.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.textPlainOnly()));
messageWriters.add(new ResourceHttpMessageWriter());
messageWriters.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
Jackson2JsonEncoder jsonEncoder = new Jackson2JsonEncoder();
messageWriters.add(new EncoderHttpMessageWriter<>(jsonEncoder));
messageWriters.add(new ServerSentEventHttpMessageWriter(jsonEncoder));
messageWriters.add(new FormHttpMessageWriter());
messageWriters.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.allMimeTypes()));
messageWriters.add(new MultipartHttpMessageWriter(messageWriters));
this.context = new BodyInserter.Context() {
@Override
public List<HttpMessageWriter<?>> messageWriters() {
return messageWriters;
}
@Override
public Optional<ServerHttpRequest> serverRequest() {
return Optional.empty();
}
@Override
public Map<String, Object> hints() {
return hints;
}
};
this.hints = new HashMap<>();
}
@Before
public void setup() throws Exception {
List<HttpMessageWriter<?>> writerList = new ArrayList<>(5);
writerList.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
writerList.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.allMimeTypes()));
writerList.add(new ResourceHttpMessageWriter());
writerList.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
writerList.add(new EncoderHttpMessageWriter<>(new Jackson2JsonEncoder()));
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
this.resultHandler = new ResponseBodyResultHandler(writerList, resolver);
}
/**
* Constructor with a default list of part writers (String and Resource).
*/
public MultipartHttpMessageWriter() {
this(Arrays.asList(
new EncoderHttpMessageWriter<>(CharSequenceEncoder.textPlainOnly()),
new ResourceHttpMessageWriter()
));
}
@Test
public void defaultWriters() {
List<HttpMessageWriter<?>> writers = this.configurer.getWriters();
assertEquals(10, writers.size());
assertEquals(ByteArrayEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ByteBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(DataBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ResourceHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertStringEncoder(getNextEncoder(writers), true);
assertEquals(ProtobufHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertEquals(Jackson2JsonEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jackson2SmileEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jaxb2XmlEncoder.class, getNextEncoder(writers).getClass());
assertStringEncoder(getNextEncoder(writers), false);
}
@Test
public void defaultAndCustomWriters() {
Encoder<?> customEncoder1 = mock(Encoder.class);
Encoder<?> customEncoder2 = mock(Encoder.class);
given(customEncoder1.canEncode(ResolvableType.forClass(Object.class), null)).willReturn(false);
given(customEncoder2.canEncode(ResolvableType.forClass(Object.class), null)).willReturn(true);
HttpMessageWriter<?> customWriter1 = mock(HttpMessageWriter.class);
HttpMessageWriter<?> customWriter2 = mock(HttpMessageWriter.class);
given(customWriter1.canWrite(ResolvableType.forClass(Object.class), null)).willReturn(false);
given(customWriter2.canWrite(ResolvableType.forClass(Object.class), null)).willReturn(true);
this.configurer.customCodecs().encoder(customEncoder1);
this.configurer.customCodecs().encoder(customEncoder2);
this.configurer.customCodecs().writer(customWriter1);
this.configurer.customCodecs().writer(customWriter2);
List<HttpMessageWriter<?>> writers = this.configurer.getWriters();
assertEquals(14, writers.size());
assertSame(customEncoder1, getNextEncoder(writers));
assertSame(customWriter1, writers.get(this.index.getAndIncrement()));
assertEquals(ByteArrayEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ByteBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(DataBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ResourceHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertEquals(CharSequenceEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ProtobufHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertSame(customEncoder2, getNextEncoder(writers));
assertSame(customWriter2, writers.get(this.index.getAndIncrement()));
assertEquals(Jackson2JsonEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jackson2SmileEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jaxb2XmlEncoder.class, getNextEncoder(writers).getClass());
assertEquals(CharSequenceEncoder.class, getNextEncoder(writers).getClass());
}
@Before
public void createContext() {
final List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
messageWriters.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
messageWriters.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.textPlainOnly()));
messageWriters.add(new ResourceHttpMessageWriter());
messageWriters.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
Jackson2JsonEncoder jsonEncoder = new Jackson2JsonEncoder();
messageWriters.add(new EncoderHttpMessageWriter<>(jsonEncoder));
messageWriters.add(new ServerSentEventHttpMessageWriter(jsonEncoder));
messageWriters.add(new FormHttpMessageWriter());
messageWriters.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.allMimeTypes()));
messageWriters.add(new MultipartHttpMessageWriter(messageWriters));
this.context = new BodyInserter.Context() {
@Override
public List<HttpMessageWriter<?>> messageWriters() {
return messageWriters;
}
@Override
public Optional<ServerHttpRequest> serverRequest() {
return Optional.empty();
}
@Override
public Map<String, Object> hints() {
return hints;
}
};
this.hints = new HashMap<>();
}
@Before
public void setup() throws Exception {
List<HttpMessageWriter<?>> writerList = new ArrayList<>(5);
writerList.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
writerList.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.allMimeTypes()));
writerList.add(new ResourceHttpMessageWriter());
writerList.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
writerList.add(new EncoderHttpMessageWriter<>(new Jackson2JsonEncoder()));
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
this.resultHandler = new ResponseBodyResultHandler(writerList, resolver);
}
/**
* Constructor with a default list of part writers (String and Resource).
*/
public MultipartHttpMessageWriter() {
this(Arrays.asList(
new EncoderHttpMessageWriter<>(CharSequenceEncoder.textPlainOnly()),
new ResourceHttpMessageWriter()
));
}
@Test
public void defaultWriters() {
List<HttpMessageWriter<?>> writers = this.configurer.getWriters();
assertEquals(10, writers.size());
assertEquals(ByteArrayEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ByteBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(DataBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ResourceHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertStringEncoder(getNextEncoder(writers), true);
assertEquals(ProtobufHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertEquals(Jackson2JsonEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jackson2SmileEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jaxb2XmlEncoder.class, getNextEncoder(writers).getClass());
assertStringEncoder(getNextEncoder(writers), false);
}
@Test
public void defaultAndCustomWriters() {
Encoder<?> customEncoder1 = mock(Encoder.class);
Encoder<?> customEncoder2 = mock(Encoder.class);
when(customEncoder1.canEncode(ResolvableType.forClass(Object.class), null)).thenReturn(false);
when(customEncoder2.canEncode(ResolvableType.forClass(Object.class), null)).thenReturn(true);
HttpMessageWriter<?> customWriter1 = mock(HttpMessageWriter.class);
HttpMessageWriter<?> customWriter2 = mock(HttpMessageWriter.class);
when(customWriter1.canWrite(ResolvableType.forClass(Object.class), null)).thenReturn(false);
when(customWriter2.canWrite(ResolvableType.forClass(Object.class), null)).thenReturn(true);
this.configurer.customCodecs().encoder(customEncoder1);
this.configurer.customCodecs().encoder(customEncoder2);
this.configurer.customCodecs().writer(customWriter1);
this.configurer.customCodecs().writer(customWriter2);
List<HttpMessageWriter<?>> writers = this.configurer.getWriters();
assertEquals(14, writers.size());
assertEquals(ByteArrayEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ByteBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(DataBufferEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ResourceHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertEquals(CharSequenceEncoder.class, getNextEncoder(writers).getClass());
assertEquals(ProtobufHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
assertSame(customEncoder1, getNextEncoder(writers));
assertSame(customWriter1, writers.get(this.index.getAndIncrement()));
assertEquals(Jackson2JsonEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jackson2SmileEncoder.class, getNextEncoder(writers).getClass());
assertEquals(Jaxb2XmlEncoder.class, getNextEncoder(writers).getClass());
assertSame(customEncoder2, getNextEncoder(writers));
assertSame(customWriter2, writers.get(this.index.getAndIncrement()));
assertEquals(CharSequenceEncoder.class, getNextEncoder(writers).getClass());
}
/**
* Return the configured resource message writer.
*/
@Nullable
public ResourceHttpMessageWriter getResourceHttpMessageWriter() {
return this.resourceHttpMessageWriter;
}
/**
* Processes a resource request.
* <p>Checks for the existence of the requested resource in the configured list of locations.
* If the resource does not exist, a {@code 404} response will be returned to the client.
* If the resource exists, the request will be checked for the presence of the
* {@code Last-Modified} header, and its value will be compared against the last-modified
* timestamp of the given resource, returning a {@code 304} status code if the
* {@code Last-Modified} value is greater. If the resource is newer than the
* {@code Last-Modified} value, or the header is not present, the content resource
* of the resource will be written to the response with caching headers
* set to expire one year in the future.
*/
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
return getResource(exchange)
.switchIfEmpty(Mono.defer(() -> {
logger.debug(exchange.getLogPrefix() + "Resource not found");
return Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND));
}))
.flatMap(resource -> {
try {
if (HttpMethod.OPTIONS.matches(exchange.getRequest().getMethodValue())) {
exchange.getResponse().getHeaders().add("Allow", "GET,HEAD,OPTIONS");
return Mono.empty();
}
// Supported methods and required session
HttpMethod httpMethod = exchange.getRequest().getMethod();
if (!SUPPORTED_METHODS.contains(httpMethod)) {
return Mono.error(new MethodNotAllowedException(
exchange.getRequest().getMethodValue(), SUPPORTED_METHODS));
}
// Header phase
if (exchange.checkNotModified(Instant.ofEpochMilli(resource.lastModified()))) {
logger.trace(exchange.getLogPrefix() + "Resource not modified");
return Mono.empty();
}
// Apply cache settings, if any
CacheControl cacheControl = getCacheControl();
if (cacheControl != null) {
exchange.getResponse().getHeaders().setCacheControl(cacheControl);
}
// Check the media type for the resource
MediaType mediaType = MediaTypeFactory.getMediaType(resource).orElse(null);
// Content phase
if (HttpMethod.HEAD.matches(exchange.getRequest().getMethodValue())) {
setHeaders(exchange, resource, mediaType);
exchange.getResponse().getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
return Mono.empty();
}
setHeaders(exchange, resource, mediaType);
ResourceHttpMessageWriter writer = getResourceHttpMessageWriter();
Assert.state(writer != null, "No ResourceHttpMessageWriter");
return writer.write(Mono.just(resource),
null, ResolvableType.forClass(Resource.class), mediaType,
exchange.getRequest(), exchange.getResponse(),
Hints.from(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix()));
}
catch (IOException ex) {
return Mono.error(ex);
}
});
}
@Test
public void ofResourceRange() throws IOException {
final int rangeStart = 10;
Resource body = new ClassPathResource("response.txt", getClass());
BodyInserter<Resource, ReactiveHttpOutputMessage> inserter = BodyInserters.fromResource(body);
MockServerHttpRequest request = MockServerHttpRequest.get("/foo")
.range(HttpRange.createByteRange(rangeStart))
.build();
MockServerHttpResponse response = new MockServerHttpResponse();
Mono<Void> result = inserter.insert(response, new BodyInserter.Context() {
@Override
public List<HttpMessageWriter<?>> messageWriters() {
return Collections.singletonList(new ResourceHttpMessageWriter());
}
@Override
public Optional<ServerHttpRequest> serverRequest() {
return Optional.of(request);
}
@Override
public Map<String, Object> hints() {
return hints;
}
});
StepVerifier.create(result).expectComplete().verify();
byte[] allBytes = Files.readAllBytes(body.getFile().toPath());
byte[] expectedBytes = new byte[allBytes.length - rangeStart];
System.arraycopy(allBytes, rangeStart, expectedBytes, 0, expectedBytes.length);
StepVerifier.create(response.getBody())
.consumeNextWith(dataBuffer -> {
byte[] resultBytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(resultBytes);
DataBufferUtils.release(dataBuffer);
assertArrayEquals(expectedBytes, resultBytes);
})
.expectComplete()
.verify();
}
/**
* Return the configured resource message writer.
*/
@Nullable
public ResourceHttpMessageWriter getResourceHttpMessageWriter() {
return this.resourceHttpMessageWriter;
}
/**
* Processes a resource request.
* <p>Checks for the existence of the requested resource in the configured list of locations.
* If the resource does not exist, a {@code 404} response will be returned to the client.
* If the resource exists, the request will be checked for the presence of the
* {@code Last-Modified} header, and its value will be compared against the last-modified
* timestamp of the given resource, returning a {@code 304} status code if the
* {@code Last-Modified} value is greater. If the resource is newer than the
* {@code Last-Modified} value, or the header is not present, the content resource
* of the resource will be written to the response with caching headers
* set to expire one year in the future.
*/
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
return getResource(exchange)
.switchIfEmpty(Mono.defer(() -> {
logger.debug(exchange.getLogPrefix() + "Resource not found");
return Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND));
}))
.flatMap(resource -> {
try {
if (HttpMethod.OPTIONS.matches(exchange.getRequest().getMethodValue())) {
exchange.getResponse().getHeaders().add("Allow", "GET,HEAD,OPTIONS");
return Mono.empty();
}
// Supported methods and required session
HttpMethod httpMethod = exchange.getRequest().getMethod();
if (!SUPPORTED_METHODS.contains(httpMethod)) {
return Mono.error(new MethodNotAllowedException(
exchange.getRequest().getMethodValue(), SUPPORTED_METHODS));
}
// Header phase
if (exchange.checkNotModified(Instant.ofEpochMilli(resource.lastModified()))) {
logger.trace(exchange.getLogPrefix() + "Resource not modified");
return Mono.empty();
}
// Apply cache settings, if any
CacheControl cacheControl = getCacheControl();
if (cacheControl != null) {
exchange.getResponse().getHeaders().setCacheControl(cacheControl);
}
// Check the media type for the resource
MediaType mediaType = MediaTypeFactory.getMediaType(resource).orElse(null);
// Content phase
if (HttpMethod.HEAD.matches(exchange.getRequest().getMethodValue())) {
setHeaders(exchange, resource, mediaType);
exchange.getResponse().getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
return Mono.empty();
}
setHeaders(exchange, resource, mediaType);
ResourceHttpMessageWriter writer = getResourceHttpMessageWriter();
Assert.state(writer != null, "No ResourceHttpMessageWriter");
return writer.write(Mono.just(resource),
null, ResolvableType.forClass(Resource.class), mediaType,
exchange.getRequest(), exchange.getResponse(),
Hints.from(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix()));
}
catch (IOException ex) {
return Mono.error(ex);
}
});
}
@Test
public void ofResourceRange() throws IOException {
final int rangeStart = 10;
Resource body = new ClassPathResource("response.txt", getClass());
BodyInserter<Resource, ReactiveHttpOutputMessage> inserter = BodyInserters.fromResource(body);
MockServerHttpRequest request = MockServerHttpRequest.get("/foo")
.range(HttpRange.createByteRange(rangeStart))
.build();
MockServerHttpResponse response = new MockServerHttpResponse();
Mono<Void> result = inserter.insert(response, new BodyInserter.Context() {
@Override
public List<HttpMessageWriter<?>> messageWriters() {
return Collections.singletonList(new ResourceHttpMessageWriter());
}
@Override
public Optional<ServerHttpRequest> serverRequest() {
return Optional.of(request);
}
@Override
public Map<String, Object> hints() {
return hints;
}
});
StepVerifier.create(result).expectComplete().verify();
byte[] allBytes = Files.readAllBytes(body.getFile().toPath());
byte[] expectedBytes = new byte[allBytes.length - rangeStart];
System.arraycopy(allBytes, rangeStart, expectedBytes, 0, expectedBytes.length);
StepVerifier.create(response.getBody())
.consumeNextWith(dataBuffer -> {
byte[] resultBytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(resultBytes);
DataBufferUtils.release(dataBuffer);
assertArrayEquals(expectedBytes, resultBytes);
})
.expectComplete()
.verify();
}
@Override
protected void register(GenericApplicationContext context, CodecConfigurer configurer) {
configurer.customCodecs().writer(new ResourceHttpMessageWriter());
configurer.customCodecs().decoder(new ResourceDecoder());
}
/**
* Configure the {@link ResourceHttpMessageWriter} to use.
* <p>By default a {@link ResourceHttpMessageWriter} will be configured.
*/
public void setResourceHttpMessageWriter(@Nullable ResourceHttpMessageWriter httpMessageWriter) {
this.resourceHttpMessageWriter = httpMessageWriter;
}