前言:

本文内容:RestFul风格讲解、重定向和转发、接收请求参数及数据回显

推荐免费SpringMVC基础教程视频:【狂神说Java】SpringMVC最新教程IDEA版通俗易懂_哔哩哔哩_bilibili

RestFul风格讲解

概念

ResultFul是一个资源定位及资源操作的风格。不是标准也不是协议,而是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

功能

  • 资源:互联网所有的内容都可以被抽象为资源
  • 资源操作:使用Post,Delete,Put,Get等不同呢方法对资源进行操作
  • 分别对应:增删改查

传统方式操作资源

通过不同的参数来实现不同的效果,方法单一(Post和Get)

使用RestFul风格操作资源

可以通过不同的请求方式来是实现不同的效果

练习

  1. 新建RestFulController.java

    1
    2
    3
    4
    @Controller
    public class RestFulController{

    }
  2. 在Spring MVC中可以使用@PathVariable注解,让方法参数的值对应绑定到一个URI模板变量上

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package com.jokerdig.controller;

    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;

    /**
    * @author Joker大雄
    * @data 2022/6/3 - 16:00
    **/
    @Controller
    public class RestFulController {

    // 原来的: http://localhost:8080/add?a=1&b=2
    // RestFul:http://localhost:8080/add/a/b

    @RequestMapping("/add/{a}/{b}")

    public String test1(@PathVariable int a, @PathVariable int b, Model model){
    int res = a+b;
    model.addAttribute("msg","结果为"+res);

    return "test";
    }
    }

    运行:http://localhost:8080/springmvc_05_controller_war_exploded/add/1/2

    1
    结果为3
  3. 也可以使用method实行指定请求类型

    用于约束请求类型,指定请求的类型如:GET,POST,HEAD,OPTIONS,PUT,PATCH,DELETE,TRACE

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package com.jokerdig.controller;

    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;

    /**
    * @author Joker大雄
    * @data 2022/6/3 - 16:00
    **/
    @Controller
    public class RestFulController {

    // 原来的: http://localhost:8080/add?a=1&b=2
    // RestFul:http://localhost:8080/add/a/b


    @RequestMapping(name="/add/{a}/{b}",method= RequestMethod.GET)
    // @RequestMapping(name="/add/{a}/{b}",method= RequestMethod.POST)
    public String test1(@PathVariable int a, @PathVariable int b, Model model){
    int res = a+b;
    model.addAttribute("msg","结果为"+res);

    return "test";
    }
    }
  4. 所有地址栏请求默认都是HTTP GET类型的

    方法级别的注解变体有如下几个:

    1
    2
    3
    4
    5
    @GetMapping
    @PostMapping
    @PutMapping
    @DeleteMapping
    @PatchMapping
  5. 请求的地址相同,但所执行的方法并不同

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    package com.jokerdig.controller;

    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.*;

    /**
    * @author Joker大雄
    * @data 2022/6/3 - 16:00
    **/
    @Controller
    public class RestFulController {

    // 原来的: http://localhost:8080/add?a=1&b=2
    // RestFul:http://localhost:8080/add/a/b

    // @RequestMapping("/add/{a}/{b}")
    // @RequestMapping(name="/add/{a}/{b}",method= RequestMethod.GET) name可能会报错
    // @RequestMapping(value="/add/{a}/{b}",method= RequestMethod.GET)
    // @RequestMapping(path="/add/{a}/{b}",method= RequestMethod.GET)
    // @RequestMapping(name="/add/{a}/{b}",method= RequestMethod.POST)
    @PostMapping("/add/{a}/{b}")
    public String test1(@PathVariable int a, @PathVariable int b, Model model){
    int res = a+b;
    model.addAttribute("msg","结果1为"+res);

    return "test";
    }
    // 请求地址相同 但走的方法不同
    @GetMapping("/add/{a}/{b}")
    public String test2(@PathVariable int a, @PathVariable int b, Model model){
    int res = a+b;
    model.addAttribute("msg","结果2为"+res);

    return "test";
    }
    }
  6. 运行测试

    1
    2
    3
    4
    5
    6
    运行:http://localhost:8080/springmvc_05_controller_war_exploded/add/1/10
    浏览器显示
    结果211
    当使用表单提交后
    会走Post请求的方法
    地址不改变
  7. 小结

    RestFul风格的优势:简洁、高效、安全。

重定向和转发

ModelAndView

设置ModelAndView对象,根据view的名称,和视图解析器跳到指定页面

页面:{视图解析器前缀}+viewName+{视图解析器后缀}

1
2
3
4
5
6
7
<!--    视图解析器-->
<bean id="InternalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀-->
<property name="suffix" value=".jsp"/>
</bean>

对应的Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.jokerdig.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* @author Joker大雄
* @data 2022/6/1 - 12:35
**/
// 只要实现Controller接口的类 就是一个控制器
public class ControllerTest implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView();

mav.addObject("msg","ControllerTest1");
mav.setViewName("test");
return mav;
}
}

ServletAPI

通过设置servletAPI,不需要视图解析器

  • 通过HttpServletResponse进行输出
  • 通过HttpServletResponse实现重定向
  • 通过HttpServletResponse实现转发

新建ModelTest1.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.jokerdig.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
* @author Joker大雄
* @data 2022/6/3 - 16:52
**/
@Controller
public class ModelTest1 {

@RequestMapping("mt1/t1")
public String test(HttpServletRequest req, HttpServletResponse resp){
HttpSession session = req.getSession();
req.setAttribute("msg",session.getId());
return "test";
}

}

