SpringMVC 参数绑定

默认的参数绑定类型

在 SpringMVC 框架的 Controller 方法(配置了@RequestMapping或其他请求映射)中,我们可以使用的默认参数类型主要有:

  • HttpServletRequest
  • HttpServletResponse
  • HttpSession
  • HttpMethod
  • Model

我们只需要在方法的参数列表中添加上述类型的参数,那么在处理请求时,SpringMVC 就会自动绑定与该请求相关的对象,正确传递给方法。

举个最简单的栗子:

@PostMapping("/user")
public String login(HttpServletRequest request) {
    // 获取请求参数
    String name = request.getParameter("username");
    String pwd = request.getParameter("password");
    // 调用业务方法
    Boolean status = userService.login(name, pwd);
    // 返回状态
    if (status) {
        return "登录成功";
    } else {
        return "登录失败";
    }
}

除了上面列举的各个请求都通用的类型,我们也可以添加其他类型的形参,比如 Integer,String,用来接收具体请求携带的参数,免去了request.getParameter的频繁使用。但是请注意,这么做必须保证请求参数的 key 与形参的名称相同,SpringMVC 才能识别并绑定。

相似的例子:

@PostMapping("/user")
public String login(String username, String password) {
    Boolean status = userService.login(username, password);
    if (status) {
        return "登录成功";
    } else {
        return "登录失败";
    }
}

POJO 类也可以自动识别,但还是那条规则:请求参数的 key 必须与该 POJO 类型的字段名称相同;另外如果参数的 key 出现了这种形式address.provinceName,那就意味着包含了另一个 POJO 类,字段名也必须相同:

public class User {
    private Long id;
    private String name;
    private String password;
    private Address address; // 包含了Address类型
}
public class Address {
    private String provinceName;
    private String cityName;
}

参数绑定的注解

1. @RequestParam

你可能觉得请求参数与方法形参必须保证名称相同的限制有点严格,那么请使用@RequestParam注解,可以将指定的请求参数赋值给方法中的形参,并提供更灵活的选项。它有 3 个可使用的属性:

  • value:指定请求参数的名称
  • required:指定参数是否必须绑定,默认 true
  • defaultValue:当请求不携带该参数时使用的默认值
@GetMapping("/category")
public Category findById(@RequestParam(value = "id", 
                        required = true, defaultValue = "1") categoryId) {
    Category = categoryService.findById(categoryId);
    return Category;
}

另外请注意,不管你的方法形参是直接使用还是加上@RequestParam注解,都只能接收 Content-Type 为 application/x-www-form-urlencoded 编码的内容,JSON 格式的数据会被拒绝。

2. @RequestBody

使用@RequestBody注解在参数上就可以接收非 application/x-www-form-urlencoded 编码格式了,用于处理HttpEntity中的数据。由于 GET 请求没有请求体,所以不适用;对 POST 请求,SpringMVC 就会通过处理适配器HandlerAdapter中配置的HttpMessageConverters来解析HttpEntity的数据,然后绑定到相应的 POJO 上。

@PostMapping("/book")
public void saveBook(@RequestBody book) { 
    bookService.save(book);
}

通过@RequestBody获取的是整个请求体的数据,这也就是说:(敲重点)如果你想要同时接收两个 POJO 类型对象,那么只能用另一个 POJO 类包含它们。

3. @PathVariable

@PathVariable注解可以绑定 URL 中的动态参数,该注解只有一个 value 属性,类型为 String,表示 URL 中需要绑定的参数名称;如果省略,则默认绑定与形参相同的名称。比较适合传递 id 或 name 等能够标识一个资源的数据。

栗子:

@GetMapping("/user/{userId}/articles")
public List<Article> findArticleByUser(@PathVariable("userId") Long id) { 
    return articleService.findArticleByUserId(id);
}

4. @RequestHeader

@RequestHeader注解可以绑定请求的报头信息(数据),有 3 个可用的属性:

  • value:指定请求头的字段名称
  • required:是否必须绑定
  • defaultValue:当该字段不存在时使用的默认值

回顾一下 HTTP 的知识,一般的请求报头信息有下面这些字段(这里截取了在「简书」浏览一篇文章时发出的其中一个请求):

我们现在想要获取 Accept-Encoding 的信息,用@RequestHeader就可以一步搞定:

@GetMapping("/article/{id}")
public Article findArticle(@PathVariable Long id,
                           @RequestHeader("Accept-Encoding") String[] encoding) { // 这里注意是字符串数组
    // ...
}

5. @CookieValue

@CookieValue注解可以绑定请求的 Cookie 信息,同样有 3 个可用的属性:

  • value:指定 Cookie 的 key
  • required:是否必须绑定
  • defaultValue:不存在该 Cookie 时的默认值

示例:

@GetMapping("/article/{id}")
public Article findArticle(@PathVariable Long id,
                           @CookieValue("JSESSIONID") String cookie) {
    // ...
}

PS:JSESSIONID 是 Servlet 容器(tomcat,jetty)用来记录用户 session 的默认 Cookie 名称

PS*2:调用request.isRequestedSessionIdFromCookie判断 Cookie 是否可用,逻辑就是读取 request 里面有没有 JSESSIONID 这个 Cookie。

自定义参数转换

我们已经知道 SpringMVC 在参数绑定和类型转换方面提供了不少的支持,那么有没有什么地方是需要我们自定义参数转换的呢?最典型的场景是日期类型转换(字符串转 java.util.Date 类型),对于这个问题,解决方案有两个:实现Converter接口或Formatter接口。

1. Converter

先看Converter,需要指定两个泛型,前者为要转换的类型,后者为转换后的类型,只有一个convert方法需要实现:

public class DateConverter implements Converter<String, Date> {
    // 日期的数据格式
    private String dataPattern = "yyyy-MM-dd HH:mm:ss";
    @Override
    public Date convert(String source) {
        try {
            // 进行日期转换
            return new SimpleDateFormat(dataPattern).parse(source);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2. Formatter

再来看Formatter接口,只需要指定一个泛型,即转换后的类型,而待转换的类型必须是 String,需要实现两个方法:parseprint,前一个方法规定了如何将字符串转换成指定类型,后一个方法规定了如何以字符串形式输出指定类型:

public class DateFormatter implements Formatter<Date> {
    // 日期的数据格式
    private String dataPattern = "yyyy-MM-dd HH:mm:ss";
    @Override
    public Date parse(String s, Locale locale) throws ParseException {
        // 将字符串转换成Date
        return new SimpleDateFormat(dataPattern).parse(s);
    }
    @Override
    public String print(Date date, Locale locale) {
        // 将Date转换成字符串
        return new SimpleDateFormat(dataPattern).format(date);
    }
}

3. 配置

接下来就是配置了,假设你用的是mvc:annotation-driven

<!-- 配置自定义的日期类型转换器 -->
<mvc:annotation-driven conversion-service="dataConverterService"/>
<bean id="dataConversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <!-- 使用Convert接口-->
    <property name="converters">
        <set>
            <bean class="cn.cna.convert.DateConverter"/>
        </set>
    </property>
</bean>
<mvc:annotation-driven conversion-service="dataConverterService"/>
<bean id="dataConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <!-- 使用Formatter接口-->
    <property name="formatters">
        <set>
            <bean class="cn.cna.convert.DateConverter"/>
        </set>
    </property>
</bean>

比较:

  • Converter是一种任意类型转换另一种任意类型,具有普遍性;
  • Formatter是 String 转换另一种,更符合 web 层的特点,比较适合在 springMVC 中使用。

END

整理自:SpringMVC+Mybatis快速开发与项目实战,黄文毅,清华大学出版社
其他参考:
一文读懂SpringMVC中的数据绑定,Wizey,简书
SpringMVC|参数绑定,GGarrett,简书
springmvc formatter,抱明月,博客园

posted @ 2020-03-10 23:35  逆风的小飞侠  阅读(245)  评论(0编辑  收藏  举报