SpringMVC——对Ajax的处理(包含 JSON 类型)

Posted solverpeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMVC——对Ajax的处理(包含 JSON 类型)相关的知识,希望对你有一定的参考价值。

一、首先要搞明白的一些事情。

1.从客户端来看,需要搞明白:

(1)要发送什么样格式的 JSON 数据才能被服务器端的 SpringMVC 很便捷的处理,怎么才能让我们写更少的代码,如何做好 JSON 数据和实体之间的对应。

(2)如何组织这些发送的数据。

2.从服务器端来看,需要搞明白:

(1)SpringMVC 如何返回 JSON 数据。

(2)SpringMVC 如何处理请求的复杂数据。

3.$.ajax 的几个参数:

(1)contentType:

contentType: \'application/json;charset=utf-8\',作为请求头,用来告诉服务器消息的主体是序列化后的 JSON 字符串。除了低版本的 ie 浏览器外,各大浏览器都原生支持 JSON.stringify() 对对象进行序列化。

(2)dataType:预期服务器返回的数据类型。

4.SpringMVC 是如何处理 JSON 数据的

5.总体的思想:

(1)SpringMVC 能完成的,尽量借助于 SpringMVC,而不是我们手动的去解析。

(2)SpringMVC 解析不了的,尽量借助于第三方的 Jar 包来解析。

(3)SpringMVC 和 第三方 Jar 包解决不了的时候,我们再自己去解析。

 

二、想要搞明白第一个问题,前提是先要搞明白第一个问题:SpringMVC 是如何处理 JSON 数据的。

1.使用 HttpMessageConverter<T> 来处理  JSON 数据的。

Spring 的 HttpMessageConverter<T> 负责将请求信息转换为一个对象,将对象输出为响应信息。

2.API

(1)boolean canRead(Class<?> clazz, MediaType mediaType);

转换器是否可将请求信息转换为 clazz 类型的对象,同时支持指定的 MIME 类型,如: text/html,application/json 等。

(2)boolean canWrite(Class<?> clazz, MediaType mediaType);

转换器是否可以将 clazz 类型的对象写到响应中,响应支持的类型在 mediaType 中定义。

(3)List<MediaType> getSupportedMediaTypes();

改转换器支持的 MediaType 类型。

(4)T read(Class<? extends T> clazz, HttpInputMessage inputMessage);

将请求信息流转换为 clazz 类型的对象。

(5)void write(T t, MediaType contentType, HttpOutputMessage outputMessage)。

将 T 类型的对象写到响应输出流中,同时指定 MediaType。

3.实现类

3.从上图可以看出,Spring 默认支持使用 Jackson来处理 JSON 问题。添加 Jackson Jar 包后,来看 RequestMappingHadlerAdapter 装配的 HttpMessageConverter:

导入的 Jackson Jar 包:

4.具体的处理方法:

(1)使用 @RequestBody 和 HttpEntity<T> 对请求进行处理。

(2)使用 @ResponseBody 和 ResponseEntity<T> 对响应进行处理。

(3)@RequestBody 对处理方法的入参进行标注。

(4)@ResponseBody 对处理方法的签名进行标注。

(5)HttpEntity<T> 和 ResponseEntity<T> 作为处理方法的入参使用。

具体的使用方法会在下面例子中进行说明。

5.@RequestBody 和 @ResponseBody 是可以同时使用的。

 

三、上面简单介绍了 SpringMVC 是怎么处理 JSON 数据的,现在来看第二个问题:发送什么样格式的 JSON 数据才能被服务器端的 SpringMVC 很便捷的处理,这里主要指的是请求的 JSON 字符串和实体的映射。

以一个简单的实体为例:Person

/**
 * @author solverpeng
 * @create 2016-08-12-10:50
 */
public class Person {
    private String name;
    private Integer age;

    public Person() {
    }

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @NotBlank(message = "人名不能为空")
    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name=\'" + name + \'\\\'\' +
                ", age=" + age +
                \'}\';
    }
}
Person.java

(1)对于简单的一个Person 对象来说,我们甚至都不需要借助于 JSON 就可以完成请求的数据与实体之间的映射。

请求:

$("#testJson").click(function () {
    $.ajax({
        url: "testJson",
        type: "post",
        data: {
            name : "abc",
            age : "23"
        },
        success: function (result) {
            console.log(result);
        }
    });
});

handler 方法:

@RequestMapping("/testJson")
public Person testJson(Person person) {
    System.out.println("person:" + person);
    return person;
}

(2)对于Person数组来说,需要发送什么样的格式才能被 SpringMVC 直接处理?

请求:

