diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorController.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorController.java index dee8ab911b..0bd7571778 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorController.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorController.java @@ -33,7 +33,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; /** @@ -83,6 +82,16 @@ public class BasicErrorController extends AbstractErrorController { return this.errorProperties.getPath(); } + @RequestMapping(produces = { "application/xml", "text/xml", "application/json", + "application/*+xml", "application/*+json" }) + public ResponseEntity> errorStructured( + HttpServletRequest request) { + Map body = getErrorAttributes(request, + isIncludeStackTrace(request, MediaType.ALL)); + HttpStatus status = getStatus(request); + return new ResponseEntity>(body, status); + } + @RequestMapping(produces = "text/html") public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { @@ -95,12 +104,20 @@ public class BasicErrorController extends AbstractErrorController { } @RequestMapping - @ResponseBody - public ResponseEntity> error(HttpServletRequest request) { - Map body = getErrorAttributes(request, - isIncludeStackTrace(request, MediaType.ALL)); + public ResponseEntity errorText(HttpServletRequest request) { + Map attributes = getErrorAttributes(request, + isIncludeStackTrace(request, MediaType.TEXT_PLAIN)); + int padding = 0; + for (Map.Entry entry : attributes.entrySet()) { + padding = Math.max(padding, entry.getKey().length()); + } + StringBuffer body = new StringBuffer(); + for (Map.Entry entry : attributes.entrySet()) { + body.append(String.format("%-" + padding + "s : %s%n", entry.getKey(), + entry.getValue())); + } HttpStatus status = getStatus(request); - return new ResponseEntity<>(body, status); + return new ResponseEntity<>(body.toString(), status); } /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorControllerIntegrationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorControllerIntegrationTests.java index 6b32f333c8..1af5e07cce 100755 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorControllerIntegrationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorControllerIntegrationTests.java @@ -169,6 +169,7 @@ public class BasicErrorControllerIntegrationTests { load("--server.error.include-exception=true"); RequestEntity request = RequestEntity .post(URI.create(createUrl("/bodyValidation"))) + .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON).body("{}"); ResponseEntity entity = new TestRestTemplate().exchange(request, Map.class); String resp = entity.getBody().toString(); @@ -189,6 +190,18 @@ public class BasicErrorControllerIntegrationTests { assertThat(resp).doesNotContain("org.springframework.validation.BindException"); } + @Test + public void testRequestBodyValidationForText() { + load(); + RequestEntity request = RequestEntity.post(URI.create(createUrl("/"))) + .accept(MediaType.TEXT_PLAIN).build(); + ResponseEntity entity = new TestRestTemplate().exchange(request, + String.class); + String resp = entity.getBody().toString(); + assertThat(resp).contains("status"); + assertThat(resp).contains("error"); + } + @Test public void testConventionTemplateMapping() { load(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java index c09cd8b13f..f3b36f6111 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java @@ -92,7 +92,8 @@ public class Handler extends URLStreamHandler { @Override protected URLConnection openConnection(URL url) throws IOException { - if (this.jarFile != null) { + if (this.jarFile != null + && url.toString().startsWith(this.jarFile.getUrl().toString())) { return JarURLConnection.get(url, this.jarFile); } try { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarFileTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarFileTests.java index 0f1af0aac9..c00f2f16fb 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarFileTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarFileTests.java @@ -481,4 +481,24 @@ public class JarFileTests { assertThat(temp.delete()).isTrue(); } + @Test + public void createUrlFromStringWithContextWhenNotFound() throws Exception { + // gh-12483 + JarURLConnection.setUseFastExceptions(true); + try { + JarFile.registerUrlProtocolHandler(); + JarFile nested = this.jarFile + .getNestedJarFile(this.jarFile.getEntry("nested.jar")); + URL context = nested.getUrl(); + new URL(context, "jar:" + this.rootJarFile.toURI() + "!/nested.jar!/3.dat") + .openConnection().getInputStream().close(); + this.thrown.expect(FileNotFoundException.class); + new URL(context, "jar:" + this.rootJarFile.toURI() + "!/no.dat") + .openConnection().getInputStream().close(); + } + finally { + JarURLConnection.setUseFastExceptions(false); + } + } + }