美文网首页程序员
SpringMVC数据绑定

SpringMVC数据绑定

作者: sunhaiyu | 来源:发表于2018-10-09 15:51 被阅读8次

    SpringMVC数据绑定

    基本类型

    对于基本类型是用int型还是Integer型?

    如果传入int型参数:

    1. 只能是int类型的,如果传入age="abc",如访问http://localhost/root?age=abc,会报400参数异常
    2. int型:key是必传的,如果不传入,如访问http://localhost/root,会报500内部错误异常。
    

    如果传入Integer类型,这是一个对int的包装类,本质上是一个对象:

    可以不传key,那么访问http://localhost/root,则age属性为null即`age: null`
    

    对于可能为空的数据,最好使用包装类型。还有一点,int型的默认值为0,Integer的默认值为null,如果数据库某字段允许NULL值,使用int型的话,存入的将是0而不是NULL。

    举个例子,有这么一个方法。

    @PostMapping("/getAge")
    public String getAge(int age) {
        return "age: " + age;
    }
    

    当我们不传age字段时,因为参数是基本数据类型,当然会报500内部错误;当表单传输的字段名为age时,会自动绑定到Controller方法中的同名参数。如果把参数名改一改,

    @PostMapping("/getAge")
    public String getAge(int a) {
        return "age: " + a;
    }
    

    当表单传入age=66,此时在方法中找不到同名参数,自动绑定就会失效。如果像上面一样用的是int型,则会报500错误。并给出很贴心的错误提示如下

    Optional int parameter 'age' is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type.
    

    大概意思是说不能将age转换成null值,因为它被声明为一个原始类型了。并提示我们可以使用其相应的包装类。好,按照提示改成Integer,再运行上面程序。

    不报错了,响应如下内容,说明数据绑定失效,虽然传入了age=66,但找不到同名参数,不能绑定到方法参数a上,所以参数a只好绑定null。而且,即使我们不传入age字段,也不会报错,响应内容依然和下面一样。(这就是推荐使用Integer而不使用int的原因之一)

    age: null
    

    可以使用@RequstParam将请求参数绑定到参数上,value属性明确指定表单字段的名称。

    @PostMapping("/getAge")
    public String getAge(@RequestParam(value = "age") Integer a) {
        return "age: " + a;
    }
    

    用value来明确指定表单传入的字段名,这个注解的意思是,将名为age字段值绑定到参数a上,使用@RequestParam不要求方法参数名和字段名一样,使得数据绑定时候更加灵活。注意当只有一个value属性时,可省略value =。该注解还有几个比较重要的属性

    • name:是value的别名,用哪个都可以
    • required:该字段是否必传,默认是true,如果不传会报错;改为false说明在前台不必传该子弹。
    • defaultValue:字段的默认值

    来试一下,

    @PostMapping("/getAge")
    public String getAge(@RequestParam(value = "age", required = false, defaultValue = "5") Integer a) {
        return "age: " + a;
    }
    

    当访问http://localhost:8080/getAge,由于不必传age字段,而且指定了age的默认值为5,所以响应是

    age: 5
    

    数组

    当传入的多个字段key都是同一个名称如下面的name,则会绑定到方法中的数组参数中。

    Snipaste_2018-10-06_17-40-00
    @PostMapping("/array")
    public String array(@RequestParam("name") String[] names) {
        StringBuilder sb = new StringBuilder();
        for (String name : names) {
            sb.append(name).append(" ");
        }
        return sb.toString();
    }
    

    响应结果如下

    abc def ghi 
    

    当然如果是GET方式请求,访问http://localhost/array?name=abc&name=def&name=ghi 也是可以的。

    SpringMVC可以将同一个字段key的多个值映射到一个数组中,那自然也能映射到List中。将参数String[] names改成List<String> names,完全没问题。

    对象以及多层级对象

    SpringMVC可以将表单的字段与Controller方法参数中的对象属性进行映射,前提是两者的名称和类型一致。

    比如表单中传入name=Jim&age=33,方法参数中的User对象也恰好有String name与Integer age属性,SpringMVC将完成字段 --> 对象属性的映射并将数据绑定到User对象的对应属性上。

    这里User类,有name和age字段,还有一个对象ContactInfo。

    package com.shy.springmvcbingding.domain;
    
    public class User {
        private String name;
        private Integer age;
        private ContactInfo contactInfo;
        // getter和setter方法省略
    }
    
    
    
    package com.shy.springmvcbingding.domain;
    
    public class ContactInfo {
        private String address;
        private String phone;
        // getter和setter方法省略
    }
    
    

    在Controller中,方法参数可以是对象

    // Controller被@RestController修饰
    @PostMapping("/user")
    public User getUser(User user) {
        return user;
    }
    

    如果我们传入以下参数

    Snipaste_2018-10-06_20-46-17

    响应输出为

    {
        "name": "Jim",
        "age": 22,
        "contactInfo": {
            "address": "China",
            "phone": "13345678910"
        }
    }
    

    SpringMVC将我们输入字段的类型、名称与方法参数中User对象中的属性一一对比,如果吻合,将会自动映射过去并封装成对象,如果某些子弹和对象的属性不能匹配,封装成的对象中该属性为null。比如上面不小心将contactInfo.address写成了contactInfo.addr,响应是下面这样的,可以看到contactInfo的address字段为null了。

    {
        "name": "Jim",
        "age": 33,
        "contactInfo": {
            "address": null,
            "phone": "13345678910"
        }
    }
    

    对于多层级的对象,如User中的ContactInfo类,该类又有address和phone两个属性。对于这种多层级的对象,需要使用.取到具体的属性。比如user.contactInfo.phone表示user实体中cantactInfo中的phone字段。因此在请求getUser方法时,若要将字段值注入到user中contactInfo的phone属性,需要传入contactInfo.phone

    拥有同属性的多个对象

    假如现在有一个Admin类,拥有和User一样的属性

    package com.shy.springmvcbingding.domain;
    
    /**
     * @author Haiyu
     * @date 2018/10/6 21:39
     */
    public class Admin {
        private String name;
        private Integer age;
        private ContactInfo contactInfo;
        // getter setter方法省略
    }
    
    

    同时新增一个方法

    @PostMapping("/userAndAdmin")
    public String getUser(User user, Admin admin) {
        return user.toString() + " "+ admin.toString();
    }
    

    方法参数中既有User又有Admin,且两者拥有一样的属性。SpringMVC是如何进行绑定的呢?

    User{name='Bob', age=22, contactInfo=ContactInfo{address='China', phone='13345678910'}} Admin{name='Bob', age=22, contactInfo=ContactInfo{address='China', phone='13345678910'}}
    

    可以看到,它不做区分的给User和Admin进行了数据绑定。假如我们想有区分的进行绑定,要怎么做呢?可以使用@InitBinder注解

    @InitBinder用于初始化WebDataBinder,在当前Controller类中有效。被@InitBinder注解的方法需要有WebDataBinder方法,且返回值一般是void,可以@RequstMapping方法调用之前,就对一些属性进行初始化。

    如下是使用@InitBinder的例子

    @InitBinder("user")
    public void initUser(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("user.");
    }
    
    @InitBinder("admin")
    public void initAdmin(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("admin.");
    }
    

    它有一个属性value,不指定的话,默认作用于当前Controller的所有表单属性和请求参数(可以认为是@ModelAttribute和@RequstParam),如果指定了value值,就像上面那样,那么只针对参数是value值的attribute和param生效。比如@InitBinder("user")只对@ModelAttribute User user有效,@InitBinder("admin")只对@ModelAttribute Admin admin有效。综上,这两个方法的意义在于给参数user、admin分别设置参数前缀user.admin.这样不同的init-binder可以作用与不同的request param或model attribute

    这下再来访问/userAndAdmin,可以看到我们给user.nameadmin.name赋予了不同的值

    Snipaste_2018-10-06_22-11-34

    有了前缀的区分,SpringMVC精准的将字段绑定到对应的类对象上了。

    User{name='Bob', age=22, contactInfo=null} Admin{name='Lucy', age=21, contactInfo=null}
    

    现在有个问题,如果我们不配置init-binder,参数也传上面那样的呢?

    User{name='null', age=null, contactInfo=null} Admin{name='null', age=null, contactInfo=null}
    

    由于没有给参数配置前缀,SpringMVC不认识user.name这样的属性了。

    当然如果Admin的属性和User不一样,比如name改成adminName,age改成adminAge,那么是无需配置init-binder的。就像下面那样

    Snipaste_2018-10-06_22-21-36

    响应是

    User{name='Lucy', age=21, contactInfo=null} Admin{adminName='Bob', adminAge=22, contactInfo=null}
    

    SpringMVC会将匹配的字段分别绑定到了User和Admin对象上。

    List、Set、Map

    List

    要将数据绑定到List<Integer>或者List<String>,其实是可以像下面这样写的。

    public String array(@RequestParam("name") List<String> names) {...}
    public String array(@RequestParam("age") List<Integer> ages) {...}
    

    在表单中只要提交相同的字段即可name=bob&name=jim&name=lucy

    但是数据要绑定到List<User>上,就不能这样写了因为User中还有其他属性。可以仿照上面User中的CantactInfo一样,用一个UserList类将List<User>包裹起来,对list某个元素的访问可以使用list[index]的形式。

    package com.shy.springmvcbingding.domain;
    
    import java.util.List;
    
    public class UserList {
        private List<User> users;
        // getter和setter省略
    }
    
    

    相应的将Controller中的方法改成下面这样

    @PostMapping("/list")
    public UserList userList(UserList userList) {
        return userList;
    }
    

    因为userList中的List<User>其属性名是users,所以users[0]表示第一个user,users[1]表示第二个user,以此类推。

    Snipaste_2018-10-07_09-36-56

    可以看到,在表单中传入字段时,我们故意跳过了users[1],会报空指针异常吗?

    {
        "users": [
            {
                "name": "vv",
                "age": 33,
                "contactInfo": null
            },
            {
                "name": null,
                "age": null,
                "contactInfo": null
            },
            {
                "name": "F",
                "age": 44,
                "contactInfo": null
            }
        ]
    }
    

    从响应中得知,被跳过的索引被设置成null了,并不会导致空指针。

    Set

    对于Set类型的数据绑定。先试着仿照List的做法。

    package com.shy.springmvcbingding.domain;
    
    import java.util.Set;
    
    public class UserSet {
        Set<User> users;
        // getter和setter方法省略
    }
    
    

    Controller中新增方法set

    @PostMapping("set")
    public UserSet userSet(UserSet userSet) {
        return userSet;
    }
    

    表单中还是和List一样传入相同的字段。运行一下会发现报错了

    Invalid property 'users[0]' of bean class [com.shy.springmvcbingding.domain.UserSet]: Cannot get element with index 0 from Set of size 0, accessed using property path 'users[0]'
    

    追踪源码

    else if (value instanceof Set) {
        // Apply index to Iterator in case of a Set.
        Set<Object> set = (Set<Object>) value;
        int index = Integer.parseInt(key);
        if (index < 0 || index >= set.size()) {
            throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
                                               "Cannot get element with index " + index + " from Set of size " +
                                               set.size() + ", accessed using property path '" + propertyName + "'");
        }
    

    这里用到了set.size()是0,说明Set中没有任何对象。好吧,我那们就手动add几个进去。

    给UserSet新增一个构造方法

    public UserSet() {
        users = new LinkedHashSet<>();
        users.add(new User());
        users.add(new User());
        users.add(new User());
    }
    

    再运行程序就会得到和List一样结果。但是Set的主要功能是去重,当name和age一样时,就认为这两个对象是相等的,有个好的建议是对于自定义对象的Set和Map都必须重写hashCode方法equals方法

    在User类中,重写这两个方法alt + insert让IDEA自动帮我们生成。

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(name, user.name) &&
            Objects.equals(age, user.age);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
    

    再运行程序,又报错了!原因还是越界了,因为在UserSet方法中连续add了三次User,但由于我们重写了hashCode和equals方法,这三个user是重复的。三次add后set的实际size是1。现在啊只传users[0].name=vv&users[0].age=33程序就不会报错了。

    {
        "users": [
            {
                "name": "vv",
                "age": 33,
                "contactInfo": null
            }
        ]
    }
    

    Map

    和上面一样先写UserMap

    package com.shy.springmvcbingding.domain;
    
    import java.util.Map;
    
    public class UserMap {
        private Map<String,User> users;
        // getter和setter方法省略
    }
    
    

    在Controller中

    @PostMapping("map")
    public UserMap userMap(UserMap userMap) {
        return userMap;
    }
    

    然后用map['key']的形式传入以下参数即可。

    Snipaste_2018-10-07_10-45-22

    响应内容为

    {
        "users": {
            "X": {
                "name": "Jack",
                "age": 22,
                "contactInfo": null
            },
            "Y": {
                "name": "Lucy",
                "age": 20,
                "contactInfo": null
            }
        }
    }
    

    Json&XML

    使用注解@RequstBody可以处理contentType:"application/json"和“application/xml”编码的内容,并将json或者xml中的数据绑定到参数对象上。

    先来测试Json,把contentType改成application/json,然后访问/json

    @PostMapping("/json")
    public User userJson(@RequestBody User user) {
        return user;
    }
    

    在request body中传入以下json内容

    {
        "name":"bob",
        "age": 16
    }
    

    响应内容是

    {
        "name": "bob",
        "age": 16,
        "contactInfo": null
    }
    

    同理,对于XML,contentType改成application/xml。在User类中,加几个xml绑定的注解。

    package com.shy.springmvcbingding.domain;
    
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlRootElement;
    import java.util.Objects;
    
    @XmlRootElement(name = "user")
    public class User {
        private String name;
        private Integer age;
        
        @XmlElement(name = "name")
        public String getName() {
            return name;
        }
        @XmlElement(name = "age")
        public Integer getAge() {
            return age;
        }
    
    }
    
    

    @XmlElement可以将Java属性映射成xml节点。

    • 在类上,加@XmlRootElement(name = "user"),表明xml的根元素名为user,如果不指定name,默认使用Java类名
    • 在getter/setter方法上加@XmlElement,将该属性映射成xml的对应节点。同样的,果不指定name,默认使用Java属性名

    然后访问/xml,传入以下xml

    <?xml version="1.0" encoding="UTF-8"?>
    <user>
        <nam>Bob</nam>
        <age>22</age>
    </user>
    
    

    响应内容为

    {
        "name": "Bob",
        "age": 22,
        "contactInfo": null
    }
    

    注意,@XmlElement最好用来getter方法上,如果用在了属性(Field)上,会引发下面的异常。

    // @XmlElement(name = "name")
    // private String name;
    
    类的两个属性具有相同名称 "name"
        this problem is related to the following location:
            at public java.lang.String com.shy.springmvcbingding.domain.User.getName()
            at com.shy.springmvcbingding.domain.User
        this problem is related to the following location:
            at private java.lang.String com.shy.springmvcbingding.domain.User.name
            at com.shy.springmvcbingding.domain.User
    ] with root cause
    

    日期转换

    PropertyEditor

    如果要绑定的类型是Date型的,在表单中传入date=2018-10-01,很明显会报错,说不能将String转换成Date型。

    可以用init-binder为参数date注册一个PropertyEditor,其中有个子类CustomDateEditor

    @InitBinder("date")
    public void initDate(WebDataBinder binder) {
        binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
    }
    
    @PostMapping("date")
    public String getDate(@RequestParam("date") Date date) {
        return date.toString();
    }
    

    其实就是将Date使用SimpleDateFormat来解析(parse)和格式化(format)。

    来看下CustomDateEditor的源码,重点关注它重写的setAsText和getAsText方法

    public CustomDateEditor(DateFormat dateFormat, boolean allowEmpty, int exactDateLength) {
        this.dateFormat = dateFormat;
        this.allowEmpty = allowEmpty;
        this.exactDateLength = exactDateLength;
    }
    
    @Override
    public void setAsText(@Nullable String text) throws IllegalArgumentException {
        if (this.allowEmpty && !StringUtils.hasText(text)) {
            // Treat empty String as null value.
            setValue(null);
        }
        else if (text != null && this.exactDateLength >= 0 && text.length() != this.exactDateLength) {
            throw new IllegalArgumentException(
                "Could not parse date: it is not exactly" + this.exactDateLength + "characters long");
        }
        else {
            try {
                // 设置value,设置的是从String解析后的Date
                setValue(this.dateFormat.parse(text));
            }
            catch (ParseException ex) {
                throw new IllegalArgumentException("Could not parse date: " + ex.getMessage(), ex);
            }
        }
    }
    
    @Override
    public String getAsText() {
        // 取出value并格式化
        Date value = (Date) getValue();
        return (value != null ? this.dateFormat.format(value) : "");
    }
    
    public void setValue(Object value) {
        this.value = value;
        firePropertyChange();
    }
    
    public Object getValue() {
        return value;
    }
    

    可以发现其实CustomDateEditor就是对DateFormat做了一个封装而已,参数allowEmpty表示是否允许text为空,该类会将空字符串当成null值。

    现在运行程序,响应内容为

    Mon Oct 01 00:00:00 CST 2018
    

    成功将字符串2018-10-01转换成了Date型。

    Formatter

    package org.springframework.format下有这么一个接口

    public interface Formatter<T> extends Printer<T>, Parser<T> {
    
    }
    

    Spring下有一个实现类DateFormatter,可以完成从字符串到日期的转换,直接使用即可。

    不过需要在xml中引入FormattingConversionServiceFactoryBean并注入相应的property

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        <mvc:annotation-driven conversion-service="dateFormatter"/>
        
        <bean id="dateFormatter" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
            <property name="formatters">
                <set>
                    <bean id="formatter" class="org.springframework.format.datetime.DateFormatter"/>
                </set>
            </property>
        </bean>
    </beans>
    

    定义好dateFormatter这个bean后,别忘了在mvc注解驱动中加上conversion-service,其属性值对应了FormattingConversionServiceFactoryBean的bean id。

    <mvc:annotation-driven conversion-service="dateFormatter"/>
    

    最后在Java Config中引入该xml文件

    package com.shy.springmvcbingding.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.ImportResource;
    
    /**
     * @author Haiyu
     * @date 2018/10/7 22:33
     */
    @Configuration
    @ImportResource("classpath:bind.xml")
    public class BindingConfig {
    }
    
    

    这样SpringBootApplication会扫描到这个配置文件并加载到上下文中。

    Converter

    FormattingConversionServiceFactoryBean这个类有两个重要的属性。

    @Nullable
    private Set<?> converters;
    
    @Nullable
    private Set<?> formatters;
    

    可知它既可以实现Formatter也可以实现Converter。那么只需把注入的property属性改成converters即可。

    <mvc:annotation-driven conversion-service="dateConverter"/>
        <bean id="dateConverter" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
            <!--<property name="formatters">-->
                <!--<set>-->
                    <!--<bean id="formatter" class="org.springframework.format.datetime.DateFormatter"/>-->
                <!--</set>-->
            <!--</property>-->
            <property name="converters">
                <set>
                    <bean id="converter" class="com.shy.springmvcbingding.converter.DateConverter">
                        <constructor-arg name="pattern" value="yyyy-MM-dd" />
                    </bean>
                </set>
            </property>
        </bean>
    

    DateConverter是自定义类,实现了Converter<S,T>接口,可以将S类型转换成T类型。如下,DateConverter<String, Date>是一个将String类型的转换成Date类型的转换器。

    package com.shy.springmvcbingding.converter;
    
    import org.springframework.core.convert.converter.Converter;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * @author Haiyu
     * @date 2018/10/7 22:59
     */
    public class DateConverter implements Converter<String, Date> {
        private String pattern;
        public DateConverter(String pattern) {
            super();
            this.pattern = pattern;
        }
    
        @Override
        public Date convert(String s) {
            SimpleDateFormat sdf = new SimpleDateFormat(pattern);
            Date date = null;
            try {
                date =  sdf.parse(s);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return date;
        }
    }
    

    @DateTimeFormat

    将字符串转换成日期最方便的方法其实是在参数上添加注解@DateTimeFormat。

    还可以为其指定pattern,如下。

    @PostMapping("date")
    public String getDate(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {
        return date.toString();
    }
    

    比较

    • PropertyEditor:局部使用,通常搭配WebDataBinder来使用
    • Formatter:全局/局部使用(xml中通过Spring注入的方式实现了全局使用),只能将String类型转换成其它类型
    • Converter:全局/局部使用(xml中通过Spring注入的方式实现了全局使用),可以将任意类型的转换成其他类型的。
    • @DateTimeFormat,局部使用,用在Controller的方法参数上

    RESTful扩展

    REST(Representational State Transfer)即表现层状态转移,如果一个架构符合REST原则,就称为RESTful架构。

    资源(resource)包括文本、图片、音频、服务等,资源呈现的形式称为表现层。

    • 文本:txt、xml、json、html、binary
    • 图片:png、jpg

    资源表现的形式通过http协议的content-type和accept来指定。

    http://www.bookstore/book
    这个网址只代表资源本身,资源的展现形式是通过content-type和accept来描述的
    

    Accept代表发送端(客户端)希望接受的数据类型。
    比如:Accept:text/xml; 代表客户端希望接受的数据类型是xml类型

    Content-Type代表发送端(客户端|服务器)发送的实体数据的数据类型。Content-Type:text/html; 代表发送端发送的数据格式是html。

    写一个方法查看请求的content-type。

    // 方法1
    @GetMapping("/contentType")
    public String contentType(HttpServletRequest request) {
        return request.getContentType();
    }
    
    // 方法2
    @GetMapping("/contentType")
    public String contentType() {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        return request.getContentType();
    }
    

    在SpringMVC中,获取HttpServletRequest和HttpServletResponse有两种方法。

    • 在方法的入参中直接定义,如上方法1
    • 使用RequestContextHolder.getRequestAttributes(),然后调用其getRequest()和getResponse()就能获得HttpServletRequest和HttpServletResponse

    HTTP请求方法中,最常用的是POST、DELETE、PUT、GET,可以认为分别对应了对资源的增删改查。再次之前西安介绍下HTTP中的幂等性:

    幂等性:每次HTTP请求相同的参数,相同的URI,产生的结果是相同的。

    • GET-获取资源。如http://www.bookstore/book/123,表示查询编号为123的书籍
    • POST-创建资源,不具有幂等性。http://www.bookstore/book,表示新增一本书籍,请求多少次就会新增多少条记录,会响应不同的URI,即产生了不同的结果,所以认为POST不具有幂等性
    • PUT-创建(更新)资源,具有幂等性。http://www.bookstore/book/123,创建或者更新编号为123的书籍,如果编号123书籍已经存在那么语义是更新;如果不存在就是新增。但是多次请求的结果都是对一个资源的更新,即多次请求后结果不变,只会响应有一个URI,所以认为PUT是幂等的
    • DELETE-删除资源。http://www.bookstore/book/123,删除编号为123的书籍

    以上4个请求方法中,POST不是幂等的,GET、DELETE、PUT是幂等的。

    用RESTful风格写Controller,以上面的book为例

    @GetMapping("/bookstore/book/{id}")
    public String getBook(@PathVariable("id") Integer id) {
        return "Query book " + id;
    }
    
    @PostMapping("/bookstore/book")
    public String addBook(@RequestParam("id") Integer id) {
        return "Add book " + id;
    }
    
    @PutMapping("/bookstore/book/{id}")
    public String updateBook(@PathVariable("id") Integer id) {
        return "Update book " + id;
    }
    @DeleteMapping("/bookstore/book{id}")
    public String deleteBook(@PathVariable("id") Integer id) {
        return "Delete book " + id;
    }
    

    注:@PathVariable可以将URL路径中{...}的内容绑定到参数上。

    RESTful总结:

    • 每一个URI代表一个资源
    • 客户端和服务端之间,传递该资源的一种表现形式
    • 客户端通过不同的HTTP请求方法,对服务端的资源进行操作(增删改查等),实现表现层状态转移

    by @sunhaiyu

    2018.10.8

    相关文章

      网友评论

        本文标题:SpringMVC数据绑定

        本文链接:https://www.haomeiwen.com/subject/rxfyaftx.html