$("#testJson6").click(function () {
    $.ajax({
        url: "testJson6",
        type: "post",
        data:\'[{ "name": "Brett", "age":"12" }, { "name": "Jason", "age":"23" }, { "name": "Elliotte", "age":"33" }]\',
        contentType: "application/json; charset=utf-8",
        success: function (result) {
            console.log(result);
        }
    });
});

handler 方法:

@RequestMapping("/testJson6")
public String testJson6(@RequestBody List<Person> persons) {
    System.out.println("persons:" + persons);
    return "success";
}

注意:

(1)需要指定 "contentType",同时需要注意的是:发送的请求数据不在 Form data 中,而是在 Request Payload 中。关于 [Request Payload] ,在后面说明。

(2)必须要指定 @RequestBody ,否则无法解析。

 

四、第三个问题:如何组织这些数据以及SpringMVC 如何处理这些数据,做好映射。

(1)说明:

上面说的两个例子,仅仅是最简单的一种形式。现在对其进行扩展,在四里,所说的 SpringMVC 如何处理这些数据,不仅仅指的是SpringMVC,也包括SpringMVC处理不了,使用第三方来处理,或者第三方处理不了,我自己来处理。

同时这里的数据也不仅仅指的 JSON 类型的数据。

(2)对于非表单的 Ajax 提交,这里只提供比较简单的一种方式。还是以上面的 Person 为例。

e1:

数据的组织与请求的发送:

var personList = [];
personList.push({name: "李四",age: "23"});
personList.push({name: "张三",age: "12"});
$("#testJson5").click(function () {
    $.ajax({
        type: "POST",
        url: "testJson5",
        data: JSON.stringify(personList),//将对象序列化成JSON字符串
        contentType: \'application/json;charset=utf-8\', //设置请求头信息
        success: function (data) {
        },
        error: function (res) {
        }
    });
});

handler 方法:

@RequestMapping("/testJson5")
public String testJson5(@RequestBody List<Person> persons) {
    System.out.println(persons);
    return "success";
}

(3)基于表单的 Ajax 提交。

提供一个序列化方法:

