微服务架构中的数据交互

前端的快速发展导致现在的开发模式发生改变。前后台分离开发在开发中应用日趋广泛。那么如何能够让前后端程序员能够更好的配合,数据交互方式是我们必须要掌握的内容。本案例使用springboot作为后端开发环境,前端分别使用form,jquery.ajax ,axios来完成与后端的数据交互。

1. http协议

在浏览器上进行的数据交互实际上应用的http协议。具体的大家可以参照文档:https://developer.mozilla.org...

2. 同步数据交互

我们先从同步数据交互开始说起,这种方式会导致表单的完全提交,页面的整体刷新。form元素中的action表示后台处理接口,method表示提交方式,浏览器支持的的方式get,post。当然协议中还规定了一些其他的提交方式,比如head,put,但是需要通过post方式进行模拟才可应用,这里不做过多讲解。enctype表示表单数据的编码方式,其取值有三种,分别为'application/x-www-form-urlencoded','multipart/form-data','text/plain',默认方式为application/x-www-form-urlencoded。

  • application/x-www-form-urlencoded
    将表单数据编码为 key1=val1@key2=val2,如果值为非字符数字的其他格式将会被使用百分数进行编码,所以这种方式不适用于表单数据中有二进制文件的表单

clipboard.png

  • multipart/form-data
    主要用于表单数据中有二进制文件的表单也就是有附件上传的提交

clipboard.png

  • text/plain
    不对表单数据进行编码

下面是一段前端代码。注意input必须要包含name属性,否则无法为协议数据提供key。另,如果input可以为用户提供直接输入的区域,如单行文本输入框<input type="text">那么就不用为其提供value,如果提供了value,这个value值将是默认值。如果input不可以为用户提供直接输入的区域,而是通过点击来选择,如下拉菜单,单选按钮,复选按钮,那么必须为input元素提供value值。

                <form action="http://localhost:8888/users/saveOrUpdate" method="POST"  enctype="application/x-www-form-urlencoded">
                    <table>
                        <tbody>
                            <tr>
                                <td>姓名</td>
                                <td><input type="text" name="username"></td>
                            </tr>
                            <tr>
                                <td>密码</td>
                                <td><input type="password" name="password"></td>
                            </tr>
                            <tr>
                                <td>性别</td>
                                <td>
                                    <label for="gender_male">
                                        <input type="radio" name="gender" id="gender_male">男
                                    </label>
                                    <label for="gender_female">
                                        <input type="radio" name="gender" id="gender_female">女
                                    </label>
                                </td>
                            </tr>
                            <tr>
                                <td colspan="2" align="right">
                                    <input type="submit" value="保存">
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </form>

下面是相对应的后台代码,由于使用的是springboot,该controller默认返回的json格式数据:

    /**
     * 保存或修改用户信息
     * */
    @PostMapping(value="/saveOrUpdate",consumes= "application/x-www-form-urlencoded")
    public User saveOrUpdate(@ModelAttribute User user) {
        if(user.getId()!=null) {
            //修改操作
        } else {
            //保存操作
        }
        return user;
    }

这样即可完成数据的同步交互,数据交互完成后页面全局刷新,会将springmvc返回的user信息显示到浏览器页面中。

clipboard.png

另外一种情况也比较常见,批量数据删除的时候,我们需要向后台传递多个id值。前端代码如下,注意,由于是一个类型的值,所有三个input的name值是一样的。

<form action="http://localhost:8888/users/batchDelete" method="post" enctype="application/x-www-form-urlencoded">
<table>
    <tr>
        <td><input type="checkbox" value="1001" name="ids"></td>
        <td>张三</td>
    </tr>
    <tr>
        <td><input type="checkbox" value="1002" name="ids"></td>
        <td>李四</td>
    </tr>
    <tr>
        <td><input type="checkbox" value="1003" name="ids"></td>
        <td>王五</td>
    </tr>
    <tr>
        <td colspan="2" align="right">
            <input type="submit" value="确认删除">
        </td>
    </tr>
</table>
</form>

后台代码如下:

    /**
     * 批量删除用户信息
     * */
    @PostMapping(value="/batchDelete",consumes= "application/x-www-form-urlencoded")
    public String batchDelete(Long[] ids) {
        if(ids!=null){
            for(Long id : ids) {
                System.out.println(id);
            }
        }
        return "删除成功";
    }

实际上值被编码为 【ids=1001&ids=1002】

3. 数据的异步提交

前后台分离的开发模式中,数据异步提交更多一些。

3.1 jQuery提供的ajax API

使用jquery的ajax技术完成与后台之间的数据交互
下面是前端代码

        $(function(){
            //1. 为表单绑定提交事件,当点击提交按钮进行表单提交的时候触发
            $('#userForm').on('submit',function(){
                //3. 获取表单数据
                var username = $('input[name=username]').val();
                var password = $('input[name=password]').val();
                var gender = $('input[name=gender]').val();
                //4. 异步提交
                $.post('http://localhost:8888/users/saveOrUpdate',{
                    username:username,
                    password:password,
                    gender:gender
                },function(){
                    alert('保存成功!');
                })
                //2. 使用return false阻止表单的默认行为
                return false;
            });
        });

