Паттерн команда при проектировании web приложения. PRG

Пишу небольшое приложение servlets и jsp. В основе своей оно использует паттерн комманда в таком виде:

public class Controller extends HttpServlet {

private static final long serialVersionUID = 2423353715955164816L;

private static final Logger log = Logger.getLogger(Controller.class);

protected void doGet(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
    process(request, response);
}

protected void doPost(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
    process(request, response);
}

/**
 * Main method of this controller.
 */
private void process(HttpServletRequest request,
        HttpServletResponse response) throws IOException, ServletException {
    
    log.debug("Controller starts");

    // extract command name from the request
    String commandName = request.getParameter("command");
    log.trace("Request parameter: command --> " + commandName);

    // obtain command object by its name
    Command command = CommandContainer.get(commandName);
    log.trace("Obtained command --> " + command);

    // execute command and get forward address
    String forward = command.execute(request, response);
    log.trace("Forward address --> " + forward);

    log.debug("Controller finished, now go to forward address --> " + forward);

    // if the forward address is not null go to the address
    if (forward != null) {
        RequestDispatcher disp = request.getRequestDispatcher(forward);
        disp.forward(request, response);
    }
}}

Те команды которые отображают содержимое возвращают свой результат в таком виде:

return Path.PAGE__NON_AUTORIZED_USER_EXPOSITIONS;

Вопрос заключается в следующем - как используя паттерн комманда реализовать PRG(POST REDIRECT GET) если те команды которые создают сущности(Post запросом) должны делать sendRedirect на указанную jsp?


Ответы (1 шт):

Автор решения: Roman Konoval

Главное, что нужно сделать, это иметь возможность в process определить, что за результат вернула команда и в зависимости от этого возвращать результат по разному.

Как именно это делать - это уже дело вкуса. Вот несколько вариантов.

Форматированная строка

Подобное делает spring. Вместо Path.PAGE__NON_AUTORIZED_USER_EXPOSITIONS возвращайте "forward:" + Path.PAGE__NON_AUTORIZED_USER_EXPOSITIONS (для случая редиректа "redirect:" + url). process должен в зависимости от префикса делать либо forward либо редирект.

Удобно сделать вспомогательные функции:

public String forward(String to) {
   return String.format("forward:%s", to);
}
public String redirect(String to) {
   return String.format("redirect:%s", to);
}

чтобы в команде можно было писать:

return forward( Path.PAGE__NON_AUTORIZED_USER_EXPOSITIONS);

Типизированный результат

Вместо String возвращать объект своего класса.

interface CommandResult {
   String getResource();
}

class ForwardResult implements CommandResult {
  private String forwardResource;
  public ForwardResource(String resource) {
    this.forwardResource = resource;
  }
  public String getResource() {
    return forwardResource;
  }
}

class RedirectResult implements CommandResult {
  public String getResource() {
  }
}

process в зависимости от типа делает то, что нужно.

interface View {
 void render(CommandResult result, HttpServletRequest request,
        HttpServletResponse response);
}

class Controller {

  private static Map<Class<?>, View> views = new HashMap<>();

  static {
    views.put(ForwardResult.class, (result, request, response) -> {
      RequestDispatcher disp =
          request.getRequestDispatcher(result.getResource());
      disp.forward(request, response);
    });
    views.put(RedirectResult.class, (result, request, response) -> {
      response.sendRedirect(request.getContextPath() + result.getResource());
   });
  }

  void process(...) {
    ...
    Command command = CommandContainer.get(commandName);
  
    CommandResult result = command.execute(request, response);
  
    views.get(result.getClass()).render(result, request, response);
  }
}

В этом случае команда должна возвращать результат как-то так:

return new ForwardResult(Path.PAGE__NON_AUTORIZED_USER_EXPOSITIONS);
→ Ссылка