From e18901d6b10d0b14959749ff63828339f71986b1 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 24 Apr 2020 12:53:00 +0100 Subject: [PATCH] Headers support in ResponseStatusExceptionResolver Closes gh-24944 --- .../ResponseStatusExceptionResolver.java | 14 +++++++++++--- .../ResponseStatusExceptionResolverTests.java | 16 +++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java index 8b79a1fd7e..5d8d40a5e3 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -115,8 +115,10 @@ public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionRes /** * Template method that handles an {@link ResponseStatusException}. - *

The default implementation delegates to {@link #applyStatusAndReason} - * with the status code and reason from the exception. + *

The default implementation applies the headers from + * {@link ResponseStatusException#getResponseHeaders()} and delegates to + * {@link #applyStatusAndReason} with the status code and reason from the + * exception. * @param ex the exception * @param request current HTTP request * @param response current HTTP response @@ -128,6 +130,12 @@ public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionRes protected ModelAndView resolveResponseStatusException(ResponseStatusException ex, HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws Exception { + ex.getResponseHeaders().forEach((name, values) -> { + if (response.getHeader(name) == null) { + values.forEach(value -> response.addHeader(name, value)); + } + }); + int statusCode = ex.getStatus().value(); String reason = ex.getReason(); return applyStatusAndReason(statusCode, reason, response); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolverTests.java index 92009f642e..7e9338fa8a 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -18,6 +18,7 @@ package org.springframework.web.servlet.mvc.annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; import java.util.Locale; import org.junit.jupiter.api.BeforeEach; @@ -28,8 +29,11 @@ import org.springframework.beans.testfixture.beans.ITestBean; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.support.StaticMessageSource; import org.springframework.core.annotation.AliasFor; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.server.MethodNotAllowedException; import org.springframework.web.server.ResponseStatusException; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.testfixture.servlet.MockHttpServletRequest; @@ -128,6 +132,16 @@ public class ResponseStatusExceptionResolverTests { assertResolved(mav, 400, "The reason"); } + @Test + void responseStatusExceptionWithHeaders() { + Exception ex = new MethodNotAllowedException( + HttpMethod.GET, Arrays.asList(HttpMethod.POST, HttpMethod.PUT)); + + ModelAndView mav = exceptionResolver.resolveException(request, response, null, ex); + + assertResolved(mav, 405, "Request method 'GET' not supported"); + assertThat(response.getHeader(HttpHeaders.ALLOW)).isEqualTo("POST,PUT"); + } private void assertResolved(ModelAndView mav, int status, String reason) { assertThat(mav != null && mav.isEmpty()).as("No Empty ModelAndView returned").isTrue();