运行测试

http://localhost:8080/springmvc_05_controller_war_exploded/mt1/t1

1
2
浏览器显示
AF9BFA8384E5DA38BB8431DEF09FF749

SpringMVC

通过SpringMVC来实现转发和重定向(无需视图解析器)

测试前先将视图解析器注释掉

新建ModelTest2.java进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.jokerdig.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
* @author Joker大雄
* @data 2022/6/3 - 17:01
**/
@Controller
public class ModelTest2 {

// 第一种方法
@RequestMapping("mt2/t1")
public String test(Model model){
model.addAttribute("msg","ModelTest2");
return "/WEB-INF/jsp/test.jsp";
}
// forward方法
@RequestMapping("mt2/t2")
public String test1(Model model){
model.addAttribute("msg","ModelTest2");
return "forward:/WEB-INF/jsp/test.jsp";
}
// redirect方法
@RequestMapping("mt2")
public String test2(Model model){
// 直接重定向到首页
return "redirect:index.jsp";
}
}

浏览器显示

1
ModelTest2

通过SpringMVC来实现转发和重定向(有视图解析器)

重定向不需要视图解析器,本质就是请求一个新的地方

新建ModelTest3.java进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.jokerdig.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
* @author Joker大雄
* @data 2022/6/3 - 17:22
**/
@Controller
public class ModelTest3 {
// 请求转发
@RequestMapping("mt3/t1")
public String test1(Model model){
model.addAttribute("msg","ModelTest3");
return "test";
}
// redirect 重定向方法
@RequestMapping("mt3")
public String test2(Model model){
// 直接重定向到首页
return "redirect:index.jsp";
}
}

接收请求参数及数据回显

处理提交数据

  1. 提交的名称和处理方法的参数名一致

    提交数据:http://localhost:8080/htest?name=zhangsan

    处理方法

    1
    2
    3
    4
    5
    @RequestMapping("/test")
    public String test(String name){
    System.out.println(name);
    return "test";
    }

    后台输出

    1
    zhangsan
  2. 提交的名称和处理方法的参数名不一致

    提交数据:http://localhost:8080/htest?uname=zhangsan

    处理方法

    1
    2
    3
    4
    5
    @RequestMapping("/test")
    public String test(@RequestParam("uname") String name){
    System.out.println(name);
    return "test";
    }

    后台输出

    1
    zhangsan
  3. 提交的是一个对象

    要求提交的表单域和对象的属性名一致,参数使用对象即可

    实体类(使用Lombok)

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
    </dependency>

    User.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package com.jokerdig.pojo;

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;

    /**
    * @author Joker大雄
    * @data 2022/6/3 - 17:49
    **/
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
    private int id;
    private String name;
    private int age;
    }

    新建UserController.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    package com.jokerdig.controller;

    import com.jokerdig.pojo.User;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;

    /**
    * @author Joker大雄
    * @data 2022/6/3 - 17:48
    **/
    @Controller
    @RequestMapping("/user")
    public class UserController {

    // localhost:8080/user/t1?name=xxx
    @RequestMapping("/t1")
    public String test1(String name, Model model){
    // 1. 接收前端参数
    System.out.println("接收到前端的参数为:"+name);
    // 2. 将返回的结果传递到前端
    model.addAttribute("msg",name);
    return "test";
    }

    // 前端接收的是一个对象 User(id name age)
    /*
    1. 接收前端用户传递的参数,判断参数的名字,假设名字直接在方法上,可以直接使用
    2. 假设传递的是一个对象,匹配对象中的字段名,名字一致则传递值,否则接收不到数据
    */
    @RequestMapping("/t2")
    public String test2(User user){
    System.out.println(user);
    return "test";
    }
    }

    运行后输出

    http://localhost:8080/springmvc_05_controller_war_exploded/user/t2?id=1&name=zhangsan&age=21

    1
    User(id=1, name=zhangsan, age=21)

    数据显示到前端

    1. 通过ModelAndView

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      package com.jokerdig.controller;

      import org.springframework.web.servlet.ModelAndView;
      import org.springframework.web.servlet.mvc.Controller;

      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;

      /**
      * @author Joker大雄
      * @data 2022/6/1 - 12:35
      **/
      // 只要实现Controller接口的类 就是一个控制器
      public class ControllerTest implements Controller {
      @Override
      public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
      ModelAndView mav = new ModelAndView();

      mav.addObject("msg","ControllerTest1");
      mav.setViewName("test");
      return mav;
      }
      }
    2. 通过ModelMap

      1
      2
      3
      4
      5
      6
      // ModelMap
      @RequestMapping("/t3")
      public String test3(ModelMap modelMap){
      modelMap.addAttribute("msg","ModelMap");
      return "test";
      }
    3. 通过Model

      1
      2
      3
      4
      5
      6
      7
      8
      9
      // localhost:8080/user/t1?name=xxx
      @RequestMapping("/t1")
      public String test1(String name, Model model){
      // 1. 接收前端参数
      System.out.println("接收到前端的参数为:"+name);
      // 2. 将返回的结果传递到前端
      model.addAttribute("msg",name);
      return "test";
      }
    4. 拓展

      Model 继承了 ModelMap 继承了 LinkedHashMap

    5. 小结

      三种传递方式的区别

      • Model:只有几个方法适用于储存数据,简化了使用和理解
      • ModelMap:继承了LinkedHashMap,除了自身一些方法,同样继承LinkedHashMap的方法和特性
      • ModelAndView:可以在储存数据的同时,设置返回的逻辑视图,来控制展示层的跳转