如何避免刷新JSP页面后重复数据?

如何避免刷新JSP页面后重复数据?,jsp,jakarta-ee,servlets,Jsp,Jakarta Ee,Servlets,提交表单后,数据被发送到servlet并存储在数据库中。T 然后我使用request.getRequestDispatcher(“CTN/ListPage.jsp”).forward(请求,响应)列出ListPage.jsp页面上的数据,但如果我刷新同一页面,浏览器会在警告消息中告诉我数据将再次重新发送,然后,最后存储的数据将被复制,。。 经过大量的搜索,这似乎是一个常见的问题。因此,我通过更改以下内容来尝试RPG解决方案: request.getRequestDispatcher(“CTN/L

提交表单后,数据被发送到servlet并存储在数据库中。T 然后我使用
request.getRequestDispatcher(“CTN/ListPage.jsp”).forward(请求,响应)
列出ListPage.jsp页面上的数据,但如果我刷新同一页面,浏览器会在警告消息中告诉我数据将再次重新发送,然后,最后存储的数据将被复制,。。 经过大量的搜索,这似乎是一个常见的问题。因此,我通过更改以下内容来尝试RPG解决方案:

request.getRequestDispatcher(“CTN/ListPage.jsp”).forward(请求,响应)

response.sendRedirect(request.getContextPath()+“CTN/ListPage.jsp”)但我得到一个404错误。。。请求的资源()不可用

我怎样才能解决这个问题

更新:Servlet代码:

package com.CTN.controller;

import com.CTN.dao.MatiereDaoLocal;
import com.CTN.dao.SeanceDaoLocal;
import com.CTN.dao.SemestreDaoLocal;
import com.CTN.model.Matiere;
import com.CTN.model.Seance;
import java.io.IOException;
import java.util.List;
import javax.ejb.EJB;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 *
 * @author WORK
 */
@WebServlet(name = "NewSeanceAjouterServlet", urlPatterns = {"/NewSeanceAjouterServlet"})
public class NewSeanceAjouterServlet extends HttpServlet {


    @EJB
    private MatiereDaoLocal MatiereDao;
    @EJB
    private SeanceDaoLocal SeanceDao;
    @EJB
    private SemestreDaoLocal SemestreDao;

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {


        int matiereId = Integer.parseInt(request.getParameter("matiereId"));
        String seanceTitre = request.getParameter("seanceTitre");
        String seanceContenue = request.getParameter("seanceContenue");
        String seanceType = request.getParameter("seanceType");


        Matiere matiere = MatiereDao.getMatiere(matiereId);



        Seance nouveauSeance = new Seance();
        nouveauSeance.setSeanceTitre(seanceTitre);
        nouveauSeance.setSeanceContenue(seanceContenue);
        nouveauSeance.setSeanceType(seanceType);



        nouveauSeance.setMatiere(matiere);
        nouveauSeance.setSeanceTitre(seanceTitre);
        nouveauSeance.setSeanceContenue(seanceContenue);


        SeanceDao.addSeance(nouveauSeance);


        List<Seance> seances = SeanceDao.getAllSeanceByMatiereId(matiereId);
        List<Matiere> matieres = MatiereDao.getAllMatiereBySemestreId(matiere.getSemestre().getSemestreId());



        request.setAttribute("matieres", matieres); 
        request.setAttribute("seances", seances);

        response.sendRedirect("CTN/ListPage.jsp");

    }
package com.CTN.controller;
导入com.CTN.dao.MatiereDaoLocal;
导入com.CTN.dao.seansedaolocal;
导入com.CTN.dao.semestredolocal;
导入com.CTN.model.Matiere;
导入com.CTN.model.Seance;
导入java.io.IOException;
导入java.util.List;
导入javax.ejb.ejb;
导入javax.servlet.RequestDispatcher;
导入javax.servlet.ServletException;
导入javax.servlet.annotation.WebServlet;
导入javax.servlet.http.HttpServlet;
导入javax.servlet.http.HttpServletRequest;
导入javax.servlet.http.HttpServletResponse;
/**
*
*@作者作品
*/
@WebServlet(name=“NewSeanceAjouterServlet”,urlPatterns={”/NewSeanceAjouterServlet})
公共类NewSeanceAjouterServlet扩展了HttpServlet{
@EJB
私人马蒂埃雷道本地马蒂埃雷道;
@EJB
私人SeanceDao本地SeanceDao;
@EJB
私人半官方半官方半官方;
受保护的void processRequest(HttpServletRequest请求、HttpServletResponse响应)
抛出ServletException、IOException{
int-matiereId=Integer.parseInt(request.getParameter(“matiereId”);
字符串seanceTitre=request.getParameter(“seanceTitre”);
字符串seanceContenue=request.getParameter(“seanceContenue”);
字符串seanceType=request.getParameter(“seanceType”);
Matiere-Matiere=MatiereDao.getMatiere(matiereId);
新肖恩斯=新肖恩斯();
新用途。设置和测试(seanceTitre);
新用途。设置和内容(seanceContenue);
Nouveausence.setSeanceType(seanceType);
新用途。塞特马蒂埃(马蒂埃);
新用途。设置和测试(seanceTitre);
新用途。设置和内容(seanceContenue);
新潮;
List seances=SeanceDao.GetAllSeanceByMatierId(MatierId);
List matieres=MatiereDao.getAllMatiereBysemesterid(matiere.getSemestre().getSemestreId());
setAttribute(“matieres”,matieres);
request.setAttribute(“座位”,座位);
sendRedirect(“CTN/ListPage.jsp”);
}
JSP页面:


乳头


库斯 埃图德之旅 虔诚的 考官
您需要将用户重定向到列表页面

最好的做法是在发布后将用户重定向到GET URL。


由于sendRedirect接受相对URL,我认为您不应该将上下文路径添加到要重定向到的URL。错误可能是因为您正在重定向到一个不存在的URL。请尝试直接在浏览器中访问该URL,看看它是否有效。

您需要将用户重定向到列表页面

最好的做法是在发布后将用户重定向到GET URL。


由于sendRedirect接受相对URL,我认为您不应该将上下文路径添加到要重定向到的URL。错误可能是因为您正在重定向到一个不存在的URL。请尝试直接在浏览器中访问该URL,看看它是否工作。

此方法与您正在执行的操作不同-这是同步onizer令牌模式。此解决方案要求您添加一种机制来识别重新提交的第一个请求

其思想是:在呈现请求表单本身时使用隐藏令牌,并使用它从任何后续请求中标记出第一个请求

收到第一次提交后,您将收到该令牌。请使该令牌无效,以便您知道包含该令牌的任何未来请求都是重复请求。有了这些信息,您可以停止将处理逻辑写入数据库

令牌可以是随机数,也可以是时间戳

一般流程:

  • 用户点击您的URL

  • 它触发一个servlet,您可以在其中将令牌添加到会话中

    session.setAttribute("TOKEN", “12345");  // some random number
    
  • 将servlet重定向到JSP(成为用户的请求表单),然后在一个隐藏字段中准备令牌

    <input type=hidden name=TOKEN value="<%= session.getAttribute("TOKEN") %>" />
    

  • PS:您正在执行的是Post重定向Get(PRG)方法,这需要额外的网络行程,并且当刷新按下得太快以至于重定向尚未触发时,会有一个小的暴露。上述解决方案解决了这两个问题。

    此方法与您正在做的不同-这是同步器令牌模式。此解决方案要求您为identif添加一个机制y来自重新提交的第一个请求

    其思想是:在呈现请求表单本身时使用隐藏令牌,并使用它从任何后续请求中标记出第一个请求

    收到第一次提交后,您将收到该令牌。使该令牌无效
    <input type=hidden name=TOKEN value="<%= session.getAttribute("TOKEN") %>" />
    
    if (tokenFromRequest == tokenFromSession) {
        session.setAttribute("TOKEN", "INVALID"); // or null
        // do your database activities
    } else { 
        // this is a resubmission, do nothing. Simply redirect to display page.
    }