From 3b8d02cb1ac912878dfbc44168635c645a2b8d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8A=A4=ED=94=8C=EB=A6=BF?= Date: Wed, 27 Sep 2023 15:35:19 +0900 Subject: [PATCH] =?UTF-8?q?[MVC=20=EA=B5=AC=ED=98=84=20-=203=EB=8B=A8?= =?UTF-8?q?=EA=B3=84]=20=EC=8A=A4=ED=94=8C=EB=A6=BF-=EB=B0=95=EC=83=81?= =?UTF-8?q?=ED=98=84=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(#562)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/techcourse/DispatcherServlet.java | 63 ----------- .../DispatcherServletInitializer.java | 13 ++- .../com/techcourse/ManualHandlerMapping.java | 47 -------- .../techcourse/NotFoundResponseHandler.java | 33 ++++++ .../techcourse/controller/HomeController.java | 20 ++++ .../controller/LoginController.java | 34 +++--- .../controller/LoginViewController.java | 27 +++-- .../controller/LogoutController.java | 17 ++- .../controller/RegisterController.java | 13 +-- .../controller/RegisterViewController.java | 17 ++- .../techcourse/controller/UserController.java | 32 ++++++ .../controller/HomeControllerTest.java | 37 ++++++ .../controller/LoginViewControllerTest.java | 10 +- .../controller/LogoutControllerTest.java | 5 +- .../controller/RegisterControllerTest.java | 24 ---- .../RegisterViewControllerTest.java | 11 +- .../controller/UserControllerTest.java | 36 ++++++ .../web/servlet/ModelAndView.java | 7 ++ .../web/servlet/mvc/asis/Controller.java | 8 -- .../servlet/mvc/asis/ForwardController.java | 20 ---- .../servlet/mvc/tobe/DispatcherServlet.java | 105 ++++++++++++++++++ ...ontrollerClassNotFoundByNameException.java | 8 -- .../tobe/exception/ViewRenderException.java | 7 ++ .../ExceptionHandler.java | 14 +++ .../ExceptionHandlers.java | 29 +++++ .../AnnotationHandlerAdapter.java | 2 +- .../ControllerHandlerAdapter.java | 27 ----- .../tobe/handleradapter/HandlerAdapters.java | 24 ++-- .../AnnotationHandlerMapping.java | 25 +++-- .../tobe/handlermapping/HandlerMapping.java | 4 +- .../tobe/handlermapping/HandlerMappings.java | 14 +-- .../web/servlet/view/JsonView.java | 47 +++++++- .../web/servlet/view/JspView.java | 10 +- .../tobe/AnnotationHandlerMappingTest.java | 4 +- 34 files changed, 502 insertions(+), 292 deletions(-) delete mode 100644 app/src/main/java/com/techcourse/DispatcherServlet.java delete mode 100644 app/src/main/java/com/techcourse/ManualHandlerMapping.java create mode 100644 app/src/main/java/com/techcourse/NotFoundResponseHandler.java create mode 100644 app/src/main/java/com/techcourse/controller/HomeController.java create mode 100644 app/src/main/java/com/techcourse/controller/UserController.java create mode 100644 app/src/test/java/com/techcourse/controller/HomeControllerTest.java create mode 100644 app/src/test/java/com/techcourse/controller/UserControllerTest.java delete mode 100644 mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/Controller.java delete mode 100644 mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/ForwardController.java create mode 100644 mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/DispatcherServlet.java delete mode 100644 mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exception/ControllerClassNotFoundByNameException.java create mode 100644 mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exception/ViewRenderException.java create mode 100644 mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exceptionhandlermapping/ExceptionHandler.java create mode 100644 mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exceptionhandlermapping/ExceptionHandlers.java delete mode 100644 mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handleradapter/ControllerHandlerAdapter.java diff --git a/app/src/main/java/com/techcourse/DispatcherServlet.java b/app/src/main/java/com/techcourse/DispatcherServlet.java deleted file mode 100644 index c7579414cf..0000000000 --- a/app/src/main/java/com/techcourse/DispatcherServlet.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.techcourse; - -import jakarta.servlet.RequestDispatcher; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Collections; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import webmvc.org.springframework.web.servlet.ModelAndView; -import webmvc.org.springframework.web.servlet.View; -import webmvc.org.springframework.web.servlet.mvc.tobe.exception.HandlerAdapterNotExistException; -import webmvc.org.springframework.web.servlet.mvc.tobe.exception.HandlerNotExistException; -import webmvc.org.springframework.web.servlet.mvc.tobe.handleradapter.AnnotationHandlerAdapter; -import webmvc.org.springframework.web.servlet.mvc.tobe.handleradapter.ControllerHandlerAdapter; -import webmvc.org.springframework.web.servlet.mvc.tobe.handleradapter.HandlerAdapters; -import webmvc.org.springframework.web.servlet.mvc.tobe.handlermapping.AnnotationHandlerMapping; -import webmvc.org.springframework.web.servlet.mvc.tobe.handlermapping.HandlerMappings; - -public class DispatcherServlet extends HttpServlet { - - private static final long serialVersionUID = 1L; - private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class); - - private HandlerMappings handlerMappings = new HandlerMappings(); - private HandlerAdapters handlerAdapters = new HandlerAdapters(); - - @Override - public void init() { - final String packageName = getClass().getPackageName(); - handlerMappings.addHandlerMapping(new ManualHandlerMapping()); - handlerMappings.addHandlerMapping(new AnnotationHandlerMapping(packageName)); - handlerAdapters.addAdapter(new ControllerHandlerAdapter()); - handlerAdapters.addAdapter(new AnnotationHandlerAdapter()); - handlerMappings.initialize(); - } - - @Override - protected void service(final HttpServletRequest request, final HttpServletResponse response) - throws ServletException, IOException { - final String requestURI = request.getRequestURI(); - log.debug("Method : {}, Request URI : {}", request.getMethod(), requestURI); - try { - final Object handler = handlerMappings.getHandler(request); - final ModelAndView modelAndView = handlerAdapters.handle(handler, request, response); - final View view = modelAndView.getView(); - view.render(Collections.emptyMap(), request, response); - } catch (HandlerNotExistException | HandlerAdapterNotExistException exception) { - setNotFound(request, response); - } catch (Exception e) { - throw new ServletException(e.getMessage()); - } - } - - private void setNotFound(final HttpServletRequest request, final HttpServletResponse response) - throws ServletException, IOException { - final RequestDispatcher requestDispatcher = request.getRequestDispatcher("404.jsp"); - response.setStatus(404); - requestDispatcher.forward(request, response); - } -} diff --git a/app/src/main/java/com/techcourse/DispatcherServletInitializer.java b/app/src/main/java/com/techcourse/DispatcherServletInitializer.java index 6e814cdd25..f974e0b83d 100644 --- a/app/src/main/java/com/techcourse/DispatcherServletInitializer.java +++ b/app/src/main/java/com/techcourse/DispatcherServletInitializer.java @@ -4,10 +4,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import web.org.springframework.web.WebApplicationInitializer; +import webmvc.org.springframework.web.servlet.mvc.tobe.DispatcherServlet; +import webmvc.org.springframework.web.servlet.mvc.tobe.handleradapter.AnnotationHandlerAdapter; +import webmvc.org.springframework.web.servlet.mvc.tobe.handlermapping.AnnotationHandlerMapping; /** - * Base class for {@link WebApplicationInitializer} - * implementations that register a {@link DispatcherServlet} in the servlet context. + * Base class for {@link WebApplicationInitializer} implementations that register a {@link DispatcherServlet} in the + * servlet context. */ public class DispatcherServletInitializer implements WebApplicationInitializer { @@ -17,12 +20,16 @@ public class DispatcherServletInitializer implements WebApplicationInitializer { @Override public void onStartup(final ServletContext servletContext) { + final String packageName = getClass().getPackageName(); final var dispatcherServlet = new DispatcherServlet(); + dispatcherServlet.addHandlerMapping(new AnnotationHandlerMapping(packageName)); + dispatcherServlet.addHandlerAdapter(new AnnotationHandlerAdapter()); + dispatcherServlet.addExceptionHandlerMapping(new NotFoundResponseHandler()); final var registration = servletContext.addServlet(DEFAULT_SERVLET_NAME, dispatcherServlet); if (registration == null) { throw new IllegalStateException("Failed to register servlet with name '" + DEFAULT_SERVLET_NAME + "'. " + - "Check if there is another servlet registered under the same name."); + "Check if there is another servlet registered under the same name."); } registration.setLoadOnStartup(1); diff --git a/app/src/main/java/com/techcourse/ManualHandlerMapping.java b/app/src/main/java/com/techcourse/ManualHandlerMapping.java deleted file mode 100644 index 8471cccdab..0000000000 --- a/app/src/main/java/com/techcourse/ManualHandlerMapping.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.techcourse; - -import com.techcourse.controller.LoginController; -import com.techcourse.controller.LoginViewController; -import com.techcourse.controller.LogoutController; -import com.techcourse.controller.RegisterViewController; -import jakarta.servlet.http.HttpServletRequest; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; -import webmvc.org.springframework.web.servlet.mvc.asis.ForwardController; -import webmvc.org.springframework.web.servlet.mvc.tobe.handlermapping.HandlerMapping; - -public class ManualHandlerMapping implements HandlerMapping { - - private static final Logger log = LoggerFactory.getLogger(ManualHandlerMapping.class); - - private static final Map controllers = new HashMap<>(); - - @Override - public void initialize() { - controllers.put("/", new ForwardController("/index.jsp")); - controllers.put("/login", new LoginController()); - controllers.put("/login/view", new LoginViewController()); - controllers.put("/logout", new LogoutController()); - controllers.put("/register/view", new RegisterViewController()); - - log.info("Initialized Handler Mapping!"); - controllers.keySet() - .forEach(path -> log.info("Path : {}, Controller : {}", path, - controllers.get(path).getClass())); - } - - @Override - public Optional getHandler(final HttpServletRequest request) { - final String requestURI = request.getRequestURI(); - log.debug("Request Mapping Uri : {}", requestURI); - - if (controllers.containsKey(requestURI)) { - return Optional.of(controllers.get(requestURI)); - } - return Optional.empty(); - } -} diff --git a/app/src/main/java/com/techcourse/NotFoundResponseHandler.java b/app/src/main/java/com/techcourse/NotFoundResponseHandler.java new file mode 100644 index 0000000000..7e4c6149dc --- /dev/null +++ b/app/src/main/java/com/techcourse/NotFoundResponseHandler.java @@ -0,0 +1,33 @@ +package com.techcourse; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.Objects; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.mvc.tobe.exceptionhandlermapping.ExceptionHandler; +import webmvc.org.springframework.web.servlet.view.JspView; + +public class NotFoundResponseHandler implements ExceptionHandler { + + private static final int NOT_FOUND_STATUS_CODE = 404; + + private String viewName; + + @Override + public void initialize() { + this.viewName = "404.jsp"; + } + + @Override + public boolean isHandleable(final HttpServletRequest request, final HttpServletResponse response) { + return response.getStatus() == NOT_FOUND_STATUS_CODE; + } + + @Override + public ModelAndView handle(final HttpServletRequest request) { + if (Objects.nonNull(viewName)) { + return new ModelAndView(new JspView(viewName)); + } + throw new UnsupportedOperationException(); + } +} diff --git a/app/src/main/java/com/techcourse/controller/HomeController.java b/app/src/main/java/com/techcourse/controller/HomeController.java new file mode 100644 index 0000000000..3f94161106 --- /dev/null +++ b/app/src/main/java/com/techcourse/controller/HomeController.java @@ -0,0 +1,20 @@ +package com.techcourse.controller; + +import context.org.springframework.stereotype.Controller; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.view.JspView; + +@Controller +public class HomeController { + + private static final JspView INDEX_PAGE_VIEW = new JspView("/index.jsp"); + + @RequestMapping(value = "/", method = RequestMethod.GET) + public ModelAndView home(final HttpServletRequest request, final HttpServletResponse response) { + return new ModelAndView(INDEX_PAGE_VIEW); + } +} diff --git a/app/src/main/java/com/techcourse/controller/LoginController.java b/app/src/main/java/com/techcourse/controller/LoginController.java index 0428fe109e..f2a08115c4 100644 --- a/app/src/main/java/com/techcourse/controller/LoginController.java +++ b/app/src/main/java/com/techcourse/controller/LoginController.java @@ -2,36 +2,42 @@ import com.techcourse.domain.User; import com.techcourse.repository.InMemoryUserRepository; +import context.org.springframework.stereotype.Controller; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.View; +import webmvc.org.springframework.web.servlet.view.JspView; -public class LoginController implements Controller { +@Controller +public class LoginController { private static final Logger log = LoggerFactory.getLogger(LoginController.class); + private static final View REDIRECT_UNAUTHORIZED_VIEW = new JspView("redirect:/401.jsp"); + private static final JspView REDIRECT_INDEX_PAGE_VIEW = new JspView("redirect:/index.jsp"); - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { + @RequestMapping(value = "/login", method = RequestMethod.POST) + public ModelAndView login(final HttpServletRequest req, final HttpServletResponse res) { if (UserSession.isLoggedIn(req.getSession())) { - return "redirect:/index.jsp"; + return new ModelAndView(REDIRECT_INDEX_PAGE_VIEW); } - return InMemoryUserRepository.findByAccount(req.getParameter("account")) - .map(user -> { - log.info("User : {}", user); - return login(req, user); - }) - .orElse("redirect:/401.jsp"); + .map(user -> checkUser(req, user)) + .orElse(new ModelAndView(REDIRECT_UNAUTHORIZED_VIEW)); } - private String login(final HttpServletRequest request, final User user) { + private ModelAndView checkUser(final HttpServletRequest request, final User user) { + log.info("User : {}", user); + if (user.checkPassword(request.getParameter("password"))) { final var session = request.getSession(); session.setAttribute(UserSession.SESSION_KEY, user); - return "redirect:/index.jsp"; + return new ModelAndView(REDIRECT_INDEX_PAGE_VIEW); } - return "redirect:/401.jsp"; + return new ModelAndView(REDIRECT_UNAUTHORIZED_VIEW); } } diff --git a/app/src/main/java/com/techcourse/controller/LoginViewController.java b/app/src/main/java/com/techcourse/controller/LoginViewController.java index 86ec26cdce..793ca15fbf 100644 --- a/app/src/main/java/com/techcourse/controller/LoginViewController.java +++ b/app/src/main/java/com/techcourse/controller/LoginViewController.java @@ -1,22 +1,31 @@ package com.techcourse.controller; +import context.org.springframework.stereotype.Controller; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.View; +import webmvc.org.springframework.web.servlet.view.JspView; -public class LoginViewController implements Controller { +@Controller +public class LoginViewController { + + private static final View LOGIN_PAGE_VIEW = new JspView("redirect:/login.jsp"); + private static final JspView REDIRECT_INDEX_PAGE_VIEW = new JspView("redirect:/index.jsp"); private static final Logger log = LoggerFactory.getLogger(LoginViewController.class); - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { + @RequestMapping(value = "/login/view", method = RequestMethod.GET) + public ModelAndView showLoginPage(final HttpServletRequest req, final HttpServletResponse res) { return UserSession.getUserFrom(req.getSession()) - .map(user -> { - log.info("logged in {}", user.getAccount()); - return "redirect:/index.jsp"; - }) - .orElse("/login.jsp"); + .map(user -> { + log.info("logged in {}", user.getAccount()); + return new ModelAndView(REDIRECT_INDEX_PAGE_VIEW); + }) + .orElse(new ModelAndView(LOGIN_PAGE_VIEW)); } } diff --git a/app/src/main/java/com/techcourse/controller/LogoutController.java b/app/src/main/java/com/techcourse/controller/LogoutController.java index 4642fd9450..f810cf8ff0 100644 --- a/app/src/main/java/com/techcourse/controller/LogoutController.java +++ b/app/src/main/java/com/techcourse/controller/LogoutController.java @@ -1,15 +1,22 @@ package com.techcourse.controller; +import context.org.springframework.stereotype.Controller; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.view.JspView; -public class LogoutController implements Controller { +@Controller +public class LogoutController { - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { + private static final JspView REDIRECT_HOME_PAGE_VIEW = new JspView("redirect:/"); + + @RequestMapping(value = "/logout", method = RequestMethod.POST) + public ModelAndView logout(final HttpServletRequest req, final HttpServletResponse res) { final var session = req.getSession(); session.removeAttribute(UserSession.SESSION_KEY); - return "redirect:/"; + return new ModelAndView(REDIRECT_HOME_PAGE_VIEW); } } diff --git a/app/src/main/java/com/techcourse/controller/RegisterController.java b/app/src/main/java/com/techcourse/controller/RegisterController.java index f0b2273784..9bdd31d764 100644 --- a/app/src/main/java/com/techcourse/controller/RegisterController.java +++ b/app/src/main/java/com/techcourse/controller/RegisterController.java @@ -13,13 +13,6 @@ @Controller public class RegisterController { - @RequestMapping(value = "/register", method = RequestMethod.GET) - public ModelAndView show(final HttpServletRequest req, final HttpServletResponse res) { - final String redirectViewName = "redirect:register.jsp"; - - return new ModelAndView(new JspView(redirectViewName)); - } - @RequestMapping(value = "/register", method = RequestMethod.POST) public ModelAndView registerUser(final HttpServletRequest req, final HttpServletResponse res) { final String redirectViewName = "redirect:/index.jsp"; @@ -30,9 +23,9 @@ public ModelAndView registerUser(final HttpServletRequest req, final HttpServlet private void saveUser(final HttpServletRequest request) { final var user = new User( - request.getParameter("account"), - request.getParameter("password"), - request.getParameter("email") + request.getParameter("account"), + request.getParameter("password"), + request.getParameter("email") ); InMemoryUserRepository.save(user); } diff --git a/app/src/main/java/com/techcourse/controller/RegisterViewController.java b/app/src/main/java/com/techcourse/controller/RegisterViewController.java index 136962136d..1618560c3c 100644 --- a/app/src/main/java/com/techcourse/controller/RegisterViewController.java +++ b/app/src/main/java/com/techcourse/controller/RegisterViewController.java @@ -1,13 +1,20 @@ package com.techcourse.controller; +import context.org.springframework.stereotype.Controller; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.view.JspView; -public class RegisterViewController implements Controller { +@Controller +public class RegisterViewController { - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { - return "/register.jsp"; + private static final JspView REGISTER_PAGE_VIEW = new JspView("/register.jsp"); + + @RequestMapping(value = "/register/view", method = RequestMethod.GET) + public ModelAndView showRegisterPage(final HttpServletRequest req, final HttpServletResponse res) { + return new ModelAndView(REGISTER_PAGE_VIEW); } } diff --git a/app/src/main/java/com/techcourse/controller/UserController.java b/app/src/main/java/com/techcourse/controller/UserController.java new file mode 100644 index 0000000000..d43b9fe157 --- /dev/null +++ b/app/src/main/java/com/techcourse/controller/UserController.java @@ -0,0 +1,32 @@ +package com.techcourse.controller; + +import com.techcourse.domain.User; +import com.techcourse.repository.InMemoryUserRepository; +import context.org.springframework.stereotype.Controller; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.view.JsonView; + +@Controller +public class UserController { + + private static final Logger log = LoggerFactory.getLogger(UserController.class); + + @RequestMapping(value = "/api/user", method = RequestMethod.GET) + public ModelAndView show(final HttpServletRequest request, final HttpServletResponse response) { + final String account = request.getParameter("account"); + log.debug("user id : {}", account); + + final ModelAndView modelAndView = new ModelAndView(new JsonView()); + final User user = InMemoryUserRepository.findByAccount(account) + .orElseThrow(); + + modelAndView.addObject("user", user); + return modelAndView; + } +} diff --git a/app/src/test/java/com/techcourse/controller/HomeControllerTest.java b/app/src/test/java/com/techcourse/controller/HomeControllerTest.java new file mode 100644 index 0000000000..7d26ebdc33 --- /dev/null +++ b/app/src/test/java/com/techcourse/controller/HomeControllerTest.java @@ -0,0 +1,37 @@ +package com.techcourse.controller; + +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("/ 경로 요청 테스트") +class HomeControllerTest extends UsingTomcatTest { + @DisplayName("GET 요청시 200 상태코드 와 함께 메인 페이지로 응답한다.") + @Test + void save() { + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + //given + final HttpGet httpGet = new HttpGet(tomcatUrl + "/"); + + //when + final HttpResponse response = httpClient.execute(httpGet); + + //then + final int actualStatusCode = response.getStatusLine().getStatusCode(); + final Header contentTypeHeader = response.getFirstHeader("Content-Type"); + + assertThat(actualStatusCode).isEqualTo(200); + assertThat(contentTypeHeader.getValue()).isEqualTo("text/html;charset=UTF-8"); + } catch (Exception e) { + Assertions.fail(); + e.printStackTrace(); + } + } +} diff --git a/app/src/test/java/com/techcourse/controller/LoginViewControllerTest.java b/app/src/test/java/com/techcourse/controller/LoginViewControllerTest.java index 3577d77b7b..e6408ed063 100644 --- a/app/src/test/java/com/techcourse/controller/LoginViewControllerTest.java +++ b/app/src/test/java/com/techcourse/controller/LoginViewControllerTest.java @@ -1,16 +1,16 @@ package com.techcourse.controller; -import static org.assertj.core.api.Assertions.assertThat; - import org.apache.http.Header; import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; + @DisplayName("/login/view 경로 요청 테스트") class LoginViewControllerTest extends UsingTomcatTest { @@ -19,10 +19,10 @@ class LoginViewControllerTest extends UsingTomcatTest { void save() { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { //given - final HttpPost httpPost = new HttpPost(tomcatUrl + "/login/view"); + final HttpGet httpGet = new HttpGet(tomcatUrl + "/login/view"); //when - final HttpResponse response = httpClient.execute(httpPost); + final HttpResponse response = httpClient.execute(httpGet); //then final int actualStatusCode = response.getStatusLine().getStatusCode(); diff --git a/app/src/test/java/com/techcourse/controller/LogoutControllerTest.java b/app/src/test/java/com/techcourse/controller/LogoutControllerTest.java index d39c7538ce..062a76bbae 100644 --- a/app/src/test/java/com/techcourse/controller/LogoutControllerTest.java +++ b/app/src/test/java/com/techcourse/controller/LogoutControllerTest.java @@ -1,7 +1,5 @@ package com.techcourse.controller; -import static org.assertj.core.api.Assertions.assertThat; - import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; @@ -10,6 +8,9 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("/logout 경로 요청 테스트") class LogoutControllerTest extends UsingTomcatTest { @DisplayName("요청시 302 상태코드와 / 로 리다이렉트한다.") diff --git a/app/src/test/java/com/techcourse/controller/RegisterControllerTest.java b/app/src/test/java/com/techcourse/controller/RegisterControllerTest.java index 0290d675db..14f3cad744 100644 --- a/app/src/test/java/com/techcourse/controller/RegisterControllerTest.java +++ b/app/src/test/java/com/techcourse/controller/RegisterControllerTest.java @@ -4,10 +4,8 @@ import java.util.ArrayList; import java.util.List; -import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; @@ -20,28 +18,6 @@ @DisplayName("/register 경로 요청 테스트") class RegisterControllerTest extends UsingTomcatTest { - @DisplayName("GET 로 요청을 보내 200 상태코드와 회원가입 페이지를 본문에 담아 반환한다.") - @Test - void show() { - try (CloseableHttpClient httpClient = HttpClients.createDefault()) { - //given - final HttpGet httpGet = new HttpGet(tomcatUrl + "/register"); - - //when - final HttpResponse response = httpClient.execute(httpGet); - - //then - final int actualStatusCode = response.getStatusLine().getStatusCode(); - final Header contentTypeHeader = response.getFirstHeader("Content-Type"); - - assertThat(actualStatusCode).isEqualTo(200); - assertThat(contentTypeHeader.getValue()).isEqualTo("text/html;charset=UTF-8"); - } catch (Exception e) { - Assertions.fail(); - e.printStackTrace(); - } - } - @DisplayName("회원가입 정보를 form-data 형식으로 본문에 담아 POST 요청시 유저를 저장하고 302 상태코드, Location 헤더에 /index.html 를 담아 응답한다.") @Test void save() { diff --git a/app/src/test/java/com/techcourse/controller/RegisterViewControllerTest.java b/app/src/test/java/com/techcourse/controller/RegisterViewControllerTest.java index d9834d9235..2f1846c5e4 100644 --- a/app/src/test/java/com/techcourse/controller/RegisterViewControllerTest.java +++ b/app/src/test/java/com/techcourse/controller/RegisterViewControllerTest.java @@ -1,16 +1,17 @@ package com.techcourse.controller; -import static org.assertj.core.api.Assertions.assertThat; - import org.apache.http.Header; import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("/register/view 경로 요청 테스트") class RegisterViewControllerTest extends UsingTomcatTest { @DisplayName("요청시 200 상태코드 와 함께 회원가입 페이지를 담아 응답한다.") @@ -18,10 +19,10 @@ class RegisterViewControllerTest extends UsingTomcatTest { void save() { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { //given - final HttpPost httpPost = new HttpPost(tomcatUrl + "/register/view"); + final HttpGet httpGet = new HttpGet(tomcatUrl + "/register/view"); //when - final HttpResponse response = httpClient.execute(httpPost); + final HttpResponse response = httpClient.execute(httpGet); //then final int actualStatusCode = response.getStatusLine().getStatusCode(); diff --git a/app/src/test/java/com/techcourse/controller/UserControllerTest.java b/app/src/test/java/com/techcourse/controller/UserControllerTest.java new file mode 100644 index 0000000000..8aa43cc8be --- /dev/null +++ b/app/src/test/java/com/techcourse/controller/UserControllerTest.java @@ -0,0 +1,36 @@ +package com.techcourse.controller; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import web.org.springframework.http.MediaType; + +@DisplayName("/api/user 경로에 대한 테스트") +class UserControllerTest extends UsingTomcatTest { + + @DisplayName("account 파라미터에 해당하는 유저를 조회하여 반환한다.") + @Test + void show() throws IOException { + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + //given + final HttpGet httpGet = new HttpGet(tomcatUrl + "/api/user?account=gugu"); + + //when + final HttpResponse response = httpClient.execute(httpGet); + + //then + final String contentType = response.getFirstHeader("Content-Type").getValue(); + final String body = EntityUtils.toString(response.getEntity()); + + assertThat(contentType).isEqualTo(MediaType.APPLICATION_JSON_UTF8_VALUE); + assertThat(body).isEqualTo("{\"account\":\"gugu\"}"); + } + } +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/ModelAndView.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/ModelAndView.java index ff8e24553f..bfb2147da9 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/ModelAndView.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/ModelAndView.java @@ -1,5 +1,7 @@ package webmvc.org.springframework.web.servlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -30,4 +32,9 @@ public Map getModel() { public View getView() { return view; } + + public void renderViewWithRequestAndResponse(final HttpServletRequest request, final HttpServletResponse response) + throws Exception { + view.render(model, request, response); + } } diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/Controller.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/Controller.java deleted file mode 100644 index bdd1fde780..0000000000 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/Controller.java +++ /dev/null @@ -1,8 +0,0 @@ -package webmvc.org.springframework.web.servlet.mvc.asis; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -public interface Controller { - String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception; -} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/ForwardController.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/ForwardController.java deleted file mode 100644 index cd8f1ef371..0000000000 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/ForwardController.java +++ /dev/null @@ -1,20 +0,0 @@ -package webmvc.org.springframework.web.servlet.mvc.asis; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import java.util.Objects; - -public class ForwardController implements Controller { - - private final String path; - - public ForwardController(final String path) { - this.path = Objects.requireNonNull(path); - } - - @Override - public String execute(final HttpServletRequest request, final HttpServletResponse response) { - return path; - } -} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/DispatcherServlet.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/DispatcherServlet.java new file mode 100644 index 0000000000..eae78525f7 --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/DispatcherServlet.java @@ -0,0 +1,105 @@ +package webmvc.org.springframework.web.servlet.mvc.tobe; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.mvc.tobe.exception.HandlerAdapterNotExistException; +import webmvc.org.springframework.web.servlet.mvc.tobe.exception.HandlerNotExistException; +import webmvc.org.springframework.web.servlet.mvc.tobe.exception.ViewRenderException; +import webmvc.org.springframework.web.servlet.mvc.tobe.exceptionhandlermapping.ExceptionHandler; +import webmvc.org.springframework.web.servlet.mvc.tobe.exceptionhandlermapping.ExceptionHandlers; +import webmvc.org.springframework.web.servlet.mvc.tobe.handleradapter.HandlerAdapter; +import webmvc.org.springframework.web.servlet.mvc.tobe.handleradapter.HandlerAdapters; +import webmvc.org.springframework.web.servlet.mvc.tobe.handlermapping.HandlerMapping; +import webmvc.org.springframework.web.servlet.mvc.tobe.handlermapping.HandlerMappings; + +public class DispatcherServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class); + + private final HandlerMappings handlerMappings = new HandlerMappings(); + private final HandlerAdapters handlerAdapters = new HandlerAdapters(); + private final ExceptionHandlers exceptionHandlers = new ExceptionHandlers(); + + @Override + public void init() { + handlerMappings.initialize(); + exceptionHandlers.initialize(); + } + + @Override + protected void service(final HttpServletRequest request, + final HttpServletResponse response) throws ServletException { + final String requestURI = request.getRequestURI(); + log.debug("Method : {}, Request URI : {}", request.getMethod(), requestURI); + try { + final Object handler = findHandler(request); + handleWithHandlerAndRequestAndResponse(handler, request, response); + } catch (HandlerNotExistException | HandlerAdapterNotExistException e) { + setNotFound(response); + handleWithExceptionHandler(request, response); + } + } + + private Object findHandler(final HttpServletRequest request) { + return handlerMappings.getHandler(request); + } + + private void handleWithHandlerAndRequestAndResponse(final Object handler, + final HttpServletRequest request, + final HttpServletResponse response) throws ServletException { + final HandlerAdapter handlerAdapter = getHandlerAdapterForHandler(handler); + try { + final ModelAndView modelAndView = handlerAdapter.handle(handler, request, response); + renderViewWithRequestAndResponse(modelAndView, request, response); + } catch (Exception e) { + throw new ServletException(e.getMessage()); + } + } + + private HandlerAdapter getHandlerAdapterForHandler(final Object handler) { + return handlerAdapters.getFirstHandleableAdapterForHandler(handler); + } + + private void renderViewWithRequestAndResponse(final ModelAndView modelAndView, + final HttpServletRequest request, + final HttpServletResponse response) { + try { + modelAndView.renderViewWithRequestAndResponse(request, response); + } catch (Exception e) { + throw new ViewRenderException(); + } + } + + public void setNotFound(final HttpServletResponse response) { + response.setStatus(404); + } + + private void handleWithExceptionHandler(final HttpServletRequest request, + final HttpServletResponse response) { + final Optional exceptionHandler = exceptionHandlers.getExceptionHandler(request, response); + + if (exceptionHandler.isPresent()) { + final ModelAndView modelAndView = exceptionHandler.get().handle(request); + renderViewWithRequestAndResponse(modelAndView, request, response); + } + } + + public void addHandlerAdapter(final HandlerAdapter handlerAdapter) { + handlerAdapters.addAdapter(handlerAdapter); + } + + public void addHandlerMapping(final HandlerMapping handlerMapping) { + handlerMappings.addHandlerMapping(handlerMapping); + } + + public void addExceptionHandlerMapping(final ExceptionHandler exceptionHandler) { + exceptionHandlers.addExceptionHandlerMapping(exceptionHandler); + } +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exception/ControllerClassNotFoundByNameException.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exception/ControllerClassNotFoundByNameException.java deleted file mode 100644 index 2d0c24ac14..0000000000 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exception/ControllerClassNotFoundByNameException.java +++ /dev/null @@ -1,8 +0,0 @@ -package webmvc.org.springframework.web.servlet.mvc.tobe.exception; - -public class ControllerClassNotFoundByNameException extends RuntimeException { - - public ControllerClassNotFoundByNameException(final String className) { - super("해당 이름의 클래스를 찾을 수 업습니다. 클래스명 : " + className); - } -} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exception/ViewRenderException.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exception/ViewRenderException.java new file mode 100644 index 0000000000..76aee985d4 --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exception/ViewRenderException.java @@ -0,0 +1,7 @@ +package webmvc.org.springframework.web.servlet.mvc.tobe.exception; + +public class ViewRenderException extends RuntimeException { + + public ViewRenderException() { + } +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exceptionhandlermapping/ExceptionHandler.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exceptionhandlermapping/ExceptionHandler.java new file mode 100644 index 0000000000..e71377b02a --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exceptionhandlermapping/ExceptionHandler.java @@ -0,0 +1,14 @@ +package webmvc.org.springframework.web.servlet.mvc.tobe.exceptionhandlermapping; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import webmvc.org.springframework.web.servlet.ModelAndView; + +public interface ExceptionHandler { + + void initialize(); + + boolean isHandleable(final HttpServletRequest request, final HttpServletResponse response); + + ModelAndView handle(final HttpServletRequest request); +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exceptionhandlermapping/ExceptionHandlers.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exceptionhandlermapping/ExceptionHandlers.java new file mode 100644 index 0000000000..ff36750313 --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/exceptionhandlermapping/ExceptionHandlers.java @@ -0,0 +1,29 @@ +package webmvc.org.springframework.web.servlet.mvc.tobe.exceptionhandlermapping; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class ExceptionHandlers { + + private final List exceptionHandlers = new ArrayList<>(); + + public void initialize() { + for (ExceptionHandler exceptionHandler : exceptionHandlers) { + exceptionHandler.initialize(); + } + } + + public void addExceptionHandlerMapping(final ExceptionHandler exceptionHandler) { + exceptionHandlers.add(exceptionHandler); + } + + public Optional getExceptionHandler(final HttpServletRequest request, + final HttpServletResponse response) { + return exceptionHandlers.stream() + .filter(handlerMapping -> handlerMapping.isHandleable(request, response)) + .findFirst(); + } +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handleradapter/AnnotationHandlerAdapter.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handleradapter/AnnotationHandlerAdapter.java index 22c22c49c8..dd00c39018 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handleradapter/AnnotationHandlerAdapter.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handleradapter/AnnotationHandlerAdapter.java @@ -14,7 +14,7 @@ public boolean isHandleable(final Object handler) { @Override public ModelAndView handle(final Object handler, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) throws Exception { if (!isHandleable(handler)) { throw new InvalidHandlerForHandlerAdapterException(handler, this); } diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handleradapter/ControllerHandlerAdapter.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handleradapter/ControllerHandlerAdapter.java deleted file mode 100644 index 7841768150..0000000000 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handleradapter/ControllerHandlerAdapter.java +++ /dev/null @@ -1,27 +0,0 @@ -package webmvc.org.springframework.web.servlet.mvc.tobe.handleradapter; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import webmvc.org.springframework.web.servlet.ModelAndView; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; -import webmvc.org.springframework.web.servlet.mvc.tobe.exception.InvalidHandlerForHandlerAdapterException; -import webmvc.org.springframework.web.servlet.view.JspView; - -public class ControllerHandlerAdapter implements HandlerAdapter { - - @Override - public boolean isHandleable(final Object handler) { - return handler instanceof Controller; - } - - @Override - public ModelAndView handle(final Object handler, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { - if (!isHandleable(handler)) { - throw new InvalidHandlerForHandlerAdapterException(handler, this); - } - final String viewName = ((Controller) handler).execute(request, response); - final JspView jspView = new JspView(viewName); - return new ModelAndView(jspView); - } -} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handleradapter/HandlerAdapters.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handleradapter/HandlerAdapters.java index d29d825a48..966cf05d2a 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handleradapter/HandlerAdapters.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handleradapter/HandlerAdapters.java @@ -1,10 +1,8 @@ package webmvc.org.springframework.web.servlet.mvc.tobe.handleradapter; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.List; -import webmvc.org.springframework.web.servlet.ModelAndView; +import java.util.stream.Collectors; import webmvc.org.springframework.web.servlet.mvc.tobe.exception.HandlerAdapterNotExistException; public class HandlerAdapters { @@ -14,14 +12,18 @@ public class HandlerAdapters { public void addAdapter(final HandlerAdapter handlerAdapter) { adapters.add(handlerAdapter); } - - public ModelAndView handle(final Object handler, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { - for (HandlerAdapter adapter : adapters) { - if (adapter.isHandleable(handler)) { - return adapter.handle(handler, request, response); - } + + public HandlerAdapter getFirstHandleableAdapterForHandler(final Object handler) { + final List handleableAdaptersForHandler = getHandleableAdaptersForHandler(handler); + if (handleableAdaptersForHandler.isEmpty()) { + throw new HandlerAdapterNotExistException(); } - throw new HandlerAdapterNotExistException(); + return handleableAdaptersForHandler.get(0); + } + + private List getHandleableAdaptersForHandler(final Object handler) { + return adapters.stream() + .filter(adapter -> adapter.isHandleable(handler)) + .collect(Collectors.toList()); } } diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handlermapping/AnnotationHandlerMapping.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handlermapping/AnnotationHandlerMapping.java index f9203d0622..1e7f68230b 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handlermapping/AnnotationHandlerMapping.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handlermapping/AnnotationHandlerMapping.java @@ -9,7 +9,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import org.reflections.Reflections; @@ -17,6 +16,7 @@ import org.slf4j.LoggerFactory; import web.org.springframework.web.bind.annotation.RequestMapping; import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.mvc.tobe.exception.HandlerNotExistException; import webmvc.org.springframework.web.servlet.mvc.tobe.exception.ReflectionInstantiationException; import webmvc.org.springframework.web.servlet.mvc.tobe.handleradapter.HandlerExecution; import webmvc.org.springframework.web.servlet.mvc.tobe.handleradapter.HandlerKey; @@ -103,14 +103,25 @@ private void addHandlerExecutions(final Object controller, final Method method) } @Override - public Optional getHandler(final HttpServletRequest request) { - final HandlerKey handlerKey = new HandlerKey( + public boolean isHandleable(final HttpServletRequest request) { + final HandlerKey handlerKey = parseRequestToHandlerKey(request); + return handlerExecutions.containsKey(handlerKey); + } + + @Override + public Object getHandler(final HttpServletRequest request) { + if (!isHandleable(request)) { + throw new HandlerNotExistException(); + } + + final HandlerKey handlerKey = parseRequestToHandlerKey(request); + return handlerExecutions.get(handlerKey); + } + + private HandlerKey parseRequestToHandlerKey(final HttpServletRequest request) { + return new HandlerKey( request.getRequestURI(), RequestMethod.valueOf(request.getMethod()) ); - if (handlerExecutions.containsKey(handlerKey)) { - return Optional.of(handlerExecutions.get(handlerKey)); - } - return Optional.empty(); } } diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handlermapping/HandlerMapping.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handlermapping/HandlerMapping.java index a3fcfc422b..506d259c24 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handlermapping/HandlerMapping.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handlermapping/HandlerMapping.java @@ -1,12 +1,12 @@ package webmvc.org.springframework.web.servlet.mvc.tobe.handlermapping; import jakarta.servlet.http.HttpServletRequest; -import java.util.Optional; public interface HandlerMapping { void initialize(); - Optional getHandler(final HttpServletRequest request); + boolean isHandleable(final HttpServletRequest request); + Object getHandler(final HttpServletRequest request); } diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handlermapping/HandlerMappings.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handlermapping/HandlerMappings.java index a8817e397b..5bd4572c24 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handlermapping/HandlerMappings.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/handlermapping/HandlerMappings.java @@ -3,7 +3,6 @@ import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import webmvc.org.springframework.web.servlet.mvc.tobe.exception.HandlerNotExistException; public class HandlerMappings { @@ -21,12 +20,11 @@ public void initialize() { } public Object getHandler(final HttpServletRequest request) { - for (HandlerMapping handlerMapping : handlerMappings) { - final Optional handler = handlerMapping.getHandler(request); - if (handler.isPresent()) { - return handler.get(); - } - } - throw new HandlerNotExistException(); + final HandlerMapping handlerMapping = handlerMappings.stream() + .filter(mapping -> mapping.isHandleable(request)) + .findFirst() + .orElseThrow(HandlerNotExistException::new); + + return handlerMapping.getHandler(request); } } diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java index 6d6be91fc1..aba42ba6ce 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java @@ -1,14 +1,57 @@ package webmvc.org.springframework.web.servlet.view; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; import java.util.Map; +import web.org.springframework.http.MediaType; import webmvc.org.springframework.web.servlet.View; public class JsonView implements View { + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static final int SINGLE_ELEMENT_SIZE = 1; + @Override - public void render(final Map model, final HttpServletRequest request, - HttpServletResponse response) throws Exception { + public void render(final Map model, + final HttpServletRequest request, + final HttpServletResponse response) throws IOException { + setResponseHeaderForJsonView(response); + writeJsonStringInResponse(model, response); + } + + private void setResponseHeaderForJsonView(final HttpServletResponse response) { + response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); + response.setCharacterEncoding(StandardCharsets.UTF_8.toString()); + } + + private void writeJsonStringInResponse(final Map model, + final HttpServletResponse response) throws IOException { + final String jsonString = getJsonString(model); + + try (final PrintWriter writer = response.getWriter()) { + writer.write(jsonString); + writer.flush(); + } + } + + private String getJsonString(Map model) { + if (model.size() == SINGLE_ELEMENT_SIZE) { + final Object[] modelElements = model.values().toArray(); + return writeValueAsString(modelElements[0]); + } + return writeValueAsString(model); + } + + private String writeValueAsString(Object object) { + try { + return OBJECT_MAPPER.writeValueAsString(object); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } } } diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java index 4e44760aa3..90f338d6ff 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java @@ -1,8 +1,10 @@ package webmvc.org.springframework.web.servlet.view; import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,8 +13,7 @@ public class JspView implements View { private static final Logger log = LoggerFactory.getLogger(JspView.class); - - public static final String REDIRECT_PREFIX = "redirect:"; + private static final String REDIRECT_PREFIX = "redirect:"; private final String viewName; public JspView(final String viewName) { @@ -20,8 +21,9 @@ public JspView(final String viewName) { } @Override - public void render(final Map model, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + public void render(final Map model, + final HttpServletRequest request, + final HttpServletResponse response) throws IOException, ServletException { if (viewName.startsWith(JspView.REDIRECT_PREFIX)) { response.sendRedirect(viewName.substring(JspView.REDIRECT_PREFIX.length())); return; diff --git a/mvc/src/test/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMappingTest.java b/mvc/src/test/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMappingTest.java index a814ab96a4..1b9aec34a9 100644 --- a/mvc/src/test/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMappingTest.java +++ b/mvc/src/test/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMappingTest.java @@ -30,7 +30,7 @@ void get() throws Exception { when(request.getRequestURI()).thenReturn("/get-test"); when(request.getMethod()).thenReturn("GET"); - final var handlerExecution = (HandlerExecution) handlerMapping.getHandler(request).get(); + final var handlerExecution = (HandlerExecution) handlerMapping.getHandler(request); final var modelAndView = handlerExecution.handle(request, response); assertThat(modelAndView.getObject("id")).isEqualTo("gugu"); @@ -45,7 +45,7 @@ void post() throws Exception { when(request.getRequestURI()).thenReturn("/post-test"); when(request.getMethod()).thenReturn("POST"); - final var handlerExecution = (HandlerExecution) handlerMapping.getHandler(request).get(); + final var handlerExecution = (HandlerExecution) handlerMapping.getHandler(request); final var modelAndView = handlerExecution.handle(request, response); assertThat(modelAndView.getObject("id")).isEqualTo("gugu");