将通知消息设置为应在 sendRedirect 之后显示的请求属性

Set notification message as request attribute which should show after sendRedirect

我创建了两组 servlet:视图和 Controllers/Handlers

此处的通知是用户的状态消息。示例:"You have successfully updated blah..."

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

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

是否有好的设计实践可以帮助解决这个问题?对于 java w/o 处理这种特殊情况的框架,任何好的 link 到 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,我将无法显示状态消息。

您正在查找“flash scope”。

The flash scope is backed by a short living cookie which is associated with a data entry in the session scope. Before the redirect, a cookie will be set on the HTTP response with a value which is uniquely associated with the data entry in the session scope. After the redirect, the presence of the flash scope cookie will be checked and the data entry associated with the cookie will be removed from the session scope and be put in the request scope of the redirected request. Finally the cookie will be removed from the HTTP response. This way the redirected request has access to request scoped data which was been prepared in the initial request.

在简单的 Servlet 术语中,如下所示:

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

    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 过滤器进一步抽象。

使用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);
    }

}