JavaScript(二) 延迟对象、跨域、AJAX示例

一、AJAX示例

AJAX全称为“Asynchronous JavaScript And XML”(异步JavaScript和XML) 是指一种创建交互式网页应用的开发技术、改善用户体验,实现无刷新效果。

1.1、优点

不需要插件支持

优秀的用户体验

提高Web程序的性能

减轻服务器和带宽的负担

1.2、缺点

浏览器对XMLHttpRequest对象的支持度不足,几乎所有浏览器现在都支持

破坏浏览器“前进”、“后退”按钮的正常功能,可以通过简单的插件弥补

对搜索引擎的支持不足

1.3、jQuery AJAX示例

在HTML5中对原生的AJAX核心对象XMLHttpRequest进行升级,也就是XHR2,功能更加强大。

jQuery对AJAX封装的非常好,这里以简单的商品管理为示例使用jQuery完成AJAX应用。

Product.java bean:

package com.gomall.bean;

/***
 * 产品
 * 
 * @author Administrator
 *
 */
public class Product {
    /** 编号 */
    private int id;
    /** 名称 */
    private String name;
    /** 价格 */
    private double price;
    /** 图片 */
    private String picture;
    /** 详细 */
    private String detail;

    @Override
    public String toString() {
        return "Product [id=" + id + ", name=" + name + ", price=" + price + ", picture=" + picture + ", detail="
                + detail + "]";
    }

    public Product(int id, String name, double price, String picture) {
        super();
        this.id = id;
        this.name = name;
        this.price = price;
        this.picture = picture;
    }

    public Product(int id, String name, double price, String picture, String detail) {
        super();
        this.id = id;
        this.name = name;
        this.price = price;
        this.picture = picture;
        this.detail = detail;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getPicture() {
        return picture;
    }

    public void setPicture(String picture) {
        this.picture = picture;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }
}

ProductService.java:
package com.gomall.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import com.gomall.bean.Product;

public class ProductService implements IProductService {
    public static ArrayList<Product> products;

    static {
        products = new ArrayList<>();
        Random random = new Random();
        for (int i = 1; i <= 10; i++) {
            Product product = new Product(i, "华为Mate9MHA-AL00/4GB RAM/全网通华为超级闪充技术双后摄设计" + random.nextInt(999), random.nextDouble() * 1000,
                    "pic(" + i + ").jpg", "产品详细");
            products.add(product);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.gomall.service.IProductService#getAll()
     */
    @Override
    public List<Product> getAll() {
        return products;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.gomall.service.IProductService#add(com.gomall.bean.Product)
     */
    @Override
    public boolean add(Product entity) {
        try {
            entity.setId(products.size() + 1);
            entity.setPicture("pic(" + entity.getId() + ").jpg"); // uploadify
                                                                    // 上传图片
            products.add(entity);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.gomall.service.IProductService#findById(int)
     */
    @Override
    public Product findById(int id) {
        for (Product product : products) {
            if (product.getId() == id) {
                return product;
            }
        }
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.gomall.service.IProductService#deleteById(int)
     */
    @Override
    public boolean deleteById(int id) {
        try {
            Product product = findById(id);
            if (product != null) {
                products.remove(product);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
}

ProductAction.java:
package com.gomall.action;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

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 org.codehaus.jackson.map.ObjectMapper;

import com.gomall.bean.Product;
import com.gomall.service.IProductService;
import com.gomall.service.ProductService;

@WebServlet("/Product")
public class ProductAction extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public ProductAction() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        /*模拟网络延时*/
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        String act = request.getParameter("act");
        IProductService productService = new ProductService();
        /**用于序列化json*/
        ObjectMapper mapper = new ObjectMapper();
        PrintWriter out=response.getWriter();
        
        if (act.equals("getAll")) {
            String json = mapper.writeValueAsString(productService.getAll());
            out.append(json);
        } else if (act.equals("area")) {
            String callback=request.getParameter("callback");
            out.append(callback+"('"+new Date()+"')");
        } else if (act.equals("getJSONP")) {
            String callback=request.getParameter("callback");
            String json = mapper.writeValueAsString(productService.getAll());
            out.append(callback+"("+json+")");
        } else if (act.equals("getAllCORS")) {
            /**向响应的头部中添加CORS信息*/
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.addHeader("Access-Control-Allow-Methods", "GET,POST");
            
            String json = mapper.writeValueAsString(productService.getAll());
            out.append(json);
        } else if(act.equals("del")){
            /**向响应的头部中添加CORS信息*/
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.addHeader("Access-Control-Allow-Methods", "GET,POST");
            int id=Integer.parseInt(request.getParameter("id"));
            String json = mapper.writeValueAsString(productService.deleteById(id));
            out.append(json);
        }
        else if(act.equals("add")){
            /**向响应的头部中添加CORS信息*/
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.addHeader("Access-Control-Allow-Methods", "GET,POST");
            String name=request.getParameter("name");
            double price=Double.parseDouble(request.getParameter("price"));
            String detail=request.getParameter("detail");
            Product entity=new Product(0, name, price, "",detail);
            String json = mapper.writeValueAsString(productService.add(entity));
            out.append(json);
        }
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

客户端跨域调用:
<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>AJAX</title>
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
        <style type="text/css">
            @CHARSET "UTF-8";
            * {
                margin: 0;
                padding: 0;
                font-family: microsoft yahei;
                font-size: 14px;
            }
            
            body {
                padding-top: 20px;
            }
            
            .main {
                width: 90%;
                margin: 0 auto;
                border: 1px solid #777;
                padding: 20px;
            }
            
            .main .title {
                font-size: 20px;
                font-weight: normal;
                border-bottom: 1px solid #ccc;
                margin-bottom: 15px;
                padding-bottom: 5px;
                color: blue;
            }
            
            .main .title span {
                display: inline-block;
                font-size: 20px;
                background: blue;
                color: #fff;
                padding: 0 8px;
                background: blue;
            }
            
            a {
                color: blue;
                text-decoration: none;
            }
            
            a:hover {
                color: orangered;
            }
            
            .tab td,
            .tab,
            .tab th {
                border: 1px solid #777;
                border-collapse: collapse;
            }
            
            .tab td,
            .tab th {
                line-height: 26px;
                height: 26px;
                padding-left: 5px;
            }
            
            .abtn {
                display: inline-block;
                height: 20px;
                line-height: 20px;
                background: blue;
                color: #fff;
                padding: 0 5px;
            }
            
            .btn {
                height: 20px;
                line-height: 20px;
                background: blue;
                color: #fff;
                padding: 0 8px;
                border: 0;
            }
            
            .abtn:hover,
            .btn:hover {
                background: orangered;
                color: #fff;
            }
            
            p {
                padding: 5px 0;
            }
            
            fieldset {
                border: 1px solid #ccc;
                padding: 5px 10px;
            }
            
            fieldset legend {
                margin-left: 10px;
                font-size: 16px;
            }
            
            .pic {
                height: 30px;
                width: auto;
            }
            #divFrom{
                display: none;
            }
        </style>
    </head>

    <body>

        <div class="main">
            <h2 class="title"><span>商品管理</span></h2>
            <table border="1" width="100%" class="tab" id="tabGoods">
                <tr>
                    <th>编号</th>
                    <th>图片</th>
                    <th>商品名</th>
                    <th>价格</th>
                    <th>详细</th>
                    <th>操作</th>
                </tr>
            </table>
            <p style="color: red" id="message"></p>
            <p>
                <a href="#" class="abtn" id="btnSave">添加</a>
                <input type="submit" value="删除选择项" class="btn" />
            </p>
            <div id="divFrom">
            <form id="formPdt">
                <fieldset>
                    <legend>添加商品</legend>
                    <p>
                        <label for="name">
                    名称:
                </label>
                        <input type="text" name="name" id="name" />
                    </p>
                    <p>
                        <label for="price">
                    价格:
                </label>
                        <input type="text" name="price" id="price" />
                    </p>
                    <p>
                        <label for="detail">
                    详细:
                      </label>
                        <textarea id="detail" name="detail" cols="60"></textarea>
                    </p>
                </fieldset>
            </form>
            </div>
        </div>

        <link rel="stylesheet" type="text/css" href="js/artDialog6/ui-dialog.css" />
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="js/artDialog6/dialog-min.js" type="text/javascript" charset="utf-8"></script>

        <!--[if (IE 8)|(IE 9)]>
        <script src="js/jquery.transport.xdr.min.js" type="text/javascript" charset="utf-8"></script>
        <![endif]-->
        <script type="text/javascript">
            var app = {
                url: "http://localhost:8087/JavaScript001/", //提供服务的域名
                add: function() {
                    var d = dialog({
                    title: '添加商品',
                    content: $('#divFrom').html(),
                    okValue: '添加',
                    modal:true,
                    backdropOpacity:0.3,
                    ok: function() {
                        var that = this;
                        $.ajax({
                        type: "post",
                        data: $(".ui-dialog #formPdt").serialize() + "&act=add",
                        success: function(data) {
                            if(data) {
                                app.log("添加成功!");
                                app.loadAll();
                                that.close();
                            } else {
                                app.log("添加失败!");
                            }
                        }
                    });
                    return false;
                    },
                    cancelValue: '关闭',
                    cancel: function() {
                        alert('你点了取消按钮')
                    },
                    onclose:function(){
                        alert("关闭了");
                    }
                });

                d.show();
                },
                del: function() {
                    //closest离当前元素最近的td父元素
                    id = $(this).closest("td").data("id");
                    var that = $(this);
                    $.ajax({
                        type: "get",
                        data: {
                            "id": id,
                            "act": "del"
                        },
                        success: function(data) {
                            if(data) {
                                that.closest("tr").remove();
                                app.log("删除成功!");
                            } else {
                                app.log("删除失败!");
                            }
                        }
                    });
                },
                loadAll: function() {
                    $.ajax({
                        type: "get",
                        data: {
                            "act": "getAllCORS"
                        },
                        success: function(data) {
                            $("#tabGoods tr:gt(0)").remove();
                            $.each(data, function(index, obj) {
                                var tr = $("<tr/>"); //行

                                $("<td/>").html(obj.id).appendTo(tr); //编号

                                var imgTd = $("<td/>");
                                $("<img/>", {
                                    "src": app.url + "images/" + obj.picture,
                                    "class": "pic"
                                }).appendTo(imgTd); //图片
                                imgTd.appendTo(tr);

                                $("<td/>").html(obj.name).appendTo(tr);
                                $("<td/>").html(Math.ceil(obj.price)).appendTo(tr);
                                $("<td/>").html(obj.detail).appendTo(tr);
                                $("<td/>").html("<a href='#' class='abtn del'>删除</a>").data("id", obj.id).appendTo(tr);
                                $("#tabGoods").append(tr);
                            });
                        }
                    });
                },
                init: function() {
                    /*动态绑定删除事件*/
                    $("#tabGoods").on("click", "a.del", {}, app.del);
                    /*绑定添加事件*/
                    $("#btnSave").click(app.add);
                    /*设置全局AJAX默认值*/
                    $.ajaxSetup({
                        dataType: "json",
                        url: app.url + "Product?type=meat-and-filler&format=json",
                        beforeSend: app.ajaxBefore,
                        complete: app.clearMsg,
                        error: function(xhr, textStatus, errorThrown) {
                            app.log("错误" + textStatus + errorThrown);
                        }
                    });
                    this.loadAll();
                },
                clearMsg: function() {
                    this.box.remove();
                },
                ajaxBefore: function() {
                    this.box=dialog({
                        modal:true
                    });
                    this.box.show();
                },
                log: function(msg) {
                    $("#message").html(msg);
                }
            };

            app.init();
        </script>
    </body>

</html>

运行结果:

删除:


二、延迟对象(Deferred)

deferred对象就是jQuery1.5版以后新增加的回调函数解决方案。

2.0、方法作为参数传递

在静态语言如C#中可以使用委托将方法作为参数传递,C++中可以使用方法指针,因为JavaScript是动态语言,方法作为参数传递非常便捷,示例如下:

示例1:

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>

    <body>
        <script type="text/javascript">
            //将方法作为参数带给另一个方法
            //C# 委托
            //C++ 方法指针
            //Java 没有,匿名内部类,接口

            function handler(arr, f1) {
                for(var i = 0; i < arr.length; i++) {
                    arr[i] = f1(arr[i]);
                }
                return arr;
            }

            function add(n) {
                return n + 1;
            }

            function sub(n) {
                return n - 1;
            }

            var result = handler([1, 2, 3], function(n) {
                return n * 3;
            });
            console.log(result);
        </script>
    </body>

</html>

结果:

示例2:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script type="text/javascript">
            function hello(f){
                console.info("问好:");
                var x=100;
                f(x);
            };
            
            hello(function(n){
                console.log("Hello!"+n);
            });
            
            hello(function(n){
                console.log("你好!"+n);
            });
            
        </script>
    </body>
</html>

结果:

函数自动提升

在Javascript中定义一个函数,有两种写法:

  function foo() { }

  var foo = function () { }

两种写法完全等价。但是在解析的时候,前一种写法会被解析器自动提升到代码的头部,因此违背了函数应该先定义后使用的要求,所以建议定义函数时,全部采用后一种写法。

示例:

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>

    <body>
        <script type="text/javascript">
            console.log(typeof f1);
            f1();

            console.log(typeof f2); //undefined
            f2();
            var f2 = function() {
                console.log('f2');
            }

            //函数自动提升
            function f1() {
                console.info('f1');
            }
        </script>
    </body>

</html>

结果:

2.1、回调函数

先看一个示例:

首先,为什么要使用Deferred?


<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>回调</title>
    </head>
    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            var student;
            $.get("student.json",function(data){
                student=data;
            },"json");
            alert(student);
        </script>
    </body>
</html>

student.json文件:{"name":"tom","id":"01"}

运行结果:

因为AJAX是异步执行的,类似高级语言中的多线程,当发起ajax请求时会有网络延迟,而代码并没有在$.get的位置被阻塞,alert先执行,但数据并没有从远程获取到,所以结果是undefined。

其实初学者经常会犯这种错误,如:

function getStudentById(id){
                $.get("students.do",{"id":id},function(stu){
                    return stu;
                },"json");
            }

上面的代码是有问题的,原因如前面的示例是一样的。怎么解决,如果你认为是异步带来的问题,当然通过同步是可以解决的,如:
$.ajax({
                type:"get",
                url:"student.json",
                async:false,  /*非异步,同步*/
                success:function(data){
                    student=data;
                }
            });

结果:

如果将所有的ajax请求修改为同步的,则ajax的好处就大打折扣了,如果即要异步又要解决上面的问题,可以使用回调方法。

示例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>回调</title>
    </head>
    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
           /*callback是一个当ajax请求成功时的回调方法*/
            function getStudent(callback) {
                $.get("student.json", function(data) {
                    callback(data);
                }, "json");
            }
            
            /*调用getStudent函数,传入参数,参数也是一个函数*/
            getStudent(function(stu){
                alert(stu.id);
            });
            
            /*把一个方法作为参数传递给另一个方法可以认为是委托,如C++中的函数指针类似*/
            getStudent(function(stu){
                alert(stu.name);
            });
        </script>
    </body>
</html>

结果:

从这里看回调很完美,其实不然,实际开发中要复杂得多,如当第一个ajax请求完成才可以完成第二个,当第二个完成才可以完成第三个,可能最一个请求要等前面的所有请求都成功时才允许执行或才有条件执行,如

使用ajax编辑用户信息,先加载用户对象,再加载省,加载市,加县,可能代码会这样写:

$.get("url1",function(){
                $.get("url2",function(){
                    $.get("url3",function(){
                      ...
                   });
                });
            });

当回调越来越多,嵌套越深,代码可读性就会越来越差。如果注册了多个回调,那更是一场噩梦,幸好从jQuery1.5开始出现了延迟对象(deferred),可以解决这个问题。


.2、deferred.done

$.ajax()操作完成后,如果使用的是低于1.5.0版本的jQuery,返回的是XHR对象,你没法进行链式操作;如果高于1.5版,返回的是deferred对象,可以进行链式操作。


当延迟成功时调用一个函数或者数组函数,功能与原success类似。

语法:deferred.done(doneCallbacks[,doneCallbacks])

返回值:Deferred Object

该参数可以是一个函数或一个函数的数组。当延迟成功时,doneCallbacks被调用。回调执行是依照他们添加的顺序。一旦deferred.done()返回延迟对象,延迟对象的其它方法也可以链接到了这里,包括增加.done()方法。当延迟解决,doneCallbacks执行使用参数提供给 resolveresolveWith方法依照添加的顺序调用。

示例代码:
<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>延迟对象(deferred)</title>
    </head>

    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            console.log("使用方法一");
            $.get("student.json", "json").done(function(stu) {
                console.log(stu.id);
            }).done(function(stu) {
                console.log(stu.name);
            });

            console.log("使用方法二");
            $.get("student.json", "json").done(function(stu) {
                console.log(stu.id);
            }, function(stu) {
                console.log(stu.name);
            });
        </script>
    </body>

</html>


运行结果:

2.3、deferred.fail

语法:deferred.fail(failCallbacks[,failCallbacks])

返回值:Deferred Object

当延迟失败时调用一个函数或者数组函数,功能与原回调方法error类似。

该参数可以是一个函数或一个函数的数组。当延迟失败时,doneCallbacks被调用。回调执行是依照他们添加的顺序。一旦deferred.fail()返回延迟对象,延迟对象的其它方法也可以链接到了这里,包括增加.done()方法。当延迟解决,doneCallbacks执行使用参数提供给 resolveresolveWith方法依照添加的顺序调用。

示例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>延迟对象(deferred)</title>
    </head>
    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            $.get("stu.json", "json").done(function(stu) {
                console.log(stu.name);
            }).fail(function(xhr, status, errorThrown){
                alert("xhr:"+xhr+",status:"+status+",errorThrown:"+errorThrown);
            });
        </script>
    </body>
</html>

运行结果:

运行结果:

2.4、deferred.always

语法:deferred.always(alwaysCallbacks,[alwaysCallbacks])

返回值:Deferred Object

当递延对象是解决(成功, resolved)或拒绝(失败,rejected)时被调用添加处理程序,与回调方法complete类似。

示例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>延迟对象(deferred)</title>
    </head>
    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            $.get("student.j", "json").done(function(stu) {
                console.log(stu.name);
            }).fail(function(data, status, errorThrown){
                console.log("data:"+data+",status:"+status+",errorThrown:"+errorThrown);
            }).always(function(data, textStatus){
                console.log("ajax执行完成,完成状态:"+textStatus);
            });
        </script>
    </body>
</html>

运行结果

成功时:

失败时:

2.5、deferred.then

deferred.then(doneFilter [, failFilter ] [, progressFilter ])

添加处理程序被调用时,递延对象得到解决或者拒绝,一次指定多个事件。

所有三个参数(包括progressCallbacks ,在jQuery的1.7 )可以是一个单独的函数或一个函数的数组。 其中一个参数,也可以为空,如果没有该类型的回调是需要的。或者,使用.done()或.fail()仅设置doneCallbacks或failCallbacks。当递延解决,doneCallbacks被调用。若递延代替拒绝,failCallbacks被调用。回调按他们添加的顺序执行。一旦deferred.then返回延迟对象,延迟对象的其它方法也可以链接到了这里,包括增加.then()方法。

示例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>延迟对象(deferred)</title>
    </head>
    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            $.get("student.jsonx", "json").then(function(stu) {
                console.log(stu.name);
            }, function(data, status, errorThrown) {
                console.log("data:" + data + ",status:" + status + ",errorThrown:" + errorThrown);
            });
        </script>
    </body>
</html>

三、跨域

互联网上的主机由IP来标识,为了方便记忆,创建了域名系统.域名与IP对应,域名的作用是不用让你记复杂的IP地址,能唯一定位资源,URL的格式是协议://主机名.公司名称.机构类型.地域类型:端口/路径,如http://www.zhangguo.com.cn:8080/products/list.do?id=1#a1

3.1、什么是跨域

JavaScript同源策略的限制,A域名下的JavaScript无法操作B或是C域名下的对象,如下所示:

假设页面:http://store.company.com/index.html

假设页面:http://store.company.com/index.html


客户端代码d05.html,http://localhost:8087/jQuery601_JAVA/d05.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>6.4.9、跨域AJAX请求</title>
</head>
<body>
    <h2>6.4.9、跨域AJAX请求</h2>
    <h2 id="message"></h2>
    <button type="button" id="btnAjax">ajax请求</button>
    <script type="text/javascript" src="js/jQuery/jquery.min.js"></script>
    <script type="text/javascript">
        $("#btnAjax").click(function() {
            $.get("http://localhost:12833/Action/FindUserById.ashx",{"id":1001},function(data){
                log(data);
            },"text");
        });

        function log(msg) {
            $("#message")[0].innerHTML += msg + "<br/>";
        }
    </script>
</body>
</html>
另一个域下面一般处理程序,http://localhost:12833/Action/FindUserById.ashx:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace jQuery601_DotNet.Action
{
    /// <summary>
    /// 根据用户编号获得用户
    /// </summary>
    public class FindUserById : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            String name = "";
            int id = Convert.ToInt32(context.Request.Params["id"]);
            if (id == 1001)
            {
                name = "Mark";
            }
            else if (id == 1002)
            {
                name = "Jack";
            }
            context.Response.Write(name);
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

运行结果:

3.2、JSONP跨域

JSONP跨域是利用script脚本允许引用不同域下的js实现的,将回调方法带入服务器,返回结果时回调。

2.1、JSONP跨域原理

客户端:


<body>
        <script type="text/javascript">
            /*回调方法*/
            function show(data) {
                alert(data);
            }
        </script>
        <script src="http://localhost:8087/JavaScript001/Product?act=area&callback=show" type="text/javascript" charset="utf-8"></script>
    </body>

服务器:
 String callback=request.getParameter("callback");
            out.append(callback+"('"+new Date()+"')");

 

服务器返回一段javascript,通过指定的方法名调用。从图中可以看出,使用JSONP的形式调用已经不再是通过XMLHTTPRequest对象,而是同步调用。

3.3、jQuery使用JSONP跨域

在jQuery中内置了实现JSONP跨域的功能,如果指定为json类型,则会把获取到的数据作为一个JavaScript对象来解析,并且把构建好的对象作为结果返回。为了实现这个目的,他首先尝试使用JSON.parse()。如果浏览器不支持,则使用一个函数来构建。JSON数据是一种能很方便通过JavaScript解析的结构化数据。如果获取的数据文件存放在远程服务器上(域名不同,也就是跨域获取数据),则需要使用jsonp类型。使用这种类型的话,会创建一个查询字符串参数 callback=? ,这个参数会加在请求的URL后面。服务器端应当在JSON数据前加上回调函数名,以便完成一个有效的JSONP请求。如果要指定回调函数的参数名来取代默认的callback,可以通过设置$.ajax()的jsonp参数。

页面脚本:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>6.4.9、跨域AJAX请求</title>
</head>
<body>
    <h2>6.4.9、跨域AJAX请求</h2>
    <h2 id="message"></h2>
    <button type="button" id="btnAjax">ajax请求</button>
    <script type="text/javascript" src="js/jQuery/jquery.min.js"></script>
    <script type="text/javascript">
        $("#btnAjax").click(function() {
            $.get("http://localhost:12833/Action/FindUserById.ashx?callback=?",{"id":1001},function(data){
                log(data);
            },"jsonp");
        });

        function log(msg) {
            $("#message")[0].innerHTML += msg + "<br/>";
        }
    </script>
</body>
</html>

服务器一般处理程序:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace jQuery601_DotNet.Action
{
    /// <summary>
    /// FindUserById 的摘要说明
    /// </summary>
    public class FindUserById : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            String name = "";
            int id = Convert.ToInt32(context.Request.Params["id"]);
            if (id == 1001)
            {
                name = "Mark";
            }
            else if (id == 1002)
            {
                name = "Jack";
            }
            String callback = context.Request["callback"];
            context.Response.Write(callback+"('"+name+"')");
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

下面是跨域传递对象:
package com.gomall.action;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

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 org.codehaus.jackson.map.ObjectMapper;

import com.gomall.service.IProductService;
import com.gomall.service.ProductService;

@WebServlet("/Product")
public class Product extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public Product() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        String act = request.getParameter("act");
        IProductService productService = new ProductService();
        ObjectMapper mapper = new ObjectMapper();
        PrintWriter out=response.getWriter();
        
        if (act.equals("getAll")) {
            String json = mapper.writeValueAsString(productService.getAll());
            out.append(json);
        } else if (act.equals("area")) {
            String callback=request.getParameter("callback");
            out.append(callback+"('"+new Date()+"')");
        } else if (act.equals("getJSONP")) {
            String callback=request.getParameter("callback");
            String json = mapper.writeValueAsString(productService.getAll());
            out.append(callback+"("+json+")");
        } 
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>AJAX</title>
    </head>
    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            $.get("http://localhost:8087/JavaScript001/Product?callback=?",{act:"getJSONP"},function(data){
                $.each(data,function(index,obj){
                    $("<p/>").html(obj.name).appendTo("body");
                });
            },"jsonp");
        </script>
    </body>
</html>

运行结果:

在jQuery中如果使用JSONP只需要将返回数据类型设置为jsonp就可以了,但是这种方法只支持get请求,不支持post请求;请求是同步的;服务器返回数据要处理,要添加回调函数,麻烦。

3.4、跨域资源共享(CORS)

同源策略(same origin policy)的限制下非同源的网站之间不能发送 ajax 请求的。

w3c 提出了跨源资源共享CORS即Cross Origin Resource Sharing(跨域源资源共享),就是我们所熟知的跨域请求。

跨域资源共享(CORS)是一种网络浏览器的技术规范,它为Web服务器定义了一种方式,允许网页从不同的域访问其资源。
CORS与JSONP相比:
1、 JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
2、 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
3、 JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS。

CORS 将请求分为两类:简单请求和非简单请求:

3.4.1.简单请求

支持get/post/put/delete请求,例如返回Access-Control-Allow-Origin:*,但是不允许自定义header且会忽略cookies,且post数据格式有限制,只支持‘text/plain','application/x-www-urlencoded'and'multipart/form-data',其中’text/plain'默认支持,后面两种需要下面的预检请求和服务器协商。

简单请求对应该规则,因此对简单请求的定义为:

(1) 请求方法是以下三种方法之一:

HEAD
GET
POST
(2)HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

简单请求的部分响应头及解释如下:

Access-Control-Allow-Origin必含)- 不可省略,否则请求按失败处理。该项控制数据的可见范围,如果希望数据对任何人都可见,可以填写"*"。

Access-Control-Allow-Credentials(可选) – 该项标志着请求当中是否包含cookies信息,只有一个可选值:true(必为小写)。如果不包含cookies,请略去该项,而不是填写false。这一项与XmlHttpRequest2对象当中的withCredentials属性应保持一致,即withCredentials为true时该项也为true;withCredentials为false时,省略该项不写。反之则导致请求失败。

Access-Control-Expose-Headers(可选) – 该项确定XmlHttpRequest2对象当中getResponseHeader()方法所能获得的额外信息。通常情况下,getResponseHeader()方法只能获得如下的信息:

Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma


全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务