springMVC 2

Scroll Down

springMVC 基本web操作

参数的接收

参数的第一种接收方式

编写java代码

@Controller
public class ControllerTest2 {

    @RequestMapping("/t1")
    public String test1(int a, int b, Model model)
    {
        model.addAttribute("msg","结果是:"+(a+b));
        return "hello";
    }
    
}

这个代码做了三件事

  1. 声明这是一个Controller
  2. 配置了RequestMapping为/t1
  3. 接收了两个参数a和b
  4. 使用Model传递参数
  5. 返回了一个视图名

接下来进行测试

地址栏填写

http://localhost:8080/t1?a=1&b=2

可见,这种方式是以get方式传递参数,如果不传递参数,服务器端会进行报错

结果也是正确的

结果是:3

指定@RequstParam

如果我们传递的参数和接受的的参数名称不一时,会报错

我们有一种方式来指定参数

@RequestMapping("/t1")
public String test1(@RequestParam("a1") int a, @RequestParam("b1") int b, Model model)
{
    model.addAttribute("msg","结果是:"+(a+b));
    return "hello";
}

这里我使用了@RequestParam("")来进行配置,用于指定参数映射

当且仅当访问的url这样才会成功

http://localhost:8080/t1?a1=1&b1=2

如果填写a=1&b=2是会报400错误的

You can use the @RequestParam annotation to bind Servlet request parameters (that is, query parameters or form data) to a method argument in a controller.

官网解释

可以使用 @RequestParam注解去绑定Servlet Request的参数到method argument上去

使用Restful风格

我们可以使用@PathVariable注解来从url中获取参数

@RequestMapping("/t2/{a}/{b}")
public String test2(@PathVariable int a, @PathVariable int b,Model model)
{
    model.addAttribute("msg","结果是:"+(a+b));
    return "hello";
}

这种方法见不到地址栏中的? &,简洁了许多,并且见不到参数的名称,增加了安全性

http://localhost:8080/t2/1/2

这样访问,1和a对应,2和b对应

官方解释

You can also declare URI variables and access their values with @PathVariable

You can explicitly name URI variables (for example, @PathVariable("customId")), but you can leave that detail out if the names are the same and your code is compiled with debugging information or with the -parameters compiler flag on Java 8.

当然这个PathVariable也支持命名别名

接收参数并封装为一个对象

public class User {
    String name;
    int age;
    String sex;
}

在SpringMVC中,传递的参数会自动映射为一个对象

@RequestMapping("/t3")
public String test3(User user,Model model)
{
    model.addAttribute("msg",user.toString());
    return "hello";
}

http://localhost:8080/t3?name=xxx&age=10&sex=m

这样访问这个Controller的参数会被自动进行包装

自动参数映射

除了映射为对象,SpringMVC的参数映射功能远比这个强大

此处参考博客

https://www.cnblogs.com/best/p/5669010.html#_label3_0_0_0

映射为复杂对象

public class Team {
    String TeamId;
    User user;
}

这种复杂的类型嵌套,我们也可以通过自动参数映射来进行完成

@RequestMapping("/t4")
public String test4(Team team, Model model)
{
    model.addAttribute("msg",team.toString());
    return "hello";
}

调用一下url来进行测试

http://localhost:8080/t4?teamId=2&user.name=xxx&user.age=10&user.sex=m

结果如下

Team{teamId='2', user=User{name='xxx', age=10, sex='m'}}

此处再演示一下如果参数传递不全会出现什么样的情况

假设传递参数

http://localhost:8080/t4?teamId=2&user.name=xxx&user.age=10

结果如下

Team{teamId='2', user=User{name='xxx', age=10, sex='null'}}

结果并不会出错,只是有参数不会被传递

数组的映射

@RequestMapping("/t5")
public String test5(String[] params, Model model)
{
    model.addAttribute("msg", Arrays.toString(params));
    return "hello";
}

对于数组的映射,SpringMVC也支持

通过参数列表中声明的数组名,来确定传递的参数的名称

http://localhost:8080/t5?params=2&params=9&params=xxx

结果如下

[2, 9, xxx]

List集合的映射

不能直接在action的参数中指定List<T>类型,定义一个类型包装List集合在其中

例如

public class Production {
    List<String> list;
}

由于tomcat的最新标准

Get请求新的安全规范要求URL中不能直接带[],如下所示

25-Oct-2018 14:12:09.277 警告 [http-nio-8080-exec-2] org.apache.tomcat.util.http.parser.HttpParser.<clinit> Character [,] is not allowed and will continue to be rejected

如果在url中传递参数会出现以下问题

java.lang.IllegalArgumentException: Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986

这种问题的解决方案如下

  1. 替换url请求。不用{}[]特殊字符! * ’( ) ; : @ & = + $ , / ? # [ ])
  2. 对请求编码解码。 UrlDecode、UrlEncode
  3. 配置Tomcat对字符的支持

因此我采用编写表单的方式来进行传参

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="post" action="/t6">
        <input type="text" name="list[0]"><br>
        <input type="text" name="list[1]"><br>
        <input type="text" name="list[2]"><br>
        <input type="text" name="list[3]"><br>
        <input type="submit">
    </form>
</body>
</html>

传参的结果如下

Production{list=[a, v, b, v]}

Map集合类型的映射

Map和List差不多,都需要一个类的包装才行

public class Books {
    Map<String,String> bookMap;
}
@RequestMapping("/t7")
public String test7(Books books, Model model)
{
    model.addAttribute("msg",books.toString());
    return "hello";
}

进行测试

仍然使用刚刚编写的表单进行测试

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="post" action="/t7">
        <input type="text" name="bookMap[a]"><br>
        <input type="text" name="bookMap[b]"><br>
        <input type="text" name="bookMap[c]"><br>
        <input type="text" name="bookMap[d]"><br>
        <input type="submit">
    </form>
</body>
</html>

可以注意到,list[],[]中的元素就是map中的key,里面的内容就是value

测试结果

Books{bookMap={a=123, b=xxx, c=abc, d=qwe}}

@RequestParam参数绑定

这个注解还有一些属性

/**
 * Whether the parameter is required.
 * <p>Defaults to {@code true}, leading to an exception being thrown
 * if the parameter is missing in the request. Switch this to
 * {@code false} if you prefer a {@code null} value if the parameter is
 * not present in the request.
 * <p>Alternatively, provide a {@link #defaultValue}, which implicitly
 * sets this flag to {@code false}.
 */
boolean required() default true;

/**
 * The default value to use as a fallback when the request parameter is
 * not provided or has an empty value.
 * <p>Supplying a default value implicitly sets {@link #required} to
 * {@code false}.
 */
String defaultValue() default ValueConstants.DEFAULT_NONE;
  1. required

    如设置为true,如果一个参数丢失会抛出异常

    如果设置为false,如果参数丢失会传递一个null值 (基本类型会报错)

  2. defaultValue

    如果没有传递这个值就会使用这个预设值

    前提条件当required设置为false的时候使用

@RequestMapping("/t8")
public String test8(Model model,@RequestParam(value = "a",required = false) String a, @RequestParam(value = "b",required = false) String b)
{
    model.addAttribute("msg",a+b);
    return "hello";
}

这是一个极端的例子,我们把a和b都设为可以接收空值

http://localhost:8080/t8

结果为

nullnull

如果编写了defaultValue

@RequestMapping("/t8")
public String test8(
    Model model,
    @RequestParam(value = "a",required = false,defaultValue = "default") String a, 
    @RequestParam(value = "b",required = false, defaultValue = "default") String b
){
    model.addAttribute("msg",a+b);
    return "hello";
}

这样编写就可以使用初始值

defaultdefault

参数绑定List

@RequestMapping("/t9")
public String test9(Model model, @RequestParam("id")  List<String> list)
{
    model.addAttribute("msg",list.toString());
    return "hello";
}

这样就可以和数组一样对List赋值

http://localhost:8080/t9?id=17&id=o&id=q

结果如下

[17, o, q]

@RequestBody

@RequestBody 注解将HTTP请求正文插入方法中,使用适合的 HttpMessageConverter将请求体写入某个对象。

You can use the @RequestBody annotation to have the request body read and deserialized into an Object through an HttpMessageConverter.

你可以使用@RequestBody 注解通过一个HttpMessageConverter去从request中读取一个Object

public class User {
    String name;
    String sex;
}
@RequestMapping("/t10")
public String test10(Model model, @RequestBody User user)
{
    model.addAttribute("msg",user.toString());
    System.out.println(user);
    return "hello";
}
$.ajax({
    type: 'POST',
    url: "/t10",
    contentType: "application/json",//如果想以json格式把数据提交到后台的话,这个必须有,否则只会当做表单提交
    data: JSON.stringify({"name":"sam","sex":"m"}),//JSON.stringify()必须有,否则只会当做表单的格式提交
    dataType: "json",//期待返回的数据类型
    success: function(data){
        alert("success:"+data);
    },
    error:function(data){
        alert("error"+data);
    }
});

注意这里使用会出现一系列问题

  1. ajax请求中一定要使用contentType否则报错,因为@RequestBody是靠contentType解析的
  2. jackson的包导入不进来,主要原因还是版本不对应,我一开始是5.2.4的spring版本,jackson一直出错,后来看网上使用成熟的版本,spring 4.2.1和 jackson 2.5.0就成功了(后面实测spring 5.0.7 和 jackson 2.9.8也是可以的)
  3. jackson乱码
<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <constructor-arg value="UTF-8"/>
        </bean>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                    <property name="failOnEmptyBeans" value="false"/>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

为了避免出错,这段代码在需要jackson的时候都导入一下

转发与重定向

加入以下字段不会被视图解析器解析

redirect

@RequestMapping("j1")
public String test1()
{
    return "redirect:hello";
}

这种写法会不产生字符串拼接

http://localhost:8080/hello.jsp

会直接在地址栏中访问,如果这个jsp页面访问不到,那么这个页面就会404,如果能找到那就跳转成功

forward

使用这个字段,也不会被实体解析器解析,相当于

request.getRequestDispatcher().forward()

我们在spring MVC中使用forward

@RequestMapping("/j2")
public String test2()
{
    return "forward:/j3";
}

@RequestMapping("/j3")
public String test3(Model model)
{
    model.addAttribute("msg","hello spring MVC");
    return "hello";
}

最后会通过跳转访问到hello.jsp

乱码问题的解决

在web.xml中进行配置

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

配置了过滤器就可以解决

springMVC 数据传递

这三种数据传递方式都会在requst域中存储数据

使用Model

每一次会话都会建立一个model

model.addAttribute("msg","hello spring MVC")

使用ModelAndView

每一次会话不会建立ModelAndView,需要自己new

用法和model相似

使用ModelMap

ModelMap是比model多继承了Map,拥有类似于Map操作的方式