From 16b867bf63e296eba874906ee05afb69438c6627 Mon Sep 17 00:00:00 2001 From: yukong <92148749+kyukong@users.noreply.github.com> Date: Thu, 22 Sep 2022 17:57:04 +0900 Subject: [PATCH] =?UTF-8?q?[MVC=20=EA=B5=AC=ED=98=84=ED=95=98=EA=B8=B0=20-?= =?UTF-8?q?=201=EB=8B=A8=EA=B3=84]=20=EC=9C=A0=EC=BD=A9(=EA=B9=80=EC=9C=A0?= =?UTF-8?q?=EB=B9=88)=20=EB=AF=B8=EC=85=98=20=EC=A0=9C=EC=B6=9C=ED=95=A9?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.=20=20(#169)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: 1단계 체크리스트 작성 * feat: @Controller 읽어와 핸들러 추가 * feat: 핸들러 메서드 실행 * feat: DispatcherServlet 초기화 시 어노테이션 기반 핸들러 매핑 추가 * feat: HandlerAdapter 이용하여 Handler 실제 로직 수행 * feat: ModelAndView rendering --- README.md | 4 ++ .../AppWebApplicationInitializer.java | 12 ++++- .../com/techcourse/ManualHandlerAdapter.java | 21 ++++++++ .../java/nextstep/mvc/DispatcherServlet.java | 43 ++++++++++----- .../tobe/AnnotationHandlerAdapter.java | 20 +++++++ .../tobe/AnnotationHandlerMapping.java | 53 ++++++++++++++++++- .../mvc/controller/tobe/HandlerExecution.java | 27 +++++++++- .../main/java/nextstep/mvc/view/JsonView.java | 6 +++ .../main/java/nextstep/mvc/view/JspView.java | 31 +++++++++-- .../java/nextstep/mvc/view/ModelAndView.java | 11 ++++ .../nextstep/web/support/RequestMethod.java | 11 ++++ .../tobe/AnnotationHandlerMappingTest.java | 18 +++++++ .../controller/tobe/HandlerExecutionTest.java | 33 ++++++++++++ .../web/support/RequestMethodTest.java | 24 +++++++++ mvc/src/test/java/samples/TestController.java | 4 +- 15 files changed, 296 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/com/techcourse/ManualHandlerAdapter.java create mode 100644 mvc/src/main/java/nextstep/mvc/controller/tobe/AnnotationHandlerAdapter.java create mode 100644 mvc/src/test/java/nextstep/mvc/controller/tobe/HandlerExecutionTest.java create mode 100644 mvc/src/test/java/nextstep/web/support/RequestMethodTest.java diff --git a/README.md b/README.md index 35eff71457..507d3daec6 100644 --- a/README.md +++ b/README.md @@ -1 +1,5 @@ # @MVC 구현하기 + +## 🚀 1단계 - @MVC 프레임워크 구현하기 +- [x] AnnotationHandlerMappingTest가 정상 동작한다. +- [x] DispatcherServlet에서 HandlerMapping 인터페이스를 활용하여 AnnotationHandlerMapping과 ManualHandlerMapping 둘다 처리할 수 있다. diff --git a/app/src/main/java/com/techcourse/AppWebApplicationInitializer.java b/app/src/main/java/com/techcourse/AppWebApplicationInitializer.java index 53e45857fd..431edcf4b3 100644 --- a/app/src/main/java/com/techcourse/AppWebApplicationInitializer.java +++ b/app/src/main/java/com/techcourse/AppWebApplicationInitializer.java @@ -2,6 +2,8 @@ import jakarta.servlet.ServletContext; import nextstep.mvc.DispatcherServlet; +import nextstep.mvc.controller.tobe.AnnotationHandlerAdapter; +import nextstep.mvc.controller.tobe.AnnotationHandlerMapping; import nextstep.web.WebApplicationInitializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,7 +15,7 @@ public class AppWebApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(final ServletContext servletContext) { final var dispatcherServlet = new DispatcherServlet(); - dispatcherServlet.addHandlerMapping(new ManualHandlerMapping()); + addHandlerMappingAndAdapter(dispatcherServlet); final var dispatcher = servletContext.addServlet("dispatcher", dispatcherServlet); dispatcher.setLoadOnStartup(1); @@ -21,4 +23,12 @@ public void onStartup(final ServletContext servletContext) { log.info("Start AppWebApplication Initializer"); } + + private void addHandlerMappingAndAdapter(final DispatcherServlet dispatcherServlet) { + dispatcherServlet.addHandlerMapping(new ManualHandlerMapping()); + dispatcherServlet.addHandlerMapping(new AnnotationHandlerMapping()); + + dispatcherServlet.addHandlerAdapter(new ManualHandlerAdapter()); + dispatcherServlet.addHandlerAdapter(new AnnotationHandlerAdapter()); + } } diff --git a/app/src/main/java/com/techcourse/ManualHandlerAdapter.java b/app/src/main/java/com/techcourse/ManualHandlerAdapter.java new file mode 100644 index 0000000000..38a529a359 --- /dev/null +++ b/app/src/main/java/com/techcourse/ManualHandlerAdapter.java @@ -0,0 +1,21 @@ +package com.techcourse; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import nextstep.mvc.HandlerAdapter; +import nextstep.mvc.controller.asis.Controller; +import nextstep.mvc.view.ModelAndView; + +public class ManualHandlerAdapter implements HandlerAdapter { + + @Override + public boolean supports(final Object handler) { + return handler instanceof Controller; + } + + @Override + public ModelAndView handle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception { + final String viewName = ((Controller) handler).execute(request, response); + return new ModelAndView(viewName); + } +} diff --git a/mvc/src/main/java/nextstep/mvc/DispatcherServlet.java b/mvc/src/main/java/nextstep/mvc/DispatcherServlet.java index 7c0ee2e5f1..f05f2a88ae 100644 --- a/mvc/src/main/java/nextstep/mvc/DispatcherServlet.java +++ b/mvc/src/main/java/nextstep/mvc/DispatcherServlet.java @@ -4,13 +4,14 @@ import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import nextstep.mvc.controller.asis.Controller; -import nextstep.mvc.view.JspView; +import nextstep.mvc.view.ModelAndView; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; public class DispatcherServlet extends HttpServlet { @@ -19,9 +20,11 @@ public class DispatcherServlet extends HttpServlet { private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class); private final List handlerMappings; + private final List handlerAdapters; public DispatcherServlet() { this.handlerMappings = new ArrayList<>(); + this.handlerAdapters = new ArrayList<>(); } @Override @@ -33,36 +36,48 @@ public void addHandlerMapping(final HandlerMapping handlerMapping) { handlerMappings.add(handlerMapping); } + public void addHandlerAdapter(final HandlerAdapter handlerAdapter) { + handlerAdapters.add(handlerAdapter); + } + @Override protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException { log.debug("Method : {}, Request URI : {}", request.getMethod(), request.getRequestURI()); try { - final var controller = getController(request); - final var viewName = controller.execute(request, response); - move(viewName, request, response); + final Object handler = getHandler(request); + final ModelAndView modelAndView = getModelAndView(handler, request, response); + final Map model = modelAndView.getModel(); + modelAndView.getView().render(model, request, response); } catch (Throwable e) { log.error("Exception : {}", e.getMessage(), e); throw new ServletException(e.getMessage()); } } - private Controller getController(final HttpServletRequest request) { + private Object getHandler(final HttpServletRequest request) { return handlerMappings.stream() .map(handlerMapping -> handlerMapping.getHandler(request)) .filter(Objects::nonNull) - .map(Controller.class::cast) .findFirst() .orElseThrow(); } - private void move(final String viewName, final HttpServletRequest request, final HttpServletResponse response) throws Exception { - if (viewName.startsWith(JspView.REDIRECT_PREFIX)) { - response.sendRedirect(viewName.substring(JspView.REDIRECT_PREFIX.length())); - return; - } + private ModelAndView getModelAndView(final Object handler, final HttpServletRequest request, final HttpServletResponse response) { + return handlerAdapters.stream() + .filter(handlerAdapter -> handlerAdapter.supports(handler)) + .map(handlerAdapter -> getModelAndView(handler, request, response, handlerAdapter)) + .findFirst() + .orElseThrow(); + } - final var requestDispatcher = request.getRequestDispatcher(viewName); - requestDispatcher.forward(request, response); + private ModelAndView getModelAndView(final Object handler, + final HttpServletRequest request, final HttpServletResponse response, final HandlerAdapter handlerAdapter + ){ + try { + return handlerAdapter.handle(request, response, handler); + } catch (Exception e) { + throw new IllegalArgumentException(String.format("적절하지 않은 handler 입니다. [%s]", handler)); + } } } diff --git a/mvc/src/main/java/nextstep/mvc/controller/tobe/AnnotationHandlerAdapter.java b/mvc/src/main/java/nextstep/mvc/controller/tobe/AnnotationHandlerAdapter.java new file mode 100644 index 0000000000..1e8b91c668 --- /dev/null +++ b/mvc/src/main/java/nextstep/mvc/controller/tobe/AnnotationHandlerAdapter.java @@ -0,0 +1,20 @@ +package nextstep.mvc.controller.tobe; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import nextstep.mvc.HandlerAdapter; +import nextstep.mvc.view.ModelAndView; +import nextstep.web.annotation.Controller; + +public class AnnotationHandlerAdapter implements HandlerAdapter { + + @Override + public boolean supports(final Object handler) { + return handler.getClass().isAnnotationPresent(Controller.class); + } + + @Override + public ModelAndView handle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception { + return ((HandlerExecution) handler).handle(request, response); + } +} diff --git a/mvc/src/main/java/nextstep/mvc/controller/tobe/AnnotationHandlerMapping.java b/mvc/src/main/java/nextstep/mvc/controller/tobe/AnnotationHandlerMapping.java index cc1331de49..aeb6a5c7e7 100644 --- a/mvc/src/main/java/nextstep/mvc/controller/tobe/AnnotationHandlerMapping.java +++ b/mvc/src/main/java/nextstep/mvc/controller/tobe/AnnotationHandlerMapping.java @@ -2,11 +2,18 @@ import jakarta.servlet.http.HttpServletRequest; import nextstep.mvc.HandlerMapping; +import nextstep.web.annotation.Controller; +import nextstep.web.annotation.RequestMapping; +import nextstep.web.support.RequestMethod; + +import org.reflections.Reflections; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; +import java.util.Set; public class AnnotationHandlerMapping implements HandlerMapping { @@ -20,11 +27,55 @@ public AnnotationHandlerMapping(final Object... basePackage) { this.handlerExecutions = new HashMap<>(); } + @Override public void initialize() { + final Reflections reflections = new Reflections(basePackage); + final Set> controllers = reflections.getTypesAnnotatedWith(Controller.class); + for (final Class controller : controllers) { + addRequestMappingMethod(controller); + } log.info("Initialized AnnotationHandlerMapping!"); } + @Override public Object getHandler(final HttpServletRequest request) { - return null; + final HandlerKey handlerKey = new HandlerKey(request.getRequestURI(), RequestMethod.from(request.getMethod())); + log.info("request handler [{}]", handlerKey); + if (!handlerExecutions.containsKey(handlerKey)) { + throw new IllegalArgumentException(String.format("요청한 핸들러가 존재하지 않습니다. [%s]", handlerKey)); + } + final HandlerExecution handlerExecution = handlerExecutions.get(handlerKey); + log.info("search handler [{}]", handlerExecution); + return handlerExecution; + } + + private void addRequestMappingMethod(final Class controller) { + final Method[] methods = controller.getMethods(); + for (final Method method : methods) { + if (!method.isAnnotationPresent(RequestMapping.class)) { + continue; + } + final RequestMapping requestMapping = method.getAnnotation(RequestMapping.class); + addHandlerExecution(controller, requestMapping, method); + } + } + + private void addHandlerExecution(final Class controller, final RequestMapping requestMapping, final Method method) { + for (final RequestMethod requestMethod : requestMapping.method()) { + final HandlerKey handlerKey = new HandlerKey(requestMapping.value(), requestMethod); + if (handlerExecutions.containsKey(handlerKey)) { + throw new IllegalArgumentException(String.format("중복적으로 매핑되었습니다. [%s]", handlerKey)); + } + handlerExecutions.put(handlerKey, getHandlerExecution(controller, method)); + } + } + + private HandlerExecution getHandlerExecution(final Class controller, final Method method) { + try { + final Object newInstance = controller.getConstructor().newInstance(); + return new HandlerExecution(newInstance, method); + } catch (Exception e) { + throw new IllegalArgumentException(String.format("기본 생성자가 존재하지 않습니다. [%s]", controller)); + } } } diff --git a/mvc/src/main/java/nextstep/mvc/controller/tobe/HandlerExecution.java b/mvc/src/main/java/nextstep/mvc/controller/tobe/HandlerExecution.java index ebdc3d9b10..ccb987e89d 100644 --- a/mvc/src/main/java/nextstep/mvc/controller/tobe/HandlerExecution.java +++ b/mvc/src/main/java/nextstep/mvc/controller/tobe/HandlerExecution.java @@ -1,12 +1,37 @@ package nextstep.mvc.controller.tobe; +import java.lang.reflect.Method; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import nextstep.mvc.view.ModelAndView; public class HandlerExecution { + private final static Logger log = LoggerFactory.getLogger(HandlerExecution.class); + + private final Object clazz; + private final Method method; + + public HandlerExecution(final Object clazz, final Method method) { + this.clazz = clazz; + this.method = method; + } + public ModelAndView handle(final HttpServletRequest request, final HttpServletResponse response) throws Exception { - return null; + final ModelAndView modelAndView = (ModelAndView) method.invoke(clazz, request, response); + log.info("execute controller method [{}]", method); + return modelAndView; + } + + @Override + public String toString() { + return "HandlerExecution{" + + "clazz=" + clazz + + ", method=" + method + + '}'; } } diff --git a/mvc/src/main/java/nextstep/mvc/view/JsonView.java b/mvc/src/main/java/nextstep/mvc/view/JsonView.java index b7c4c06a71..07327a348d 100644 --- a/mvc/src/main/java/nextstep/mvc/view/JsonView.java +++ b/mvc/src/main/java/nextstep/mvc/view/JsonView.java @@ -7,6 +7,12 @@ public class JsonView implements View { + private final String body; + + public JsonView(final String body) { + this.body = body; + } + @Override public void render(final Map model, final HttpServletRequest request, HttpServletResponse response) throws Exception { } diff --git a/mvc/src/main/java/nextstep/mvc/view/JspView.java b/mvc/src/main/java/nextstep/mvc/view/JspView.java index 858c3ccece..f04e3db037 100644 --- a/mvc/src/main/java/nextstep/mvc/view/JspView.java +++ b/mvc/src/main/java/nextstep/mvc/view/JspView.java @@ -1,10 +1,12 @@ package nextstep.mvc.view; +import jakarta.servlet.RequestDispatcher; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.Map; public class JspView implements View { @@ -13,18 +15,41 @@ public class JspView implements View { public static final String REDIRECT_PREFIX = "redirect:"; + private final String viewName; + private final boolean redirect; + public JspView(final String viewName) { + validateJsp(viewName); + if (viewName.startsWith(REDIRECT_PREFIX)) { + this.viewName = viewName.substring(JspView.REDIRECT_PREFIX.length()); + this.redirect = true; + return; + } + this.viewName = viewName; + this.redirect = false; + } + + private void validateJsp(final String viewName) { + if (!viewName.endsWith(".jsp")) { + throw new IllegalArgumentException(String.format("JSP 형식이 아닙니다. [%s]", viewName)); + } } @Override public void render(final Map model, final HttpServletRequest request, final HttpServletResponse response) throws Exception { - // todo - + addLocation(response); model.keySet().forEach(key -> { log.debug("attribute name : {}, value : {}", key, model.get(key)); request.setAttribute(key, model.get(key)); }); - // todo + final RequestDispatcher requestDispatcher = request.getRequestDispatcher(viewName); + requestDispatcher.forward(request, response); + } + + private void addLocation(final HttpServletResponse response) throws IOException { + if (redirect) { + response.sendRedirect(viewName); + } } } diff --git a/mvc/src/main/java/nextstep/mvc/view/ModelAndView.java b/mvc/src/main/java/nextstep/mvc/view/ModelAndView.java index cb172084b3..aeda8b3c77 100644 --- a/mvc/src/main/java/nextstep/mvc/view/ModelAndView.java +++ b/mvc/src/main/java/nextstep/mvc/view/ModelAndView.java @@ -9,6 +9,10 @@ public class ModelAndView { private final View view; private final Map model; + public ModelAndView(final String viewName) { + this(getView(viewName)); + } + public ModelAndView(final View view) { this.view = view; this.model = new HashMap<>(); @@ -19,6 +23,13 @@ public ModelAndView addObject(final String attributeName, final Object attribute return this; } + private static View getView(String viewName) { + if (viewName.endsWith(".jsp")) { + return new JspView(viewName); + } + return new JsonView(viewName); + } + public Object getObject(final String attributeName) { return model.get(attributeName); } diff --git a/mvc/src/main/java/nextstep/web/support/RequestMethod.java b/mvc/src/main/java/nextstep/web/support/RequestMethod.java index 1f37f21d5a..06c1d93a7c 100644 --- a/mvc/src/main/java/nextstep/web/support/RequestMethod.java +++ b/mvc/src/main/java/nextstep/web/support/RequestMethod.java @@ -1,5 +1,16 @@ package nextstep.web.support; +import java.util.Arrays; + public enum RequestMethod { + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE + ; + + public static RequestMethod from(final String value) { + return Arrays.stream(values()) + .filter(method -> method.name().equals(value.toUpperCase())) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(String.format("정의되지 않은 메서드입니다. [%s]", value))); + } } diff --git a/mvc/src/test/java/nextstep/mvc/controller/tobe/AnnotationHandlerMappingTest.java b/mvc/src/test/java/nextstep/mvc/controller/tobe/AnnotationHandlerMappingTest.java index 3236c5c433..c139e46b05 100644 --- a/mvc/src/test/java/nextstep/mvc/controller/tobe/AnnotationHandlerMappingTest.java +++ b/mvc/src/test/java/nextstep/mvc/controller/tobe/AnnotationHandlerMappingTest.java @@ -3,9 +3,11 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -19,6 +21,7 @@ void setUp() { handlerMapping.initialize(); } + @DisplayName("GET 요청에 대한 handler 조회") @Test void get() throws Exception { final var request = mock(HttpServletRequest.class); @@ -34,6 +37,7 @@ void get() throws Exception { assertThat(modelAndView.getObject("id")).isEqualTo("gugu"); } + @DisplayName("POST 요청에 대한 handler 조회") @Test void post() throws Exception { final var request = mock(HttpServletRequest.class); @@ -48,4 +52,18 @@ void post() throws Exception { assertThat(modelAndView.getObject("id")).isEqualTo("gugu"); } + + @DisplayName("추가하지 않은 handler 조회 시 예외 발생") + @Test + void getHandlerNotExist() { + final var request = mock(HttpServletRequest.class); + + when(request.getAttribute("id")).thenReturn("gugu"); + when(request.getRequestURI()).thenReturn("/not-exist-path"); + when(request.getMethod()).thenReturn("GET"); + + assertThatThrownBy(() -> handlerMapping.getHandler(request)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("요청한 핸들러가 존재하지 않습니다."); + } } diff --git a/mvc/src/test/java/nextstep/mvc/controller/tobe/HandlerExecutionTest.java b/mvc/src/test/java/nextstep/mvc/controller/tobe/HandlerExecutionTest.java new file mode 100644 index 0000000000..a50bfa7b91 --- /dev/null +++ b/mvc/src/test/java/nextstep/mvc/controller/tobe/HandlerExecutionTest.java @@ -0,0 +1,33 @@ +package nextstep.mvc.controller.tobe; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Method; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import nextstep.mvc.view.ModelAndView; +import samples.TestController; + +class HandlerExecutionTest { + + @DisplayName("객체에 정의된 메서드 실행") + @Test + void handle() throws Exception { + final Object object = TestController.class.getConstructor().newInstance(); + final Method method = object.getClass().getMethods()[0]; + final HandlerExecution handlerExecution = new HandlerExecution(object, method); + + final var request = mock(HttpServletRequest.class); + final var response = mock(HttpServletResponse.class); + when(request.getAttribute("id")).thenReturn("gugu"); + + final ModelAndView modelAndView = handlerExecution.handle(request, response); + assertThat(modelAndView.getObject("id")).isEqualTo("gugu"); + } +} diff --git a/mvc/src/test/java/nextstep/web/support/RequestMethodTest.java b/mvc/src/test/java/nextstep/web/support/RequestMethodTest.java new file mode 100644 index 0000000000..619ac81e63 --- /dev/null +++ b/mvc/src/test/java/nextstep/web/support/RequestMethodTest.java @@ -0,0 +1,24 @@ +package nextstep.web.support; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class RequestMethodTest { + + @DisplayName("입력한 문자열과 동일한 RequestMethod 객체 조회") + @Test + void from() { + assertThat(RequestMethod.from("get")).isEqualTo(RequestMethod.GET); + } + + @DisplayName("존재하지 않는 RequestMethod 조회 시 예외 발생") + @Test + void fromAboutNotExist() { + assertThatThrownBy(() -> RequestMethod.from("invalid method")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("정의되지 않은 메서드입니다."); + } +} diff --git a/mvc/src/test/java/samples/TestController.java b/mvc/src/test/java/samples/TestController.java index 49d81be351..7c14c3703e 100644 --- a/mvc/src/test/java/samples/TestController.java +++ b/mvc/src/test/java/samples/TestController.java @@ -18,7 +18,7 @@ public class TestController { @RequestMapping(value = "/get-test", method = RequestMethod.GET) public ModelAndView findUserId(final HttpServletRequest request, final HttpServletResponse response) { log.info("test controller get method"); - final var modelAndView = new ModelAndView(new JspView("")); + final var modelAndView = new ModelAndView(new JspView("/get-test.jsp")); modelAndView.addObject("id", request.getAttribute("id")); return modelAndView; } @@ -26,7 +26,7 @@ public ModelAndView findUserId(final HttpServletRequest request, final HttpServl @RequestMapping(value = "/post-test", method = RequestMethod.POST) public ModelAndView save(final HttpServletRequest request, final HttpServletResponse response) { log.info("test controller post method"); - final var modelAndView = new ModelAndView(new JspView("")); + final var modelAndView = new ModelAndView(new JspView("/post-test.jsp")); modelAndView.addObject("id", request.getAttribute("id")); return modelAndView; }