目前后台还没做什么改变,我们打开页面,输入表单信息,单击提交。出现了跨域异常。但是如果查看请求信息,发现请求是已经发出去了,并且jquery中的post函数对我们的数据进行了编码,并且是按照表单数据格式(POST默认提交编码格式application/x-www-form-urlencoded)进行编码。

clipboard.png

clipboard.png

接下来我们就解决后台跨域的问题。其实很简单,只需要为当前类或者当前函数添加@CrossOrigin注解即可。其余代码不变。

    /**
     * 保存或修改用户信息
     * */
    @CrossOrigin
    @ApiOperation(value="保存或修改用户信息",
            response=User.class)
    //@PostMapping(value="/saveOrUpdate",consumes= "application/x-www-form-urlencoded")
    @PostMapping(value="/saveOrUpdate",consumes= "application/x-www-form-urlencoded")
    public User saveOrUpdate(@ModelAttribute User user) {
        if(user.getId()!=null) {
            //修改操作
        } else {
            //保存操作
        }
        return user;
    }

我们在这里分析一下,如果使用jquery,默认ajax函数会将参数按照application/x-www-form-urlencoded进行编码。这是为什么呢?

clipboard.png

那么我们如何发送一个json格式的数据呢(到底发送什么格式的数据需要前后台开发者商定好,为了满足需求,我们尽量学会每种格式都会用)以下部分是前端代码:

                // 1. 调用底层的ajax函数
                $.ajax('http://localhost:8888/users/saveOrUpdate',{
                    method:'POST',
                    // 2. 手动对要发送的数据进行JSON格式的序列化
                    data:JSON.stringify({
                        username:username,
                        password:password,
                        gender:gender
                    }),
                    // 3. 指定参数类型为 json格式,但是注意,并不是说我指定了浏览器就会帮我们将数据转换为json,而是需要向第二步一样,我们自己对数据进行转化
                    contentType:'application/json',
                    success:function(){
                        alert('success');
                    }
                });

由于数据格式已经发生变化,所以后台代码也要跟着发生变化。如下

    @PostMapping(value="/saveOrUpdate",consumes= "application/json")
    public User saveOrUpdate(@RequestBody User user) {
        if(user.getId()!=null) {
            //修改操作
        } else {
            //保存操作
        }
        return user;
    }

测试发现传递的值为json格式,并且后台可以获取值。
clipboard.png
但是要注意,由于发送的数据格式为application/json,所以浏览器向后台发送了两个请求,一个OPTION类型的,一个POST类型的,后者才是真正的请求。

clipboard.png

好,继续往下讨论,如果是提交一个数组的话应该如何实现,这种需求在开发中也很常见。
前端代码如下


                <form id="deleteForm" >
                    <table>
                        <tr>
                            <td><input type="checkbox" value="1001" name="ids"></td>
                            <td>张三</td>
                        </tr>
                        <tr>
                            <td><input type="checkbox" value="1002" name="ids"></td>
                            <td>李四</td>
                        </tr>
                        <tr>
                            <td><input type="checkbox" value="1003" name="ids"></td>
                            <td>王五</td>
                        </tr>
                        <tr>
                            <td colspan="2" align="right">
                                <input type="submit" value="确认删除">
                            </td>
                        </tr>
                    </table>
                </form>
            //1. 绑定事件
            $('#deleteForm').on('submit',function(){
                //2. 获取要删除元素的ID,以数组的方式进行保存,例如 [1001,1002]
                var ids = $('#deleteForm input[name=ids]:checked').map(function(index,item){
                    return $(item).val();
                }).toArray();
                //3. 通过删除
                $.post('http://localhost:8888/users/batchDelete',{ids:ids},function(data){
                    console.log(data);
                    alert('success');
                });
                return false;
            });

后台保持不变

    /**
     * 批量删除用户信息
     * */
    @ApiOperation(value="批量删除用户信息",
            response=String.class)
    @PostMapping("/batchDelete")
    public String batchDelete(Long[] ids) {
        if(ids!=null){
            for(Long id : ids) {
                System.out.println(id);
            }
        } else {
            System.out.println("ids:"+ids);
        }
        return "删除成功";
    }

但是尝试之后发现代码没有报错,但是后台无法获取值
clipboard.png
这是因为ids为一个数组,ajax会对数组进行编码,为原先的key添加了中括号,成为了ids[],但是后台却是用ids作为参数来接受,这时肯定无法获取。

clipboard.png
查看jquery文档我们发现可以通过设定traditional来阻止上述行为

clipboard.png

clipboard.png

