Browse Source
This commit moves the existing Spring MVC Mustache support to its own `servlet` package and adds a new one under `reactive` for the WebFlux web applications. New `MustacheView` and `MustacheViewResolver` types resolve and render Mustache views for WebFlux applications. Since this templating engine is now supported by two flavors of Spring web apps, the `spring-boot-starter-mustache` does not depend anymore on the `spring-boot-starter-web` one: it's up to the developer to add the relevant starter `web` or `webflux` to their application. Fixes gh-8648pull/9002/head
19 changed files with 618 additions and 139 deletions
@ -0,0 +1,118 @@
@@ -0,0 +1,118 @@
|
||||
/* |
||||
* Copyright 2012-2017 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.autoconfigure.mustache.reactive; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.InputStreamReader; |
||||
import java.io.OutputStreamWriter; |
||||
import java.io.Reader; |
||||
import java.io.Writer; |
||||
import java.nio.charset.Charset; |
||||
import java.util.Locale; |
||||
import java.util.Map; |
||||
import java.util.Optional; |
||||
|
||||
import com.samskivert.mustache.Mustache; |
||||
import com.samskivert.mustache.Template; |
||||
import reactor.core.publisher.Flux; |
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.core.io.buffer.DataBuffer; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.web.reactive.result.view.AbstractUrlBasedView; |
||||
import org.springframework.web.reactive.result.view.View; |
||||
import org.springframework.web.server.ServerWebExchange; |
||||
|
||||
/** |
||||
* Spring WebFlux {@link View} using the Mustache template engine. |
||||
* |
||||
* @author Brian Clozel |
||||
* @since 2.0.0 |
||||
*/ |
||||
public class MustacheView extends AbstractUrlBasedView { |
||||
|
||||
private Mustache.Compiler compiler; |
||||
|
||||
private String charset; |
||||
|
||||
/** |
||||
* Set the JMustache compiler to be used by this view. |
||||
* <p>Typically this property is not set directly. Instead a single |
||||
* {@link Mustache.Compiler} is expected in the Spring application context |
||||
* which is used to compile Mustache templates. |
||||
* @param compiler the Mustache compiler |
||||
*/ |
||||
public void setCompiler(Mustache.Compiler compiler) { |
||||
this.compiler = compiler; |
||||
} |
||||
|
||||
/** |
||||
* Set the charset used for reading Mustache template files. |
||||
* @param charset the charset to use for reading template files |
||||
*/ |
||||
public void setCharset(String charset) { |
||||
this.charset = charset; |
||||
} |
||||
|
||||
@Override |
||||
public boolean checkResourceExists(Locale locale) throws Exception { |
||||
return resolveResource() != null; |
||||
} |
||||
|
||||
private Resource resolveResource() { |
||||
Resource resource = getApplicationContext().getResource(getUrl()); |
||||
if (resource == null || !resource.exists()) { |
||||
return null; |
||||
} |
||||
return resource; |
||||
} |
||||
|
||||
@Override |
||||
protected Mono<Void> renderInternal(Map<String, Object> model, |
||||
MediaType contentType, ServerWebExchange exchange) { |
||||
Resource resource = resolveResource(); |
||||
if (resource == null) { |
||||
return Mono.error(new IllegalStateException("Could not find Mustache template with URL [" |
||||
+ getUrl() + "]")); |
||||
} |
||||
DataBuffer dataBuffer = exchange.getResponse().bufferFactory().allocateBuffer(); |
||||
try (Reader reader = getReader(resource)) { |
||||
Template template = this.compiler.compile(reader); |
||||
Charset charset = getCharset(contentType).orElse(getDefaultCharset()); |
||||
try (Writer writer = new OutputStreamWriter(dataBuffer.asOutputStream(), charset)) { |
||||
template.execute(model, writer); |
||||
writer.flush(); |
||||
} |
||||
} |
||||
catch (Throwable exc) { |
||||
return Mono.error(exc); |
||||
} |
||||
return exchange.getResponse().writeWith(Flux.just(dataBuffer)); |
||||
} |
||||
|
||||
private Reader getReader(Resource resource) throws IOException { |
||||
if (this.charset != null) { |
||||
return new InputStreamReader(resource.getInputStream(), this.charset); |
||||
} |
||||
return new InputStreamReader(resource.getInputStream()); |
||||
} |
||||
|
||||
private Optional<Charset> getCharset(MediaType mediaType) { |
||||
return (mediaType != null ? Optional.ofNullable(mediaType.getCharset()) : Optional.empty()); |
||||
} |
||||
} |
||||
@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
|
||||
/* |
||||
* Copyright 2012-2017 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.autoconfigure.mustache.reactive; |
||||
|
||||
import com.samskivert.mustache.Mustache; |
||||
|
||||
import org.springframework.web.reactive.result.view.AbstractUrlBasedView; |
||||
import org.springframework.web.reactive.result.view.UrlBasedViewResolver; |
||||
import org.springframework.web.reactive.result.view.ViewResolver; |
||||
|
||||
/** |
||||
* Spring WebFlux {@link ViewResolver} for Mustache. |
||||
* |
||||
* @author Brian Clozel |
||||
* @since 2.0.0 |
||||
*/ |
||||
public class MustacheViewResolver extends UrlBasedViewResolver { |
||||
|
||||
private final Mustache.Compiler compiler; |
||||
|
||||
private String charset; |
||||
|
||||
/** |
||||
* Create a {@code MustacheViewResolver} backed by a default |
||||
* instance of a {@link Mustache.Compiler}. |
||||
*/ |
||||
public MustacheViewResolver() { |
||||
this.compiler = Mustache.compiler(); |
||||
setViewClass(requiredViewClass()); |
||||
} |
||||
|
||||
/** |
||||
* Create a {@code MustacheViewResolver} backed by a custom |
||||
* instance of a {@link Mustache.Compiler}. |
||||
* @param compiler the Mustache compiler used to compile templates |
||||
*/ |
||||
public MustacheViewResolver(Mustache.Compiler compiler) { |
||||
this.compiler = compiler; |
||||
setViewClass(requiredViewClass()); |
||||
} |
||||
|
||||
/** |
||||
* Set the charset. |
||||
* @param charset the charset |
||||
*/ |
||||
public void setCharset(String charset) { |
||||
this.charset = charset; |
||||
} |
||||
|
||||
@Override |
||||
protected Class<?> requiredViewClass() { |
||||
return MustacheView.class; |
||||
} |
||||
|
||||
@Override |
||||
protected AbstractUrlBasedView createUrlBasedView(String viewName) { |
||||
MustacheView view = (MustacheView) super.createUrlBasedView(viewName); |
||||
view.setCompiler(this.compiler); |
||||
view.setCharset(this.charset); |
||||
return view; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
/* |
||||
* Copyright 2012-2017 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* Auto-configuration for Mustache with Spring WebFlux. |
||||
*/ |
||||
package org.springframework.boot.autoconfigure.mustache.reactive; |
||||
@ -1,122 +0,0 @@
@@ -1,122 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2017 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.autoconfigure.mustache; |
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
import java.util.Date; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.boot.SpringApplication; |
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration; |
||||
import org.springframework.boot.test.context.SpringBootTest; |
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; |
||||
import org.springframework.boot.test.web.client.TestRestTemplate; |
||||
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.Import; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.annotation.DirtiesContext; |
||||
import org.springframework.test.context.junit4.SpringRunner; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Integration tests for {@link MustacheAutoConfiguration}. |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
@RunWith(SpringRunner.class) |
||||
@DirtiesContext |
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { |
||||
"spring.mustache.prefix:classpath:/mustache-templates/" }) |
||||
public class MustacheAutoConfigurationIntegrationTests { |
||||
|
||||
@Autowired |
||||
private ServletWebServerApplicationContext context; |
||||
|
||||
private int port; |
||||
|
||||
@Before |
||||
public void init() { |
||||
this.port = this.context.getWebServer().getPort(); |
||||
} |
||||
|
||||
@Test |
||||
public void testHomePage() throws Exception { |
||||
String body = new TestRestTemplate().getForObject("http://localhost:" + this.port, |
||||
String.class); |
||||
assertThat(body.contains("Hello World")).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void testPartialPage() throws Exception { |
||||
String body = new TestRestTemplate() |
||||
.getForObject("http://localhost:" + this.port + "/partial", String.class); |
||||
assertThat(body.contains("Hello World")).isTrue(); |
||||
} |
||||
|
||||
@Configuration |
||||
@MinimalWebConfiguration |
||||
@Controller |
||||
public static class Application { |
||||
|
||||
@RequestMapping("/") |
||||
public String home(Map<String, Object> model) { |
||||
model.put("time", new Date()); |
||||
model.put("message", "Hello World"); |
||||
model.put("title", "Hello App"); |
||||
return "home"; |
||||
} |
||||
|
||||
@RequestMapping("/partial") |
||||
public String layout(Map<String, Object> model) { |
||||
model.put("time", new Date()); |
||||
model.put("message", "Hello World"); |
||||
model.put("title", "Hello App"); |
||||
return "partial"; |
||||
} |
||||
|
||||
public static void main(String[] args) { |
||||
SpringApplication.run(Application.class, args); |
||||
} |
||||
|
||||
} |
||||
|
||||
@Target(ElementType.TYPE) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
@Import({ MustacheAutoConfiguration.class, |
||||
ServletWebServerFactoryAutoConfiguration.class, |
||||
DispatcherServletAutoConfiguration.class, |
||||
PropertyPlaceholderAutoConfiguration.class }) |
||||
protected @interface MinimalWebConfiguration { |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,121 @@
@@ -0,0 +1,121 @@
|
||||
/* |
||||
* Copyright 2012-2017 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.autoconfigure.mustache; |
||||
|
||||
import com.samskivert.mustache.Mustache; |
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.boot.autoconfigure.mustache.servlet.MustacheViewResolver; |
||||
import org.springframework.boot.test.util.EnvironmentTestUtils; |
||||
import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.Import; |
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link MustacheAutoConfiguration}. |
||||
* |
||||
* @author Brian Clozel |
||||
*/ |
||||
public class MustacheAutoConfigurationTests { |
||||
|
||||
private AnnotationConfigWebApplicationContext webContext; |
||||
|
||||
private GenericReactiveWebApplicationContext reactiveWebContext; |
||||
|
||||
@Test |
||||
public void registerBeansForServletApp() { |
||||
loadWithServlet(null); |
||||
assertThat(this.webContext.getBeansOfType(Mustache.Compiler.class)).hasSize(1); |
||||
assertThat(this.webContext.getBeansOfType(MustacheResourceTemplateLoader.class)).hasSize(1); |
||||
assertThat(this.webContext.getBeansOfType(MustacheViewResolver.class)).hasSize(1); |
||||
} |
||||
|
||||
@Test |
||||
public void registerCompilerForServletApp() { |
||||
loadWithServlet(CustomCompilerConfiguration.class); |
||||
assertThat(this.webContext.getBeansOfType(MustacheResourceTemplateLoader.class)).hasSize(1); |
||||
assertThat(this.webContext.getBeansOfType(MustacheViewResolver.class)).hasSize(1); |
||||
assertThat(this.webContext.getBeansOfType(Mustache.Compiler.class)).hasSize(1); |
||||
assertThat(this.webContext.getBean(Mustache.Compiler.class).standardsMode).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void registerBeansForReactiveApp() { |
||||
loadWithReactive(null); |
||||
assertThat(this.reactiveWebContext.getBeansOfType(Mustache.Compiler.class)).hasSize(1); |
||||
assertThat(this.reactiveWebContext.getBeansOfType(MustacheResourceTemplateLoader.class)).hasSize(1); |
||||
assertThat(this.reactiveWebContext.getBeansOfType(MustacheViewResolver.class)).isEmpty(); |
||||
assertThat(this.reactiveWebContext |
||||
.getBeansOfType(org.springframework.boot.autoconfigure.mustache.reactive.MustacheViewResolver.class) |
||||
).hasSize(1); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void registerCompilerForReactiveApp() { |
||||
loadWithReactive(CustomCompilerConfiguration.class); |
||||
assertThat(this.reactiveWebContext.getBeansOfType(Mustache.Compiler.class)).hasSize(1); |
||||
assertThat(this.reactiveWebContext.getBeansOfType(MustacheResourceTemplateLoader.class)).hasSize(1); |
||||
assertThat(this.reactiveWebContext.getBeansOfType(MustacheViewResolver.class)).isEmpty(); |
||||
assertThat(this.reactiveWebContext |
||||
.getBeansOfType(org.springframework.boot.autoconfigure.mustache.reactive.MustacheViewResolver.class) |
||||
).hasSize(1); |
||||
assertThat(this.reactiveWebContext.getBean(Mustache.Compiler.class).standardsMode).isTrue(); |
||||
} |
||||
|
||||
private void loadWithServlet(Class<?> config) { |
||||
this.webContext = new AnnotationConfigWebApplicationContext(); |
||||
EnvironmentTestUtils.addEnvironment(this.webContext, |
||||
"spring.mustache.prefix=classpath:/mustache-templates/"); |
||||
if (config != null) { |
||||
this.webContext.register(config); |
||||
} |
||||
this.webContext.register(BaseConfiguration.class); |
||||
this.webContext.refresh(); |
||||
} |
||||
|
||||
private void loadWithReactive(Class<?> config) { |
||||
this.reactiveWebContext = new GenericReactiveWebApplicationContext(); |
||||
EnvironmentTestUtils.addEnvironment(this.reactiveWebContext, |
||||
"spring.mustache.prefix=classpath:/mustache-templates/"); |
||||
if (config != null) { |
||||
this.reactiveWebContext.register(config); |
||||
} |
||||
this.reactiveWebContext.register(BaseConfiguration.class); |
||||
this.reactiveWebContext.refresh(); |
||||
} |
||||
|
||||
@Configuration |
||||
@Import({MustacheAutoConfiguration.class}) |
||||
protected static class BaseConfiguration { |
||||
|
||||
} |
||||
|
||||
@Configuration |
||||
protected static class CustomCompilerConfiguration { |
||||
|
||||
@Bean |
||||
public Mustache.Compiler compiler(Mustache.TemplateLoader mustacheTemplateLoader) { |
||||
return Mustache.compiler().standardsMode(true).withLoader(mustacheTemplateLoader); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
/* |
||||
* Copyright 2012-2017 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.autoconfigure.mustache.reactive; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.context.support.GenericApplicationContext; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link org.springframework.boot.autoconfigure.mustache.reactive.MustacheViewResolver}. |
||||
* |
||||
* @author Brian Clozel |
||||
*/ |
||||
public class MustacheViewResolverTests { |
||||
|
||||
private MustacheViewResolver resolver = new MustacheViewResolver(); |
||||
|
||||
@Before |
||||
public void init() { |
||||
GenericApplicationContext applicationContext = new GenericApplicationContext(); |
||||
applicationContext.refresh(); |
||||
this.resolver.setApplicationContext(applicationContext); |
||||
this.resolver.setPrefix("classpath:/mustache-templates/"); |
||||
this.resolver.setSuffix(".html"); |
||||
} |
||||
|
||||
@Test |
||||
public void resolveNonExistent() throws Exception { |
||||
assertThat(this.resolver.resolveViewName("bar", null).block()).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
public void resolveExisting() throws Exception { |
||||
assertThat(this.resolver.resolveViewName("foo", null).block()).isNotNull(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
/* |
||||
* Copyright 2012-2017 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.autoconfigure.mustache.reactive; |
||||
|
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.Collections; |
||||
|
||||
import com.samskivert.mustache.Mustache; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.context.support.GenericApplicationContext; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest; |
||||
import org.springframework.mock.http.server.reactive.MockServerWebExchange; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link MustacheView}. |
||||
* |
||||
* @author Brian Clozel |
||||
*/ |
||||
public class MustacheViewTests { |
||||
|
||||
private GenericApplicationContext context = new GenericApplicationContext(); |
||||
|
||||
private MockServerWebExchange exchange; |
||||
|
||||
@Before |
||||
public void init() { |
||||
this.context.refresh(); |
||||
} |
||||
|
||||
@Test |
||||
public void viewResolvesHandlebars() throws Exception { |
||||
this.exchange = MockServerHttpRequest.get("/test").toExchange(); |
||||
MustacheView view = new MustacheView(); |
||||
view.setCompiler(Mustache.compiler()); |
||||
view.setUrl("classpath:/mustache-templates/foo.html"); |
||||
view.setCharset(StandardCharsets.UTF_8.displayName()); |
||||
view.setApplicationContext(this.context); |
||||
view.render(Collections.singletonMap("World", "Spring"), MediaType.TEXT_HTML, this.exchange).block(); |
||||
assertThat(this.exchange.getResponse().getBodyAsString().block()).isEqualTo("Hello Spring"); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,119 @@
@@ -0,0 +1,119 @@
|
||||
/* |
||||
* Copyright 2012-2017 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.autoconfigure.mustache.reactive; |
||||
|
||||
import java.util.Date; |
||||
|
||||
import com.samskivert.mustache.Mustache; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.boot.SpringApplication; |
||||
import org.springframework.boot.WebApplicationType; |
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.mustache.MustacheResourceTemplateLoader; |
||||
import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration; |
||||
import org.springframework.boot.test.context.SpringBootTest; |
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.Import; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.test.context.junit4.SpringRunner; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.ui.Model; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Integration Tests for {@link MustacheAutoConfiguration}, {@link MustacheViewResolver} |
||||
* and {@link MustacheView}. |
||||
* |
||||
* @author Brian Clozel |
||||
*/ |
||||
@RunWith(SpringRunner.class) |
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, |
||||
properties = "spring.main.web-application-type=reactive") |
||||
public class MustacheWebIntegrationTests { |
||||
|
||||
@Autowired |
||||
private WebTestClient client; |
||||
|
||||
@Test |
||||
public void testHomePage() throws Exception { |
||||
String result = (String) this.client.get().uri("/").exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(String.class).returnResult().getResponseBody(); |
||||
assertThat(result).contains("Hello App").contains("Hello World"); |
||||
} |
||||
|
||||
@Test |
||||
public void testPartialPage() throws Exception { |
||||
String result = (String) this.client.get().uri("/partial").exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(String.class).returnResult().getResponseBody(); |
||||
assertThat(result).contains("Hello App").contains("Hello World"); |
||||
} |
||||
|
||||
@Configuration |
||||
@Import({ReactiveWebServerAutoConfiguration.class, |
||||
WebFluxAutoConfiguration.class, |
||||
HttpHandlerAutoConfiguration.class, |
||||
PropertyPlaceholderAutoConfiguration.class}) |
||||
@Controller |
||||
public static class Application { |
||||
|
||||
@RequestMapping("/") |
||||
public String home(Model model) { |
||||
model.addAttribute("time", new Date()); |
||||
model.addAttribute("message", "Hello World"); |
||||
model.addAttribute("title", "Hello App"); |
||||
return "home"; |
||||
} |
||||
|
||||
@RequestMapping("/partial") |
||||
public String layout(Model model) { |
||||
model.addAttribute("time", new Date()); |
||||
model.addAttribute("message", "Hello World"); |
||||
model.addAttribute("title", "Hello App"); |
||||
return "partial"; |
||||
} |
||||
|
||||
@Bean |
||||
public MustacheViewResolver viewResolver() { |
||||
Mustache.Compiler compiler = Mustache.compiler().withLoader( |
||||
new MustacheResourceTemplateLoader("classpath:/mustache-templates/", ".html")); |
||||
MustacheViewResolver resolver = new MustacheViewResolver(compiler); |
||||
resolver.setPrefix("classpath:/mustache-templates/"); |
||||
resolver.setSuffix(".html"); |
||||
return resolver; |
||||
} |
||||
|
||||
public static void main(String[] args) { |
||||
SpringApplication application = new SpringApplication(Application.class); |
||||
application.setWebApplicationType(WebApplicationType.REACTIVE); |
||||
application.run(args); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue