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代码不用做更改

 

posted @ 2020-02-06 16:20  天戈  阅读(602)  评论(0编辑  收藏  举报