$.fn.serializeObject = function()
{
    var o = {};
    var a = this.serializeArray();
    $.each(a, function() {
        if (o[this.name] !== undefined) {
            if (!o[this.name].push) {
                o[this.name] = [o[this.name]];
            }
            o[this.name].push(this.value || \'\');
        } else {
            o[this.name] = this.value || \'\';
        }
    });
    return o;
};

还有一种序列化方式:

★单表单情况:

表单:

<form action="" method="post">
    First Name:<input type="text" name="firstName" maxlength="12" size="12"/> <br/>
    Last Name:<input type="text" name="lastName" maxlength="36" size="12"/> <br/>
    Gender:<br/>
    Male:<input type="radio" name="gender" value="1"/><br/>
    Female:<input type="radio" name="gender" value="0"/><br/>
    Favorite Food:<br/>
    Steak:<input type="checkbox" name="foods" value="Steak"/><br/>
    Pizza:<input type="checkbox" name="foods" value="Pizza"/><br/>
    Chicken:<input type="checkbox" name="foods" value="Chicken"/><br/>
    <textarea wrap="physical" cols="20" name="quote" rows="5">Enter your favorite quote!</textarea><br/>
    Select a Level of Education:<br/>
    <select name="education">
        <option value="Jr.High">Jr.High</option>
        <option value="HighSchool">HighSchool</option>
        <option value="College">College</option>
    </select><br/>
    Select your favorite time of day:<br/>
    <select size="3" name="tOfD">
        <option value="Morning">Morning</option>
        <option value="Day">Day</option>
        <option value="Night">Night</option>
    </select>

    <p><input type="submit"/></p>
</form>

对应的实体:

/**
 * @author solverpeng
 * @create 2016-08-16-11:14
 */
public class Student {
    private String firstName;
    private String lastName;
    private Integer gender;
    private List<String> foods;
    private String quote;
    private String education;
    private String tOfD;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    public List<String> getFoods() {
        return foods;
    }

    public void setFoods(List<String> foods) {
        this.foods = foods;
    }

    public String getQuote() {
        return quote;
    }

    public void setQuote(String quote) {
        this.quote = quote;
    }

    public String getEducation() {
        return education;
    }

    public void setEducation(String education) {
        this.education = education;
    }

    public String gettOfD() {
        return tOfD;
    }

    public void settOfD(String tOfD) {
        this.tOfD = tOfD;
    }

    @Override
    public String toString() {
        return "Student{" +
                "firstName=\'" + firstName + \'\\\'\' +
                ", lastName=\'" + lastName + \'\\\'\' +
                ", gender=" + gender +
                ", foods=" + foods +
                ", quote=\'" + quote + \'\\\'\' +
                ", education=\'" + education + \'\\\'\' +
                ", tOfD=\'" + tOfD + \'\\\'\' +
                \'}\';
    }
}
Student.java

e1:使用 serializeObject()

序列化后的值:

JSON.stringify($(\'form\').serializeObject()):

{"firstName":"jack","lastName":"lily","gender":"1","foods":["Pizza","Chicken"],"quote":"hello hello","education":"Jr.High","tOfD":"Day"}

请求:

$(function() {
    $(\'form\').submit(function() {
        $.ajax({
            url : "testStudent",
            data : JSON.stringify($(\'form\').serializeObject()),
            contentType : "application/json;charset=utf-8",
            type : "post",
            success : function (result) {
                console.log(result);
            }
        });
        return false;
    });
});

e11:SpringMVC自身进行处理

handler 方法:

@RequestMapping("/testStudent")
public String testStudent(@RequestBody Student student) {
    System.out.println(student);
    return "success";
}

e12:引入第三方 Jar 包进行处理。

准备:

导入 sl4j jar 包,同时添加 JsonUtil 工具类。

public final class JsonUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(JsonUtil.class);

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    /**
     * 将 POJO 转换为 JSON
     */
    public static <T> String toJson(T obj) {
        String json;

        try {
            json = OBJECT_MAPPER.writeValueAsString(obj);
        } catch(JsonProcessingException e) {
            LOGGER.error("convert POJO to JSON failure", e);
            throw new RuntimeException(e);
        }

        return json;
    }

    /**
     * 将 JSON 转换为 POJO
     */
    public static <T> T fromJson(String json, Class<T> type) {
        T pojo;
        try {
            pojo = OBJECT_MAPPER.readValue(json, type);
        } catch(IOException e) {
            LOGGER.error("convert JSON to POJO failure", e);
            throw new RuntimeException(e);
        }
        return pojo;
    }

}
JsonUtil.java

后端处理:

@RequestMapping("/testStudent")
public String testStudent(@RequestBody String inputBody) {
    Student student = JsonUtil.fromJson(inputBody, Student.class);
    System.out.println(student);
    return "success";
}

都可以正常打印 Student 对象。

e2:使用 serialize()

序列化后的值:

$(\'form\').serialize():

firstName=jack&lastName=lily&gender=1&foods=Pizza&foods=Chicken&quote=hello+hello&education=Jr.High&tOfD=Day

请求:

$(function() {
    $(\'form\').submit(function() {
        $.ajax({
            url : "testStudent",
            data : $(\'form\').serialize(),
            type : "post",
            success : function (result) {
                console.log(result);
            }
        });
        return false;
    });
});

handler 方法:

@RequestMapping("/testStudent")
public String testStudent(Student student) {
    System.out.println(student);
    return "success";
}

可以正常打印 Student 对象。

e1 和 e2 对比说明:

e1提交的 JSON 数据,e2 提交的不是 JSON 格式的数据。e1 的请求参数存放在 [Request Payload] 中,而 e2 的请求参数存放在 Form Data 中。

★单表单复杂数据

表单还是上面的 Student 表单,但是在表单外增加了:

<span id="amount">33</span>

需求是:通过 Ajax 发送表单数据的同时,同时发送 "amount" 。

经过测试,我就直接说结论了,有兴趣的童鞋可以自行探索,有新的发现欢迎和我交流。

结论:

不能对这样的数据,指定 "contentType:application/json",否则后端SpringMVC或者第三方的Jar 包 不能进行自动的解析,增加了解析的复杂度,所以将 json 串传入后台,在后台进行解析。

e1:serializeObject()

请求:

$(function() {
    $(\'form\').submit(function() {
        $.ajax({
            url : "testStudent",
            data : {
                amount : $("#amount").text(),
                student : JSON.stringify($(\'form\').serializeObject())
            },
            type : "post",
            success : function (result) {
                console.log(result);
            }
        });
        return false;
    });
});

后端处理:使用第三方工具类进行解析

@RequestMapping("/testStudent")
public String testStudent(@RequestParam("student") String studentStr, String amount) {
    Student student = JsonUtil.fromJson(studentStr, Student.class);
    System.out.println("student:" + student);
    System.out.println("amount:" + amount);
    return "success";
}

可以正常打印。

e2:serialize()

请求:

$(function() {
    $(\'form\').submit(function() {
        $.ajax({
            url : "testStudent",
            data : {
                amount : $("#amount").text(),
                student : $(\'form\').serialize()
            },
            type : "post",
            success : function (result) {
                console.log(result);
            }
        });
        return false;
    });
});

Handler 方法:

e1:尝试让 SpringMVC 来解析:

@RequestMapping("/testStudent")
public String testStudent(@RequestParam("student") Student student, String amount) {
    System.out.println("student:" + student);
    System.out.println("amount:" + amount);
    return "success";
}

结果:请求无法到达 handler 方法

e2:

@RequestMapping("/testStudent")
public String testStudent(Student student, String amount) {
    System.out.println("student:" + student);
    System.out.println("amount:" + amount);
    return "success";
}

结果:请求可以正常到达目标 Handler 方法,但是无法映射 Student 对象。

方案:自己解析,编写自定义的类型转换器:

public class String2StudentConverter implements Converter<String, Student>{
    @Override
    public Student convert(String source) {
        return InjectUtil.convert2Obj(source, Student.class);
    }
}

这里我编写了一个通用的类型转换器:

用来转换形如: 

firstName=jack&lastName=lily&gender=1&foods=Steak&foods=Pizza&quote=Enter+your+favorite+quote!&education=Jr.High&tOfD=Day 到 Student 对象。

/**
 * @author solverpeng
 * @create 2016-08-22-17:37
 */
public final class InjectUtil<T> {
    private static final Logger LOGGER = LoggerFactory.getLogger(InjectUtil.class);

    public static <T> T converter2Obj(String source, Class<T> tClass) {
        T t = null;
        try {
            t = tClass.newInstance();
            Map<String, Object> params = new HashMap<String, Object>();
            if(source != null && source.length() > 0) {
                String[] fields = source.split("&");
                for(String field : fields) {
                    String[] fieldKeyValue = field.split("\\\\=");
                    String fieldKey = fieldKeyValue[0];
                    String fieldValue = fieldKeyValue[1];
                    if (params.containsKey(fieldKey)) {
                        Object keyValueRetrieved = params.get(fieldKey);
                        if (keyValueRetrieved instanceof String) {
                            ArrayList<String> values = new ArrayList<>();
                            values.add(keyValueRetrieved.toString());
                            values.add(fieldValue);
                            params.put(fieldKey, values);
                        } else {
                            ((ArrayList<String>) keyValueRetrieved).add(fieldValue);
                        }
                    } else {
                        params.put(fieldKey, fieldValue);
                    }
                }
            }
            BeanUtils.populate(t, params);
        } catch(InstantiationException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
            LOGGER.error("String convert to Bean failure!", e);
        }
        return t;
    }

}

不要忘记在 SpringMVC 中添加自定义的转换器。

e3:也可以在 handler 方法中来调用上面我编写的通用的类型转换器来完成解析。

@RequestMapping("/testStudent")
public String testStudent(@RequestParam("student") String studentStr, String amount) {
    System.out.println("studentStr:" + studentStr);
    System.out.println("amount:" + amount);
    return "success";
}

说明:对于复杂数据来说,我们借助不了 SpringMVC,只能借助于第三方,或是自己来编写解析器来解析。

★多表单一次提交

表单数据:

<form action="" method="post" id="form2">
    First Name:<input type="text" name="firstName" maxlength="12" size="12"/> <br/>
    Last Name:<input type="text" name="lastName" maxlength="36" size="12"/> <br/>
    Gender:<br/>
    Male:<input type="radio" name="gender" value="1"/><br/>
    Female:<input type="radio" name="gender" value="0"/><br/>
    &lt;%&ndash;Favorite Food:<br/>
    Steak:<input type="checkbox" name="foods" value="Steak"/><br/>
    Pizza:<input type="checkbox" name="foods" value="Pizza"/><br/>
    Chicken:<input type="checkbox" name="foods" value="Chicken"/><br/>&ndash;%&gt;
    <textarea wrap="physical" cols="20" name="quote" rows="5">Enter your favorite quote!</textarea><br/>
    Select a Level of Education:<br/>
    <select name="education">
        <option value="Jr.High">Jr.High</option>
        <option value="HighSchool">HighSchool</option>
        <option value="College">College</option>
    </select><br/>
    Select your favorite time of day:<br/>
    <select size="3" name="tOfD">
        <option value="Morning">Morning</option>
        <option value="Day">Day</option>
        <option value="Night">Night</option>
    </select>
    <p><input type="submit"/></p>
</form>
<form action="" method="post" id="form1">
    First Name:<input type="text"SpringMVC—对Ajax的处理(含 JSON 类型)

SpringMVC—对Ajax的处理(含 JSON 类型)

前端Ajax/JS/HTML+后端SpringMVC

SpringMVC—对Ajax的处理(含 JSON 类型)

前端AJAX传递数组给Springmvc接收处理

前端Ajax/JS/HTML+后端SpringMVC