Disable whitelabel view if Thymeleaf error.html detected
This commit is contained in:
parent
08cf5b4139
commit
0498617411
|
|
@ -29,16 +29,23 @@ import org.springframework.boot.actuate.web.ErrorController;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||||
|
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration.DefaultTemplateResolverConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
|
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
|
||||||
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainerFactory;
|
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainerFactory;
|
||||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
|
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
|
||||||
import org.springframework.boot.context.embedded.ErrorPage;
|
import org.springframework.boot.context.embedded.ErrorPage;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.ConditionContext;
|
||||||
|
import org.springframework.context.annotation.Conditional;
|
||||||
import org.springframework.context.expression.MapAccessor;
|
import org.springframework.context.expression.MapAccessor;
|
||||||
|
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
import org.springframework.expression.Expression;
|
import org.springframework.expression.Expression;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.PropertyPlaceholderHelper;
|
import org.springframework.util.PropertyPlaceholderHelper;
|
||||||
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
|
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
|
||||||
import org.springframework.web.servlet.DispatcherServlet;
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
|
@ -79,10 +86,28 @@ public class ErrorMvcAutoConfiguration implements EmbeddedServletContainerCustom
|
||||||
|
|
||||||
@Bean(name = "error")
|
@Bean(name = "error")
|
||||||
@ConditionalOnMissingBean(name = "error")
|
@ConditionalOnMissingBean(name = "error")
|
||||||
|
@ConditionalOnExpression("${error.whitelabel.enabled:true}")
|
||||||
|
@Conditional(ErrorTemplateMissingCondition.class)
|
||||||
public View defaultErrorView() {
|
public View defaultErrorView() {
|
||||||
return this.defaultErrorView;
|
return this.defaultErrorView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ErrorTemplateMissingCondition extends SpringBootCondition {
|
||||||
|
@Override
|
||||||
|
public Outcome getMatchOutcome(ConditionContext context,
|
||||||
|
AnnotatedTypeMetadata metadata) {
|
||||||
|
if (ClassUtils.isPresent("org.thymeleaf.spring3.SpringTemplateEngine",
|
||||||
|
context.getClassLoader())) {
|
||||||
|
if (DefaultTemplateResolverConfiguration.templateExists(
|
||||||
|
context.getEnvironment(), context.getResourceLoader(), "error")) {
|
||||||
|
return Outcome.noMatch("Thymeleaf template found for error view");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// FIXME: add matcher for JSP view if Jasper detected
|
||||||
|
return Outcome.match("no error template view detected");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static class SpelView implements View {
|
private static class SpelView implements View {
|
||||||
|
|
||||||
private final String template;
|
private final String template;
|
||||||
|
|
|
||||||
|
|
@ -58,10 +58,12 @@ import org.thymeleaf.templateresolver.TemplateResolver;
|
||||||
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
|
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
|
||||||
public class ThymeleafAutoConfiguration {
|
public class ThymeleafAutoConfiguration {
|
||||||
|
|
||||||
|
public static final String DEFAULT_PREFIX = "classpath:/templates/";
|
||||||
|
public static final String DEFAULT_SUFFIX = ".html";
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnMissingBean(name = "defaultTemplateResolver")
|
@ConditionalOnMissingBean(name = "defaultTemplateResolver")
|
||||||
protected static class DefaultTemplateResolverConfiguration implements
|
public static class DefaultTemplateResolverConfiguration implements EnvironmentAware {
|
||||||
EnvironmentAware {
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||||
|
|
@ -96,9 +98,8 @@ public class ThymeleafAutoConfiguration {
|
||||||
return "SPRING";
|
return "SPRING";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
resolver.setPrefix(this.environment.getProperty("prefix",
|
resolver.setPrefix(this.environment.getProperty("prefix", DEFAULT_PREFIX));
|
||||||
"classpath:/templates/"));
|
resolver.setSuffix(this.environment.getProperty("suffix", DEFAULT_SUFFIX));
|
||||||
resolver.setSuffix(this.environment.getProperty("suffix", ".html"));
|
|
||||||
resolver.setTemplateMode(this.environment.getProperty("mode", "HTML5"));
|
resolver.setTemplateMode(this.environment.getProperty("mode", "HTML5"));
|
||||||
resolver.setCharacterEncoding(this.environment.getProperty("encoding",
|
resolver.setCharacterEncoding(this.environment.getProperty("encoding",
|
||||||
"UTF-8"));
|
"UTF-8"));
|
||||||
|
|
@ -107,6 +108,15 @@ public class ThymeleafAutoConfiguration {
|
||||||
return resolver;
|
return resolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean templateExists(Environment environment,
|
||||||
|
ResourceLoader resourceLoader, String view) {
|
||||||
|
String prefix = environment.getProperty("spring.thymeleaf.prefix",
|
||||||
|
ThymeleafAutoConfiguration.DEFAULT_PREFIX);
|
||||||
|
String suffix = environment.getProperty("spring.thymeleaf.suffix",
|
||||||
|
ThymeleafAutoConfiguration.DEFAULT_SUFFIX);
|
||||||
|
return resourceLoader.getResource(prefix + view + suffix).exists();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
|
@ -182,6 +192,7 @@ public class ThymeleafAutoConfiguration {
|
||||||
public SpringSecurityDialect securityDialect() {
|
public SpringSecurityDialect securityDialect() {
|
||||||
return new SpringSecurityDialect();
|
return new SpringSecurityDialect();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,8 @@ public class SampleActuatorUiApplicationTests {
|
||||||
.contains("<html>"));
|
.contains("<html>"));
|
||||||
assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody()
|
assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody()
|
||||||
.contains("<body>"));
|
.contains("<body>"));
|
||||||
|
assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody()
|
||||||
|
.contains("Please contact the operator with the above information"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RestTemplate getRestTemplate() {
|
private RestTemplate getRestTemplate() {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<include resource="org/springframework/boot/logging/logback/base.xml"/>
|
<include resource="org/springframework/boot/logging/logback/base.xml"/>
|
||||||
<!-- logger name="org.springframework" level="DEBUG"/ -->
|
<!-- logger name="org.springframework.boot" level="DEBUG"/-->
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,12 @@ package org.springframework.boot.sample.ops;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
@ -35,8 +37,12 @@ import org.junit.Test;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.actuate.properties.SecurityProperties;
|
import org.springframework.boot.actuate.properties.SecurityProperties;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.http.HttpEntity;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpRequest;
|
import org.springframework.http.HttpRequest;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||||
|
|
@ -134,26 +140,42 @@ public class SampleActuatorApplicationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testErrorPage() throws Exception {
|
public void testErrorPage() throws Exception {
|
||||||
ResponseEntity<String> entity = getRestTemplate().getForEntity(
|
ResponseEntity<String> entity = getRestTemplate("user", getPassword())
|
||||||
"http://localhost:8080/health", String.class);
|
.getForEntity("http://localhost:8080/foo", String.class);
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, entity.getStatusCode());
|
||||||
assertEquals("ok", entity.getBody());
|
String body = entity.getBody();
|
||||||
|
assertNotNull(body);
|
||||||
|
assertTrue("Wrong body: " + body,
|
||||||
|
body.contains("\"error\":"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHtmlErrorPage() throws Exception {
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
|
||||||
|
HttpEntity<?> request = new HttpEntity<Void>(headers);
|
||||||
|
ResponseEntity<String> entity = getRestTemplate("user", getPassword()).exchange(
|
||||||
|
"http://localhost:8080/foo", HttpMethod.GET, request, String.class);
|
||||||
|
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, entity.getStatusCode());
|
||||||
|
String body = entity.getBody();
|
||||||
|
assertNotNull("Body was null", body);
|
||||||
|
assertTrue("Wrong body: " + body,
|
||||||
|
body.contains("This application has no explicit mapping for /error"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTrace() throws Exception {
|
public void testTrace() throws Exception {
|
||||||
getRestTemplate().getForEntity(
|
getRestTemplate().getForEntity("http://localhost:8080/health", String.class);
|
||||||
"http://localhost:8080/health", String.class);
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
ResponseEntity<List> entity = getRestTemplate("user", getPassword()).getForEntity(
|
ResponseEntity<List> entity = getRestTemplate("user", getPassword())
|
||||||
"http://localhost:8080/trace", List.class);
|
.getForEntity("http://localhost:8080/trace", List.class);
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
List<Map<String,Object>> list = (List<Map<String, Object>>) entity.getBody();
|
List<Map<String, Object>> list = (List<Map<String, Object>>) entity.getBody();
|
||||||
Map<String, Object> trace = list.get(list.size()-1);
|
Map<String, Object> trace = list.get(list.size() - 1);
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Map<String, Object> map = (Map<String, Object>) ((Map<String, Object>) ((Map<String, Object>)
|
Map<String, Object> map = (Map<String, Object>) ((Map<String, Object>) ((Map<String, Object>) trace
|
||||||
trace.get("info")).get("headers")).get("response");
|
.get("info")).get("headers")).get("response");
|
||||||
assertEquals("200", map.get("status"));
|
assertEquals("200", map.get("status"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,4 +66,10 @@ public class MessageController {
|
||||||
redirect.addFlashAttribute("globalMessage", "Successfully created a new message");
|
redirect.addFlashAttribute("globalMessage", "Successfully created a new message");
|
||||||
return new ModelAndView("redirect:/{message.id}", "message.id", message.getId());
|
return new ModelAndView("redirect:/{message.id}", "message.id", message.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/foo")
|
||||||
|
public String foo() {
|
||||||
|
throw new RuntimeException("Expected exception in controller");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,12 @@
|
||||||
package org.springframework.boot.sample.ui;
|
package org.springframework.boot.sample.ui;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.hamcrest.Description;
|
import org.hamcrest.Description;
|
||||||
|
|
@ -15,13 +22,6 @@ import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Basic Spring MVC Test for the Sample Controller"
|
* A Basic Spring MVC Test for the Sample Controller"
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue