Spring MVC Controller 入参类型

  • 入参的类型简介
  • 从请求URL当中来的参数
    • url占位符
    • url的query部分
      • 需要注意的是
    • 从url中组装对象入参
  • 从Session中来的参数
    • 获取所有HttpSession
    • 获取Session中指定的对象
  • 从Cookie中来的参数
  • 用于前后端传递数据的Map
      • 需要注意的是
  • HttpServletRequest和HttpServletResponse
  • 总结一下
  • 参考文档

刚接触Spring MVC时,被Controller眼花缭乱的入参搞得不知东南西北,尤其是从Servelet转过来,固定的入参变成了随意设置的入参,可是困惑了一阵.现在,我感觉自己理清了不少,所以做个总结.

入参的类型简介

Spring MVC Controller入参很灵活,分一下类,可以大致分为如下几种: 
* 从请求url当中来的参数 
* 从Session中来的参数 
* 从Cookie中来的参数 
* 用于前后端传递数据的Map(例如ModelAndView
* HttpServletRequest和HttpServletResponse 
* 其他不常用的类型,比如InputStream/OutputStream 和 Reader/Writer,WebRequest/NativeWebRequest(本文不介绍,见文末参考文档2) 
只要参数类型属于上述几种,那么无论Controller的路由处理函数的入参写成有多少个,都是合法的 
接下来就上述几种参数,再细细说明

从请求URL当中来的参数

这应当是最普遍的参数来源,细分以下,从请求URL中来的参数,可以再分为如下几种:

url占位符:

使用@PathVariable注解(Spring专属),或者@PathParam注解(Java标准),从url中获取参数 
而对应映射的url需要使用占位符来表示.我们来举个例子:

@GetMapping("/test/{openId}/{name}")
public String test(@PathVariable("openId") Long openId,@PathVariable("name") String name ){
    // here we get openId,name from url placeHolder
    return "test";
}

这样配置的Controller,可以接受形如/test/11/li,/test/12/li?queryOption=12&...的url 
此外,url占位符形式的映射,可以配置在整个Controller类上,这样一个Controller下所有的路由处理方法都可以接收占位符的入参,省时省力 
同时,占位符这种形式,也是RESTful提倡的一种形式

url的query部分

使用RequestParam注解(Spring专属),或者QueryParam注解(Java标准),从url的query部分(?后内容)获取参数 
而对应的url映射,不需要像占位符那样列出入参的名字,我们举个例子:

@GetMapping("/test")
public String test(@RequestParam("openId") Long openId,@RequestParam("name") String name){
    //here we get openId, name from url
    return "test";
}

这样配置的Controller,可以接受形如/test?openId=11&name=li&....形式的参数

可以看出,从url的query部分取得参数和从url的路径占位符部分取得参数,其实都是可以的(在我们的例子中,都可以取得openId 和name),那么如何选择呢? 
个人感觉 
* 如果参数是一个Controller下多个路由处理函数共用的,那么使用url路径占位符的形式,可以节省编码量 
* 此外,对于RESTful有要求的url,url路径占位符有很大的发挥空间

需要注意的是

因为query部分并不显示在配置的路径映射里面,但是默认要求所有@RequestParam都需要出现在url的query部分,否则匹配不上,显示400 bad request.如果允许某一个被@RequestParam注解的url的query部分的参数为空,那么可以使用如下形式:@RequestPparam(value="openId",required=false)来声明,这样,就允许url的query部分不传递openId,同时可以映射到对应的路由处理函数

从url中组装对象入参

将url的参数绑定到自定义的对象上,这是Spring MVC一个很有用的功能,帮助开发人员省去不少胶水代码 
自定义对象的绑定规则不多: 
* 自定义对象符合JavaBean规范 
* 请求的url的路径占位符部分(上面提到的url参数第一种)和query部分(上面提到的url参数第二种),参数名和自定义对象的属性名一致 
这样就可以将url的参数组成一个对象,当作入参使用 
比如url形如/test/111?fromSite=111 
对应自定义对象和Controller如下

@GetMapping("/test/{openId}")
public String test(Pojo pojo){
    //here we get a pojo instance with openId and fromSite
    return "test";
}

class Pojo{
    private Long openId;
    private String fromSite;

    public Long getOpenId() {
        return openId;
    }

    public void setOpenId(Long openId) {
        this.openId = openId;
    }

    public String getFromSite() {
        return fromSite;
    }

    public void setFromSite(String fromSite) {
        this.fromSite = fromSite;
    }
}

更多关于绑定自定义对象的内容,可以参考另一片文章:Spring MVC 复杂数据绑定

从Session中来的参数

因为Session不在页面和url上体现,所以前端页面和url都不需要做变动

Spring MVC提供了两种读取HttpSession的方法

获取所有HttpSession

自由度很大的一种方式,可以操作所有存放在Session中的对象,代码如下

@GetMapping("/test")
public String test(HttpSession session){
    //here we get HttpSession,just write HttpSession session in param
    return "test";
}

获取Session中指定的对象

只能获得先前存在Session中的指定的对象,如果指定的对象不存在,则会返回400 bad request.如果指定对象的类型和Session中存放的对象类型不一致,则会报类型转化的异常. 
示例代码如下:

@GetMapping("/test")
public String test(@SessionAttribute("pojo") Pojo pojo){
    //here we get pojo from Session
    return "test";
}

从Cookie中来的参数

从Cookie中取得参数,相比于Session,就显得简单粗暴了,只有一种方式,只能读取,不能设置,如果要设置,需要入参传入HttpServletRequest和HttpServletResponse设置,关于这两个类的使用,请在接下来的第二个Section.

示例代码:

@GepMapping("/test")
public String test(@CookieAttribute("test") String test/*cookie 只能存放String*/){
    //here we get String test from cookie
    return "test";
}

用于前后端传递数据的Map

这里指的是用于前后端传递参数的Map,当前端使用JSP,或者Velocity等模板技术时,模板上是可以获得后台参数,用于渲染(填充)页面的,这个时候后台传来的参数放到哪里传到页面模板呢?放到HttpServletRequest里当然可以,此外还可以使用Map. 
当Controller的路由处理函数入参声明一个Map后,处理函数就可以把需要传递到页面模板的数据放到Map当中去,当函数执行完return到指定的页面后,页面就可以从Map中拿到数据了.页面拿数据的方式不需要任何不同,就是使用Map的key就可以 
示例代码:

//路由处理函数
@GepMapping("/test")
public String test(Map map){
    Pojo pojo=new Pojo();
    pojo.setOpenId(100L);
    pojo.setFromSite("www.test.com");
    map.put("testPojo",pojo);
    return "test";
}
//一个自定义的对象
class Pojo{
    private Long openId;
    private String fromSite;

    public Long getOpenId() {
        return openId;
    }

    public void setOpenId(Long openId) {
        this.openId = openId;
    }

    public String getFromSite() {
        return fromSite;
    }

    public void setFromSite(String fromSite) {
        this.fromSite = fromSite;
    }
}

//页面模板,使用EL表达式

here will has a pojo : openId= ${testPojo.openId} ,and fromSite=${testPojo.fromSite}


需要注意的是:

  1. Map这个入参,其实也是很灵活的,相关的类有三个:Map,Model,ModelMap.其中Model和ModelMap都实现了Map接口.在Spring MVC里提供了一个BindingAwareModelMap类,它同时实现了Model和ModelMap接口,每次请求都会隐式创建一个BindingAwareModelMap的实例,所以入参声明这Map,ModelMap或者Model,底层都是对应的一个BindingAwareModelMap的实例,都是为了前后端数据的传递,三者没有区别. 
    盗一张图说明继承层次: 
    BindingAwareModelMap 继承层次 
    Spring MVC Controller 入参类型_第1张图片
  2. 此外还有一个ModelAndView类,这个类也有前后台传递数据的功能,甚至还有决定页面的功能,不过这个类和Map,Model,ModelMap并不同源,当这个类和Map,Model.ModelMap一起使用,并且增添同名数据的时候,ModelAndView的优先级更高,会覆盖其他同名数据.(详情,见文末参考文档3)

HttpServletRequest和HttpServletResponse

当你想拥有和操作Servlet一样的自由度的时候,可以尝试路由处理函数中传入HttpServletRequest和HttpServletResponse. 
示例代码如下:


@GepMapping("/test")
public String test(HttpServletRequest request,HttpServletResponse response){
    //here we get HttpServletRequest and HttpServletResponse,we can do something with request and response
    //有没有觉得这个方法和Servlet的doGet和doPost很像了
    //比如,我们实践一下在 **从Cookie中来的参数** 中提到的,增加Cookie的操作
    Cookie cookie=new Cookie("test","this is a test Cookie");
    cookie.setPath("/");
    cookie.setMaxAge(-1);//代表页面关闭即销毁Cookie
    response.addCookie(cookie);
    return "test";
}

总结一下

Controller的路由处理函数,可以传入的参数五花八门,从Servlet转过来的时候的确理不清头绪.当然,这其中的灵活性也是Spring MVC的优势. 
不过只要入参的来源属于上述几种,那么无论入参写成什么样,有多少个都是合法的,下面举一个比较极端的,但是合法的例子:

@GepMapping("/test/{openId}/{name}")
public String test(@PathVariable("openId") Long openId,@PathVariable("name") String name,
                    @RequestParam(value="fromSite",required=false) String fromSite,
                    HttpSession session,@CookieAttribute("test") String test,@ModelAttribute Pojo pojo/*自定义JavaBean对象*/,
                    HttpServletRequest request,HttpServletResponse response){
    return "test";
}

参考文档

  1. Spring MVC 复杂数据绑定
  2. SpringMVC强大的数据绑定(1)——第六章 注解式控制器详解——跟着开涛学SpringMVC
  3. SpringMVC里的Model、Map、ModelMap以及ModelAndView

你可能感兴趣的:(java)