zoukankan      html  css  js  c++  java
  • ajax请求跨域和表单重复提交解决方案

    跨域问题

    1.ajax请求什么时候会引发跨域问题?

    在当前域名请求网站中,默认不允许通过ajax请求发送其他域名。

    1.2:发生环境模拟

    当在a服务器的页面中发生ajax请求其他域(这里指http://eureka7002.com:6061/)的资源时,浏览器的控制台就会抛出异常信息

     发送的ajax请求

        $.ajax({
            type : "get",
            url : "http://eureka7002.com:6061/myServlet",
         dataType : "JSON" data: {
    "userName":"天雁"}, success : function(data) { alert(data); },error: function () { alert("请求错误") } });

    引发的错误信息

     这种情况之下,b服务器可以接受到a服务器的ajax请求以及数据的,但是无法成功的响应

    2.解决方案

    2.1在b服务器端添加响应头信息,表示支持所有网站的请求

    response.setHeader("Access-Control-Allow-Origin", "*"); 

    2.2使用JSONP

     在同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的,即一般的ajax是不能进行跨域请求的。但 img、iframe 、script等标签是个例外,这些标签可以通过src属性请求到其他服务器上的数据。利用 script标签的开放策略,我们可以实现跨域请求数据,当然这需要服务器端的配合。Jquery中ajax 的核心是通过 XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加 <script>标签来调用服务器提供的 js脚本。

    当我们正常地请求一个JSON数据的时候,服务端返回的是一串 JSON类型的数据,而我们使用 JSONP模式来请求数据的时候服务端返回的是一段可执行的 JavaScript代码。因为jsonp 跨域的原理就是用的动态加载 script的src ,所以我们只能把参数通过 url的方式传递,所以jsonp的 type类型只能是get!

    使用JSONP模式来请求数据的整个流程:

    客户端发送一个请求,规定一个可执行的函数名(这里就是 jQuery做了封装的处理,自动帮你生成回调函数并把数据取出来供success属性方法来调用,而不是传递的一个回调句柄),服务器端接受了这个 backfunc函数名,然后把数据通过实参的形式发送出去

    第一步:修改a服务器的ajax请求:

      $.ajax({
            type : "get",     //jsonp方式只支持get方式,就是写post他也会自动转换为get
            url : "http://eureka7002.com:6061/myServlet",
            dataType : "jsonp",   
            jsonp: "callback", //默认为callback,可修改,保持前后端一致就可以
            data: {"userName":"天雁"},
            success : function(data) {
                alert(data);
            },error: function () {
                alert("请求错误")
            }
        });

    第二步:修改b服务器端代码

    @WebServlet("/myServlet")
    public class MyServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
        }
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
           
            String userName = req.getParameter("userName");
            String callback = req.getParameter("callback");
            System.out.println("接受到的数据:"+userName);
            String json="成功响应";
            resp.getWriter().write(callback+"("+JSON.toJSONString(json)+")");
            //callback里响应的结果要是JSON类型的数据    
        }
    }
        

    2.3 使用HttpClinet进行请求转发

    这种解决方式的思路就是,不让ajax直接发送跨域请求,而是请求自己服务器的Servlet,在Servlet中使用HttpClient进行请求转发

    1.修改请求路径

     $.ajax({
            type : "get",
            url : "/myServlet",
            data: {"userName":"hhh"},
            success : function(data) {
                alert(data);
            },error: function () {
                alert("请求错误")
            }
        });

    2.添加Servlet

    package com.ty.servlet;
    
    import org.apache.http.client.methods.CloseableHttpResponse;
    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 javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    @WebServlet("/myServlet")
    public class MyServlet extends HttpServlet {
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String userName = req.getParameter("userName");
            //建立连接请求
            CloseableHttpClient httpClient = HttpClients.createDefault();
            //创建get请求
            HttpGet get=new HttpGet("http://eureka7002.com:6061/myServlet?userName="+userName);
            CloseableHttpResponse response = httpClient.execute(get);
            String result = EntityUtils.toString(response.getEntity());
            //将A工程响应结果给页面
            resp.getWriter().write(result);
    
        }
    }

    4.还可以通过Nginx和SpringCloud的zuul来解决跨域问题

    表单重复提交

    1.引发表单重复提交的事件

    网络延时

     在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交。

    重新刷新

    表单提交后用户点击【刷新】按钮导致表单重复提交

    点击浏览器的【后退】按钮回退到表单页面后进行再次提交

    用户提交表单后,点击浏览器的【后退】按钮回退到表单页面后进行再次提交

    2.解决方案

    2.1使用JS解决网络延迟情况(不推荐)

    <script type="text/javascript">
        var isFlag = false//表单是否已经提交标识,默认为false
    
        function submitFlag() {
    
            if (!isFlag) {
                isFlag = true;
                return true;
            } else {
                return false;
            }
    
        }
    </script>
    </head>
    
    <body>
        <form action="${pageContext.request.contextPath}/DoFormServlet"
            method="post" onsubmit="return submitFlag()">
            用户名:<input type="text" name="userName"> <input type="submit"
                value="提交" id="submit">
        </form>
    </body>

    思路就是当第一次点击提交按钮的将表单提交同时将判断值更改为true,知道再次点击提交按钮通过判断属性值就不可以提交了,

    也可在第一次点击完提交按钮之后将提交按钮禁用

    2.2使用Session保存Token令牌解决重新刷新

    使用js的方式只可以解决网络延迟的问题

    使用Token可以同时解决以上三种问题

    当然,如果你允许用户在提交完后退之后可以重新提交的话,你可以使用下面这种方式

    在表单页面中发送ajax请求,当加载这个页面的时候就会请求后台,后台会将一个Token值保存到Session中,然后绑定在表单中的隐藏域中,

    表单提交的时候token的值也会随着表单的提交而提交,

    之后就会在后台进行判断,如果是第一次提交的话就会通过验证,并情况Session,在点击刷新请求的时候就不会提交了

    token.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
    <html>
    <head>
        <title>防止表单重复提交</title>
    </head>
    <body>
    <form action="/FromServlet" method="post">
        <input type="text" name="userName">
        <input type="hidden" name="token" value=${token}>
        <input type="submit" value="提交">
    </form>
    <script type="text/javascript" src="js/jquery-1.8.3.min.js"></script>
    <script>
       $(function () {
            //生成令牌
            $.ajax({
                url:"TokenServlet",
                type:"POST",
                success:function (token) {
                    $("input[name=token]").val(token);
                }
            })
        })
    </script>
    </body>
    </html>

    ajax请求地址TokenServlet

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                        //生成令牌
                        String token = UUID.randomUUID().toString();
                        //令牌保存到session当中
                        request.getSession().setAttribute("sessionToken",token);
                        //响应
                        response.getWriter().write(token);
                    }

    表单提交地址

    package com.ty.servlet;
    
    import org.apache.http.client.methods.CloseableHttpResponse;
    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 javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    @WebServlet("/myServlet")
    public class MyServlet extends HttpServlet {
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String userName = req.getParameter("userName");
            //建立连接请求
            CloseableHttpClient httpClient = HttpClients.createDefault();
            //创建get请求
            HttpGet get=new HttpGet("http://eureka7002.com:6061/myServlet?userName="+userName);
            CloseableHttpResponse response = httpClient.execute(get);
            String result = EntityUtils.toString(response.getEntity());
            //将A工程响应结果给页面
            resp.getWriter().write(result);
    
        }
    }

    2.3使用Session保存Token令牌解决后退之后重新提交

    这种方式上面一种方式的区别就是,当你提交完成之后,点击后退再提交的话也会被当成重复提交

    这种情况就不用发送ajax请求了,访问地址也由直接访问页面变为访问Servlet之后转发到页面,但是将token值放入session中的时机也就变为了

    转发之前,在页面中使用el表达式获取token值绑定到隐藏域中

    直接访问TokenServler,只有刷新这个请求的时候,才会重新生成token

    package com.ty.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.UUID;
    
    @WebServlet("/TokenServlet")
    public class TokenServlet extends HttpServlet {
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("--------------------");
            //生成令牌
            String token = UUID.randomUUID().toString();
            //令牌保存到session当中
            req.getSession().setAttribute("token",token);
            req.getRequestDispatcher("token.jsp").forward(req, resp);
        }
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doPost(req, resp);
        }
    }

    页面上也不在需要ajax

    <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
    <html>
    <head>
        <title>防止表单重复提交</title>
    </head>
    <body>
    <form action="/FromServlet" method="post">
        <input type="text" name="userName">
        <input type="hidden" name="token" value=${token}>
        <input type="submit" value="提交">
    </form>

    后台FromServlet代码不用做更改

  • 相关阅读:
    HTTP协议
    在项目中使用模块
    将封装模块发布到NPM注册表
    package.json文件常用指令说明
    npm常用命令
    分享wifi热点
    Globals模块常用的方法和属性
    WebStorm2016.1.1永久破解
    Module
    解释器模式(Interpreter Pattern)
  • 原文地址:https://www.cnblogs.com/yjc1605961523/p/12269108.html
Copyright © 2011-2022 走看看