【问题标题】:Set notification message as request attribute which should show after sendRedirect将通知消息设置为请求属性,该属性应在 sendRedirect 之后显示
【发布时间】:2015-12-01 08:35:52
【问题描述】:

我创建了两组 servlet:视图和控制器/处理程序

  • 视图:执行简单的读取并将数据转发到JSPs
  • 控制器:执行数据库更新或插入并向 JSP 或视图类型 servlet 发送通知

这里的通知是给用户的状态消息。示例:“您已成功更新等等……”

如果我在控制器中使用requestDispatcher.forward() 并且用户通过确认重新发送来刷新(在控制器通过查看/jsp 控制之后)页面,则可能会执行重复操作

如果我使用response.sendRedirect(),如果不在会话中设置这些通知,我似乎无法发送任何通知

在这方面有没有好的设计实践?任何与处理此特定场景的 java w/o 框架的 MVC 的良好链接将不胜感激。

我没有使用 Spring 或 Struts - 只是普通的旧 HTTPServlets

示例 - 控制器:

public XServlet extends HttpServlet{
     public void processRequest(request, response) throws ...{ 
         //Do some stuff here
         if(success){
             setUserMessage("Hooray ur profile pic is uploaded!");
         } else {
             setUserMessage("Oh no! We couldn't upload that file its too biG!");
         }

         //Send the notification
         request.setAttribute("status", getUserMessage());
         request.getRequestDispatcher("editProfile.jsp").forward(request, response);
    }
}

这样做意味着如果用户尝试刷新页面,控件将再次传递给该控制器,并且可能会不必要地重复某些操作。

但是,如果我使用sendRedirect(),那么我无法在不使用会话属性或将其附加到 url 的情况下显示状态消息。

【问题讨论】:

  • 我似乎无法发送任何通知 这是什么意思?你想达到什么目的?给出一个具体的例子用例。
  • 嗨,我已经用更多细节阐明了 Q。这里的通知意味着最终用户的状态消息。
  • 您回答了自己的问题。发送重定向包括告诉浏览器发送新请求。因此,在处理新请求时,存储在第一个请求属性中的所有内容都会丢失。所以你确实需要 URL 中的会话属性或参数。
  • 我明白了,但现在状态存储在会话中,这意味着一旦显示我必须清除它。是否有更清洁的机制来实现这一目标?谢谢
  • MVC 框架有一些叫做“flash 属性”的东西,基本上就是你所描述的:当访问它们时,它们会从会话中删除。由于您决定不使用任何更高级别的框架,因此您还决定自己重新发明和重做这些框架实现的所有功能。

标签: jsp servlets redirect message


【解决方案1】:

使用bean设置请求范围消息

@SessionScoped
public class Message implements Serializable {

private static final long serialVersionUID = 1L;

public Message() {
}

@PostConstruct
private void prepareMessage() {
    messages = new HashMap<>(10);
}

private HashMap<String, String> messages;

public String add(String value) {
    if (messages.size() == 10) {
        messages.clear();
    }
    String key = generateRandomKey();
    messages.put(key, value);
    return key;
}

private String generateRandomKey() {
    SecureRandom random = new SecureRandom();
    return String.valueOf((random.nextInt(999999) + 1));
}

public String get(String key) {
    String msg= messages.get(key);
    messages.remove(key);
    return msg;
  }
    }

改写

protected void doPost(HttpServletRequest request, HttpServletResponse        response)
            throws ServletException, IOException {
 //operation
String mKey = msg.add("some msg.");
            response.sendRedirect("mastercontroller?viewID=" + entity + "&mKey=" + mKey);}

然后转发

 protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    String viewId = request.getParameter("viewID");
    System.out.println(viewId);
    String mKey = request.getParameter("mKey");

    if (mKey == null || mKey.isEmpty()) {
        request.getRequestDispatcher(viewId + ".jsp").forward(request, response);
    }else{
        String responseMsg=msg.get(mKey);
        if(responseMsg==null){
            request.getRequestDispatcher(viewId + ".jsp").forward(request, response);
            return;
        } 
        request.setAttribute("postback", "true");
        request.setAttribute("responseMsg",responseMsg);
        request.getRequestDispatcher(viewId + ".jsp").forward(request, response);
    }

}

【讨论】:

    【解决方案2】:

    您正在寻找“flash scope”。

    Flash 范围由一个短暂的 cookie 支持,该 cookie 与会话范围中的数据条目相关联。在重定向之前,将在 HTTP 响应上设置一个 cookie,其值与会话范围内的数据条目唯一关联。重定向后,将检查 flash 范围 cookie 的存在,并将与 cookie 关联的数据条目从会话范围中删除,并放入重定向请求的请求范围中。最后,cookie 将从 HTTP 响应中删除。这样,重定向的请求就可以访问在初始请求中准备的请求范围数据。

    简单的 Servlet 术语如下所示:

    1. 创建 flash 作用域并添加条目:

      String message = "Some message";
      
      // ...
      
      Map<String, Object> flashScope = new HashMap<>();
      flashScope.put("message", message);
      
    2. 在重定向之前,将其存储在由唯一 ID 键入的会话中,并将其设置为 cookie:

      String flashScopeId = UUID.randomUUID().toString();
      request.getSession().setAttribute(flashScopeId, flashScope);
      Cookie cookie = new Cookie("flash", flashScopeId);
      cookie.setPath(request.getContextPath());
      response.addCookie(cookie);
      
      // ...
      
      response.sendRedirect(request.getContextPath() + "/someservlet");
      
    3. 在下一个请求中,找到 flash cookie,将其映射回请求范围并删除 cookie:

      if (request.getCookies() != null) {
          for (Cookie cookie : request.getCookies()) {
              if ("flash".equals(cookie.getName())) {
                  Map<String, Object> flashScope = (Map<String, Object>) request.getSession().getAttribute(cookie.getValue());
      
                  if (flashScope != null) {
                      request.getSession().removeAttribute(cookie.getValue());
      
                      for (Entry<String, Object> entry : flashScope.entrySet()) {
                          request.setAttribute(entry.getKey(), entry.getValue());
                      }
                  }
      
                  cookie.setValue(null);
                  cookie.setMaxAge(0);
                  cookie.setPath(request.getContextPath());
                  response.addCookie(cookie);
              }
          }
      }
      

    这可以使用特定于上下文的辅助方法(如 setFlashAttribute())和带有响应包装器的 servlet 过滤器来进一步抽象。

    【讨论】:

    • BalusC 再次感谢您......您提供的信息量对我有很大帮助......
    • 这通常是用来显示这种临时消息并避免它们可索引的吗?您还有其他选择可以指点我吗?是否有人为了解决这个问题而使用forward 而不是redirect
    • 查看问题。提问者自己已经非常清楚地阐述了这一点。
    猜你喜欢
    • 2018-08-11
    • 2017-10-23
    • 2010-09-09
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 2018-07-17
    • 1970-01-01
    • 2018-11-18
    相关资源
    最近更新 更多