注意!这种方式是要慎用,因为traditional一旦设置为true,jquery将不会采用递归的方式序列化数据,这时如果我们传递数组中嵌入对象的这种数据将会出问题。这里还有其他更好的方式

  • 修改后台接受参数的方式

设置注解@RequestParam(value="ids[]"),表明要接受的参数的key就是'ids[]'

/**
     * 批量删除用户信息
     * */
    @ApiOperation(value="批量删除用户信息",
            response=String.class)
    @PostMapping("/batchDelete")
    public String batchDelete(@RequestParam(value="ids[]") Long[] ids) {
        if(ids!=null){
            for(Long id : ids) {
                System.out.println(id);
            }
        } else {
            System.out.println("ids:"+ids);
        }
        return "删除成功";
    }
  • 通过json来获取数据

在前端中声明参数类型为json,并且使用JSON.stringify()进行编码

            //1. 绑定事件
            $('#deleteForm').on('submit',function(){
                //2. 获取要删除元素的ID,以数组的方式进行保存,例如 [1001,1002]
                var ids = $('#deleteForm input[name=ids]:checked').map(function(index,item){
                    return $(item).val();
                }).toArray();
                //3. 通过删除
                //$.ajaxSettings.traditional = true;
                $.ajaxSetup({
                    contentType:'application/json'
                })
                $.post('http://localhost:8888/users/batchDelete',JSON.stringify(ids),function(data){
                    console.log(data);
                    alert('success');
                });
                return false;
            });

在后台代码中使用@requestBody来接受JSON格式参数

    /**
     * 批量删除用户信息
     * */
    @ApiOperation(value="批量删除用户信息",
            response=String.class)
    @PostMapping("/batchDelete")
    public String batchDelete(@RequestBody Long[] ids) {
        if(ids!=null){
            for(Long id : ids) {
                System.out.println(id);
            }
        } else {
            System.out.println("ids:"+ids);
        }
        return "删除成功";
    }

还有一种比较复杂的情况,当传递的数据是数组包含对象的话应该如何处理,数据格式如下:

[{id:1001,name:'terry'},{id:1002,name:'larry'}]

这里建议使用json来传递数据,前端代码如下:

                <form id="batchSaveForm" >
                    <table>
                        <tr>
                            <td>username:<input type="text" name="username"></td>
                            <td>realname:<input type="text" name="realname"></td>
                        </tr>
                        <tr>
                            <td>username:<input type="text" name="username"></td>
                            <td>realname:<input type="text" name="realname"></td>
                        </tr>
                        <tr>
                            <td>username:<input type="text" name="username"></td>
                            <td>realname:<input type="text" name="realname"></td>
                        </tr>
                        <tr>
                            <td colspan="2" align="right">
                                <input type="submit" value="确认保存">
                            </td>
                        </tr>
                    </table>
                </form>
                
            // 批量保存数据
            $('#batchSaveForm').on('submit',function(){
                var uns = $('#batchSaveForm :input[name=username]');
                var urs = $('#batchSaveForm :input[name=realname]');
                var arr = [];
                uns.each(function(index,item){
                    var obj = {};
                    obj.username = $(item).val();
                    arr.push(obj);
                });
                urs.each(function(index,item){
                    arr[index].realname = $(item).val();
                });
            
                $.ajaxSetup({
                    contentType:'application/json'
                })
                $.post('http://localhost:8888/users/batchSave',JSON.stringify(arr),function(){
                    alert('success');
                });
                
                return false;
            });

后台代码如下:

    /**
     * 保存多个用户信息
     * */
    @ApiOperation(value="保存或修改用户信息",
            response=List.class)
    @PostMapping(value="/batchSave")
    public User[] batchSave(@RequestBody User[] users) {
        if(users!=null) {
            for(User u : users) {
                System.out.println(u);
            }
        }else {
            System.out.println("null...");
        }
        return users;
    }

3.2 axios的异步数据交互

axios相对比jquery来说,它是一个更加纯粹的ajax框架,并且是基于promise机制的,使用axios需要注意是axios默认会将数据转换为JSON格式进行提交,而jquery默认是表单数据格式。如果想要改变需要设置headers属性。
异步提交对象前端代码:

            $('#userForm').on('submit',function(){
                //3. 获取表单数据
                var username = $('input[name=username]').val();
                var password = $('input[name=password]').val();
                var gender = $('input[name=gender]').val();

                //4. 异步提交
                axios.post('http://localhost:8888/users/saveOrUpdate',{
                    username:username,
                    password:password,
                    gender:gender
                }).then(function(){
                    alert('保存成功!');
                });

                //2. 使用return false阻止表单的默认行为
                return false;
            });

后台代码注意接收的是JSON格式的数据

    @ApiOperation(value="保存或修改用户信息",
            response=User.class)
    @PostMapping(value="/saveOrUpdate",consumes= "application/json")
    public User saveOrUpdate(@RequestBody User user) {
        if(user.getId()!=null) {
            //修改操作
        } else {
            //保存操作
        }
        return user;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值