diff --git a/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java b/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java index d485607516..08a8d58880 100644 --- a/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java +++ b/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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.method.support; import java.util.Map; +import org.springframework.http.HttpStatus; import org.springframework.ui.Model; import org.springframework.ui.ModelMap; import org.springframework.validation.support.BindingAwareModelMap; @@ -54,6 +55,8 @@ public class ModelAndViewContainer { private boolean redirectModelScenario = false; + private HttpStatus status; + private final SessionStatus sessionStatus = new SimpleSessionStatus(); private boolean requestHandled = false; @@ -176,6 +179,22 @@ public class ModelAndViewContainer { return this.sessionStatus; } + /** + * Provide a HTTP status that will be passed on to with the + * {@code ModelAndView} used for view rendering purposes. + * @since 4.3 + */ + public void setStatus(HttpStatus status) { + this.status = status; + } + + /** + * Return the configured HTTP status, if any. + */ + public HttpStatus getStatus() { + return this.status; + } + /** * Whether the request has been handled fully within the handler, e.g. * {@code @ResponseBody} method, and therefore view resolution is not diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java index 0143fdc868..4dab2ac9c1 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -1240,6 +1240,9 @@ public class DispatcherServlet extends FrameworkServlet { logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'"); } try { + if (mv.getStatus() != null) { + response.setStatus(mv.getStatus().value()); + } view.render(mv.getModelInternal(), request, response); } catch (Exception ex) { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/ModelAndView.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/ModelAndView.java index d58ea7de6a..d7df2ab9c2 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/ModelAndView.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/ModelAndView.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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; import java.util.Map; +import org.springframework.http.HttpStatus; import org.springframework.ui.ModelMap; import org.springframework.util.CollectionUtils; @@ -36,6 +37,7 @@ import org.springframework.util.CollectionUtils; * @author Rod Johnson * @author Juergen Hoeller * @author Rob Harrop + * @author Rossen Stoyanchev * @see DispatcherServlet * @see ViewResolver * @see HandlerAdapter#handle @@ -49,6 +51,9 @@ public class ModelAndView { /** Model Map */ private ModelMap model; + /** Optional status for the response */ + private HttpStatus status; + /** Indicates whether or not this instance has been cleared with a call to {@link #clear()} */ private boolean cleared = false; @@ -115,6 +120,24 @@ public class ModelAndView { } } + + /** + * Creates new ModelAndView given a view name, model, and status. + * @param viewName name of the View to render, to be resolved + * by the DispatcherServlet's ViewResolver + * @param model Map of model names (Strings) to model objects + * (Objects). Model entries may not be {@code null}, but the + * model Map may be {@code null} if there is no model data. + * @param status an alternative status code to use for the response. + */ + public ModelAndView(String viewName, Map model, HttpStatus status) { + this.view = viewName; + if (model != null) { + getModelMap().addAllAttributes(model); + } + this.status = status; + } + /** * Convenient constructor to take a single model object. * @param viewName name of the View to render, to be resolved @@ -215,6 +238,21 @@ public class ModelAndView { return getModelMap(); } + /** + * Set the status to use for the response. + * @since 4.3 + */ + public void setStatus(HttpStatus status) { + this.status = status; + } + + /** + * Return the configured status for the response. + */ + public HttpStatus getStatus() { + return this.status; + } + /** * Add an attribute to the model. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandler.java index a848a97058..10ea402812 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -98,6 +98,7 @@ public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturn } } } + mavContainer.setStatus(mav.getStatus()); mavContainer.addAllAttributes(mav.getModel()); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java index a1c8e09443..38c0b49f27 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -916,7 +916,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter return null; } ModelMap model = mavContainer.getModel(); - ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model); + ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java index 32bf6882e5..f60ae38b58 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -1736,6 +1736,19 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl assertArrayEquals(content, response.getContentAsByteArray()); } + @Test + public void modelAndViewWithStatus() throws Exception { + initServletWithControllers(ModelAndViewController.class); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/path"); + MockHttpServletResponse response = new MockHttpServletResponse(); + getServlet().service(request, response); + + assertEquals(422, response.getStatus()); + assertEquals("view", response.getForwardedUrl()); + } + + /* * Controllers */ @@ -3219,6 +3232,14 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl } } + @Controller + public static class ModelAndViewController { + + @RequestMapping("/path") + public ModelAndView methodWithHttpStatus(MyEntity object) { + return new ModelAndView("view", new ModelMap(), HttpStatus.UNPROCESSABLE_ENTITY); + } + } // Test cases deleted from the original ServletAnnotationControllerTests: