Spring Boot 学习之路 -- 处理 HTTP 请求

在这里插入图片描述

前言

  1. 最近因为业务需要,被拉去研究后端的项目,代码框架基于 Spring Boot,后端对我来说完全小白,需要重新学习研究…
  2. 出于个人习惯,会以 Blog 文章的方式做一些记录,文章内容基本来源于「 Spring Boot 从入门到精通(明日科技) 」一书,做了一些整理,更易于个人理解和回顾查找,所以大家如果希望更系统性的学习,可以阅读此书。

系列文章
Spring Boot 学习之路📑 基础认知
Spring Boot 学习之路📑 项目配置
Spring Boot 学习之路📑 处理 HTTP 请求
Spring Boot 学习之路📑 Service 层
Spring Boot 学习之路📑 Thymeleaf 模板引擎

HTTP 请求是指从客户端到服务器的请求消息。对于一个 Spring Boot 项目而言,服务器就是 Spring Boot,客户端就是用户本地的浏览器。启动 Spring Boot 项目后,首先用户通过 URL 地址发送请求,然后 Spring Boot 通过解析 URL 地址处理请求,最后 Spring Boot 把处理结果返回给用户。

HTTP 请求有 3 种很常见的请求类型,它们分别是 GET、POST 和 DELETE

请求类型说明
GET表示请求从服务器获取特定资源
POST表示在服务器上创建一个新的资源
DELETE表示从服务器删除特定的资源

本文将介绍 Spring Boot 是如何使用注解解析 URL 地址,进而处理上述 3 种类型的 HTTP 请求的。


一、处理 HTTP 请求的注解

在开发 Spring Boot 项目的过程中,Spring Boot 的典型应用是处理 HTTP 请求。所谓处理 HTTP 请求,就是 Spring Boot 把用户通过 URL 地址发送的请求「交给不同的业务代码进行处理的过程」

1.1 @Controller

Spring Boot 提供了用于声明控制器类的 @Controller 注解。也就是说,在 Spring Boot 项目中,把被 @Controller 注解标注的类称作「控制器类」。控制器类在 Spring Boot 项目中发挥的作用是处理用户发送的 HTTP 请求。

Spring Boot 会把不同的用户请求交给不同的控制器进行处理,而控制器则会把处理后得到的结果反馈给用户。

说明:
控制器(controller)定义了应用程序的行为,它负责对用户发送的请求进行解释,并把这些请求映射成相应的行为。

因为 @Controller 注解本身被 @Component 注解标注:

@Component
public @interface Controller {

所以控制器类属于组件。这说明在启动 Spring Boot 项目时,控制器类会被扫描器自动扫描。这样,程序开发人员就可以在控制器类中注入 Bean。例如,在控制器中注入 Environment 环境组件,代码如下:

​​​​@Controller
​​​​public class TestController {
​​​​    @Autowired
​​​​    Environment env;
​​​​}​​

1.2 @RequestMapping

Spring Boot 提供了用于映射 URL 地址的 @RequestMapping 注解。@RequestMapping 注解可以标注类和方法。如果一个类或者方法被 @RequestMapping 注解标注,那么这个类或者方法就能够「处理用户通过 @RequestMapping 注解映射的 URL 地址发送的请求」

下面将首先介绍 @RequestMapping 注解的属性,然后介绍如何使用 @RequestMapping 注解映射包含层级关系的 URL 地址。

注意:
@Controller 注解要结合 @RequestMapping 注解一起使用。

属性:value

value 属性是 @RequestMapping 注解的默认属性,用于指定映射的 URL 地址。在单独使用 value 属性时,value 属性可以被隐式调用。调用 value 属性的语法如下:

​​​​@RequestMapping("test")
​​​​@RequestMapping("/test")
​​​​@RequestMapping(value= "/test")
​​​​@RequestMapping(value={"/test"})​​

上面这 4 种语法所映射的 URL 地址均为“域名/test”。其中,域名指的是当前 Spring Boot 项目所在的域。如果你在在 IntelliJ IDEA 中启动一个 Spring Boot 项目,那么域名默认为:

http://localhost:8080 或 http://127.0.0.1:8080

比如我们创建一个 TestController:

@Controller
public class TestController {
    @RequestMapping("/index")       // 映射的 URL 地址为 /index
    @ResponseBody                   // 直接将字符串显示在页面上 
    public String test(){
        return "欢迎访问我的主页";
    }
}

在浏览器上访问 http://127.0.0.1:8080/index 地址:

在这里插入图片描述

说明:
如果一个方法被 @ResponseBody 注解标注,那么由这个方法返回的字符串将被直接显示在浏览器的页面上。

同时,@RequestMapping 注解映射的 URL 地址也可以是多层的。例如:

​​​​@RequestMapping("/shop/books/computer")​​

上述代码映射的完整URL地址是:http://127.0.0.1:8080/shop/books/computer。需要特别注意的是,这个 URL 地址中的任何一层都是不可或缺的,否则将引发 404 错误

另外,@RequestMapping 注解允许一个方法同时映射多个 URL 地址。其语法如下:

​​​​@RequestMapping(value = { "/address1", "/address2", "/address3", ....... })​​

属性:method

method 属性能够指定用户通过 @RequestMapping 注解映射的 URL 地址「发送的请求的类型」。这样,使用 method 属性就能够让不同的方法处理由相同 URL 地址发送的不同类型的请求。

下面将通过一个实例演示 method 属性的用法,我们修改 TestController 类:

  1. 如果由 “/index” 地址发送的请求的类型是 GET,则打印“处理 GET 请求”
  2. 如果由 “/index” 地址发送的请求的类型是 POST 请求,则打印“处理 POST 请求”
@Controller
public class TestController {
    @RequestMapping(value = "/index", method = RequestMethod.GET)
    @ResponseBody
    public String get(){
        return "处理 GET 请求";
    }

    @RequestMapping(value = "/index", method = RequestMethod.POST)
    @ResponseBody
    public String post(){
        return "处理 POST 请求";
    }
}

使用 Postman 模拟 GET 请求和 POST 请求,所示结果如下。如果发送的请求既不是 GET 类型也不是 POST 类型,则会触发 405 错误。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

由 URL 地址发送的请求具有多种类型,详见 RequestMethod 枚举类。

RequestMethod 枚举类的代码如下:

public enum RequestMethod {
    GET,
    HEAD,
    POST,
    PUT,
    PATCH,
    DELETE,
    OPTIONS,
    TRACE;
}

属性:params

params 属性能够指定在用户通过 @RequestMapping 注解映射的 URL 地址发送的请求中「须包含哪些参数」。因为 params 属性的类型是「字符串数组」,所以通过 params 属性能够同时指定多个参数。

下面将通过一个实例演示 params 属性的用法,我们修改 TestController:

@Controller
public class TestController {
    @RequestMapping(value = "/index", params = { "name", "id" })
    @ResponseBody
    public String haveParams(){
        return "欢迎回来~";
    }

    @RequestMapping(value = "/index")
    @ResponseBody
    public String noParams(){
        return "忘传参数了...";
    }
}

使用 Postman 模拟用户通过 URL 地址发送的请求。查看结果:

在这里插入图片描述
在这里插入图片描述

属性:headers

headers 属性能够指定在用户通过 @RequestMapping 注解映射的 URL 地址发送的请求中「须包含哪些指定的请求头」。也就是说,在一个 headers 属性中,可以包含若干个请求头。通过这些请求头,服务器能够得知客户端环境以及与请求正文相关的一些信息,例如浏览器的版本、请求参数的长度等。

headers 属性在 @RequestMapping 注解中的格式如下:

​​​​@RequestMapping(headers = {"键1=值1", "键2=值2", ......})​​

说明:
请求头指的是 HTTP 请求中的头部信息,即用于 HTTP 通信的操作参数。

从 headers 属性在 @RequestMapping 注解中的格式,能够非常清晰地看到请求头在 headers 属性中的格式:

​​​​"键=值"​​

我们测试下,修改 TestController,在控制器类中编写 noParams() 和 haveParams() 这两个方法。如果用户通过 URL 地址发送的请求包含 headers 属性,就交由 havaParams() 方法处理,让用户直接进入欢迎界面;否则,就交由 noParams() 方法处理,让用户进入登录界面。

@Controller
public class TestController {
    @RequestMapping(value = "/index")
    @ResponseBody
    public String noParams(){
        return "请重新登录!";
    }

    @RequestMapping(value = "/index", headers = { "Cookie=JSESSIONID=123456789"})
    @ResponseBody
    public String havaParams(){
        return "欢迎回来~";
    }
}

说明:
Cookie 是某些网站为了辨别用户身份、进行 Session 跟踪而储存在用户本地终端上的数据。Cookie 会被暂时地或永久地保存在用户客户端计算机中。
对于本示例,如果用户在某个登录界面选择了“自动登录”选项,那么服务器就会将用户登录的 session id 写在浏览器的 Cookie 中。

使用 Postman 模拟用户通过 URL 地址发送的请求。

  1. 如果在请求中不包含 headers 属性,访问 http://127.0.0.1:8080/index 地址:

在这里插入图片描述

  1. 如果为请求头添加 Cookie,值为“JSESSIONID=123456789”,再访问同一个地址:

在这里插入图片描述

属性:consumes

consumes 属性能够指定用户通过 @RequestMapping 注解映射的 URL 地址发送的请求的「数据类型」。其中,常见的类型有 “application/json”、“text/html” 等。

下面将通过一个实例演示 consumes 属性的用法。

修改 TestController 类,将 @RequestMapping 注解的 consumes 属性设置为“application/json”。在控制器类中编写 formatError() 和 hello() 这两个方法。如果用户发送的请求的数据类型是 JSON,就交由 hello() 方法处理,并提示“成功进入接口”的信息;否则,就交由 formatError() 方法处理,并提示“数据格式错误!”的信息。

@Controller
public class TestController {
    @RequestMapping(value = "/index")
    @ResponseBody
    public String formatError(){
        return "数据格式错误!";
    }

    @RequestMapping(value = "/index", consumes = "application/json")
    @ResponseBody
    public String hello(){
        return "成功进入接口";
    }
}

使用 Postman 模拟用户通过URL地址发送的请求。如果直接访问 http://127.0.0.1:8080/index 地址,结果如下:

在这里插入图片描述

如果在请求体(Body)中填写 JSON 数据,再访问上述地址就可以看到下图结果:

在这里插入图片描述

层级 URL

通常一个 URL 地址不只是简单的一层地址,而是根据业务分类形成的多层地址。那么,如何理解在一个 URL 地址中包含多层地址呢?

例如,在某电商平台通过访问 “/shop/books” 地址查看图书信息;其中,“/shop” 是这个电商平台的地址,“/books” 表示图书类。那么,应该如何使用 @RequestMapping 注解映射这个包含层级关系的 URL 地址呢?

代码如下:

@Controller
public class BeanTestController {
    @RequestMapping("/shop/books")
    @ResponseBody
    public String books() {
        return "图书类";
    }
}

不难发现,在表示电商平台的控制器类中包含一个 book() 方法。通过使用 @RequestMapping 注解标注这个 book() 方法,就能够实现映射一个多层的 URL 地址的功能。

如果这个电商平台还卖服装,那么就会含有表示服装类的 “/clothes” 地址。那么,又应该如何使用 @RequestMapping 注解既映射 “/shop/books” 地址,又映射 “/shop/clothes” 地址呢?代码如下:

@Controller
public class BeanTestController {
    @RequestMapping("/shop/clothes")
    @ResponseBody
    public String cloths() {
        return "服饰类";
    }

    @RequestMapping("/shop/books")
    @ResponseBody
    public String books() {
        return "图书类";
    }
}

不难发现,“/shop/books” 和 “/shop/clothes” 这两个地址具有相同的上层地址 “/shop”。那么,有没有什么编码方式能够优化上述代码呢?

答案是使用 @RequestMapping(“/shop”) 注解标注表示电商平台的「控制器类」

优化后的代码如下:

@Controller
@RequestMapping("/shop")
public class BeanTestController {
    @RequestMapping("/clothes")
    @ResponseBody
    public String cloths() {
        return "服饰类";
    }

    @RequestMapping("/books")
    @ResponseBody
    public String books() {
        return "图书类";
    }
}

说明:
前面我们就提到过:@RequestMapping 注解不仅可以标注方法,还可以标注类。

在访问一个多层的 URL 地址时,输入的 URL 地址必须是完整的,比如下面的效果图:

在这里插入图片描述
在这里插入图片描述

1.3 @ResponseBody

在上文讲解的所有实例中,其中的方法都被 @RequestMapping@ResponseBody 注解同时标注。在掌握了 @RequestMapping 注解的相关内容后,下面将介绍 @ResponseBody 注解的作用。

@ResponseBody 注解的作用是把被 @ResponseBody 注解标注的方法的返回值「转换为页面数据」。

  • 如果被 @ResponseBody 注解标注的方法的返回值是字符串,页面就会显示字符串;
  • 如果被 @ResponseBody 注解标注的方法的返回值是其他类型的数据,这些数据就会先被自动封装成 JSON 格式的字符串,再显示在页面中。

下面将介绍在使用 @ResponseBody 注解时会遇到的另外一种情况:如果控制器类中的某个方法被 @RequestMapping 注解标注,却没有被 @ResponseBody 标注,那么这个方法的返回值会是什么呢?

答案是即将跳转的 URL 地址。例如:

@Controller
public class TestController {
    @RequestMapping("/index")                  // 映射 "/index" 地址,未标注 @ResponseBody
    public ModelAndView index(){
        return new ModelAndView("/welcome");   // 跳转至 "/welcome" 地址
    }
}

在上述代码中,index() 方法被 @RequestMapping 注解标注,却没有被 @ResponseBody 标注。该方法的返回值是 org.springframework.web.servlet.ModelAndView 类型。

因为上述代码的功能是当用户访问 “/index” 地址时,页面就会跳转至与 “/welcome” 地址对应的页面,所以可以把 index() 方法的返回值修改为字符串。修改后的代码如下:

@Controller
public class BeanTestController {
    @RequestMapping("/index")                  // 映射 "/index" 地址,未标注 @ResponseBody
    public String index(){
        return "/welcome";                     // 跳转至 "/welcome" 地址
    }
}

通过上述代码,是不是就能够实现跳转页面的功能了呢?答案是否定的。为了实现跳转页面的功能,还需要向上述代码添加用于映射 “/welcome” 地址的方法,并且这个方法要被 @RequestMapping 和 @ResponseBody 注解同时标注。添加用于映射 “/welcome” 地址的方法后的代码如下:

@Controller
public class BeanTestController {
    @RequestMapping("/index")
    public String index(){
        return "/welcome";
    }

    @RequestMapping("/welcome")
    @ResponseBody
    public String welcome(){
        return "欢迎来到我的主页";
    }
}

启动项目后,打开浏览器访问 http://127.0.0.1:8080/index 地址,即可看到页面会跳转至与 “/welcome” 地址对应的页面:

在这里插入图片描述

此外,在使用 @ResponseBody 注解时还需要特别注意一个问题:@ResponseBody 注解虽然也可以标注控制器类,但是控制器类中的所有方法的返回值都会直接显示在页面上。例如,把上述代码中的 @ResponseBody 注解标注在控制器类上,代码如下:

@Controller
@ResponseBody  // 将注解标注在类上
public class BeanTestController {
    @RequestMapping("/index")
    public String index(){
        return "/welcome";
    }

    @RequestMapping("/welcome")
    public String welcome(){
        return "欢迎来到我的主页";
    }
}

启动项目,打开浏览器访问 http://127.0.0.1:8080/index 地址,会发现页面没有发生跳转,并且显示的结果是index()方法的返回值:

在这里插入图片描述

1.4 @RestController

@RestController 注解虽然是 Spring Boot 的新增注解,但实质上是 @Controller 和 @ResponseBody 这两个注解的综合体。也就是说,当控制器类同时被 @Controller 和 @ResponseBody 这两个注解标注时,这两个注解可以被 @RestController 注解替代。这样就可以起到简化代码的作用。例如:

​​​​@Controller
​​​​@ResponseBody
​​​​public class TestController {
​​​​}​​

使用 @RestController 注解可以简化上述代码。简化后的代码如下:

​​​​@RestController
​​​​public class TestController {
​​​​}​​

二、重定向 URL 地址

重定向 URL 地址是指用户通过原始的 URL 地址发送的请求指向了新的 URL 地址,并且请求中的数据不会被保留。也就是说,通过重定向 URL 地址,服务器可以把用户推送到其他网站上。

下面将介绍 Spring Boot 用于实现重定向的两种方法。

2.1 redirect:

前面一节,我们已经明确了如果控制器类中的某个方法被 @RequestMapping 注解标注,却没有被 @ResponseBody 标注,且这个方法的返回值是字符串,那么这个方法的作用是实现页面跳转的功能,这个方法返回的字符串表示的是即将跳转的 URL 地址。如果在即将跳转的 URL 地址的前面加上“redirect:”,就表示用户通过原始的 URL 地址发送的请求指向了这个 URL 地址。

下面通过一个实例演示“redirect:”前缀的用法。

@Controller
public class BeanTestController {
    @RequestMapping("/bd")
    public String bd(){
        return "redirect:https://www.baidu.com";
    }
}

启动项目后,打开浏览器访问 http://127.0.0.1:8080/bd 地址,浏览器会自动跳转至百度首页,并且地址栏中 URL 地址显示的也是百度首页的 URL 地址,原始的 URL 地址已经在地址栏中看不到了。

在这里插入图片描述

2.2 response

response 对象指的是 HttpServletResponse 类型的对象,可用于实现重定向 URL 的功能。那么,Spring Boot 是如何使用 response 对象实现这个功能的呢?

Spring Boot 可以直接在控制器类的某个方法中创建 response 对象,通过这个对象调用 sendRedirect() 方法就可以指定重定向的 URL 地址。只不过,如果这个方法具有返回值,那么上述操作会导致这个返回值失效。因此,程序开发人员通常会把这个方法的返回值的类型设置为 void。

@Controller
public class BeanTestController {
    @RequestMapping("/bd")
    public void bd(HttpServletResponse response){
        try {
            response.sendRedirect("https://www.baidu.com");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

启动项目后,打开浏览器访问 http://127.0.0.1:8080/bd 地址,即可看到百度首页。


三、解析 URL 地址中的参数

当用户通过 URL 地址发送请求时,Spring Boot 要做的第一个工作就是解析 URL 地址中的参数,从而明确这个请求需要交由控制器类中的哪个方法进行处理。

3.1 自动解析

Spring Boot 能够自动解析 URL 地址中的参数,并将这些参数的值注入某个方法的参数中。想要获取 URL 地址中的参数的值,只需在这个方法中设置「同类型」的、「同名」的参数即可。

例如,URL 地址为 http://127.0.0.1:8080/index?name=tom 的请求,想要获取这个 URL 地址中的 name 参数,可以在控制器类的方法中定义 name 参数,代码如下:

@RestController
public class TestController {
    @RequestMapping("/index")
    public String test(String name){
        System.out.println("name = " + name);
        return "success";
    }
}

在 test() 方法的作用下,Spring Boot 会自动将 URL 地址中参数的值注入同名的方法参数中。上面这段代码将会在控制台输出:

​​​​name = tom​​

使用这种自动识别 URL 地址中参数的功能时,要注意以下几点:

  • Spring Boot 可以识别各种类型的 URL 地址中的参数,例如 GET、POST、PUT 等类型。
  • 参数名区分大小写。
  • 参数没有顺序要求,Spring Boot 会以参数名称作为识别条件。
  • URL 地址中参数的数量可以与方法中参数的数量不一致,只有名称相同的参数才会被注入。
  • 方法参数的类型不应采用基本数据类型。例如,整数应采用 Integer 类型,而不是 int 类型。
  • 如果方法参数没有被注入值,则采用默认值;其中,引用类型默认值为 null。如果方法的某一参数的值为 null,要么是因为 URL 地址没有此参数,要么是因为参数名称不匹配。
  • URL 地址的参数(Request Param)不是请求体(Request Body)。

我们来测试下,写个例子:验证用户发送的账号、密码是否正确。

创建 TestController 控制器类,当用户访问 “/login” 地址时需要向服务发送账号和密码。如果账号是 “David”,密码是 “123456”,则提示用户登录成功;否则,提示用户登录失败。

@RestController
public class TestController {
    @RequestMapping("/login")
    public String login(String username, String password) {
        if (username != null && password != null) {
            if ("David".equals(username) && "123456".equals(password)) {
                return username + ", 欢迎回来!";
            }
        }
        return "您的账号或密码错误!";
    }
}

接下来看下效果:

在这里插入图片描述
在这里插入图片描述

3.2 @RequestParam

Spring Boot 是一个支持海量注解的框架,因此通过注解也能够解析 URL 地址中的参数。Spring Boot 中用于标注方法参数的注解就是 @RequestParam 注解。它的作用是「显式地指定 URL 地址中的参数与方法中的参数之间的映射关系」

@RequestParam 注解的语法如下:

​​​​@RequestMapping("/test")
​​​​public String test(@RequestParam String value1, @RequestParam String value2) {
​​​​    return "";
​​​​}​​

为了能够深入理解 @RequestParam 注解的语法,下面介绍 @RequestParam 注解中的 3 个属性。

属性:value

@RequestParam 注解允许方法中的参数与 URL 地址中的参数不同名,value 属性用于指定 URL 地址中的参数的名称,Spring Boot 会自动将 value 属性的值注入方法参数中。value 属性是 @RequestParam 注解的默认属性,可以隐式调用。

value 属性的两种使用语法如下:

​​​​public String test(@RequestParam("n") String name) { }         // 在语法格式中省略了 "value = "
​​​​public String test(@RequestParam(value = "n") String name) { } // 在语法格式中没有省略 "value = "​

在上述语法中,“n” 是 URL 地址中的参数的名称。在 @RequestParam 注解的作用下,test() 方法中的参数 name 就可以得到 URL 地址中的参数 n 的值。

比如看下面的例子:获取用户发送的 token 口令。

在互联网领域内,token 用以表示指令牌。所谓指令牌,就是许可证、通行证、密码或者口令。当用户向服务器发送 token 口令时,经常把 token 缩写成 “tk”、“tn” 或 “t”。

我们修改 TestController 控制器类,将用户通过 URL 地址发送的请求中参数 tk 的值注入 login() 方法的参数 token 中,打印此口令值。

@RestController
public class TestController {
    @RequestMapping("/login")
    public String login(@RequestParam(value = "tk") String token) {
        return "前端传递的口令为:" + token;
    }
}

打开浏览器,访问 http://127.0.0.1:8080/login?tk=dh6wd84n 地址(参数 tk 的值可任意输入)。

在这里插入图片描述

属性:required

required 属性的作用是指定被 @RequestParam 注解标注的「方法参数是否必须被注入值」,required 属性的默认值为 true。也就是说,对于被 @RequestParam 注解标注且没有写明 “required=false” 的方法参数,一律强制把 URL 地址中的参数的值注入其中。如果 URL 地址没有此参数,就会抛出 MissingServletRequestParameterException(缺少请求参数)异常

以下面这行代码为例,介绍 required 属性的使用方法。

​​​​public String test(@RequestParam(value = "n") String name) { }​​

因为 required 属性的默认值为 true,所以可以对上述代码做如下的修改:

​​​​public String test(@RequestParam(value = "n" , required = true) String name) { }​​

当在 @RequestParam 注解中写明 “required=false” 时,需要对上面的这行代码做如下的修改:

public String test(@RequestParam(required = false) String name) { }​​

需要特别说明的是,对于上述的 test() 方法,如果在 @RequestParam 注解中写明 “required=false”,就等同于如下的不被 @RequestParam 注解标注方法参数的 test() 方法:

​​​​public String test(String name) { }​​

属性:defaultValue

defaultValue 属性的作用是「指定方法参数的默认值」。如果 URL 地址没有 @RequestParam 注解指定的参数,@RequestParam 注解就会将默认值注入方法参数中。

比如我们看下面这个例子:如果用户没有发送用户名,则用“游客”称呼用户。

修改 TestController 控制器类,给 login() 方法中的参数 username 设定默认值(即“游客”)。如果 URL 地址没有参数 name,则让 username 取默认值。

@RestController
public class TestController {
    @RequestMapping("/login")
    public String login(@RequestParam(value = "name", defaultValue = "游客") String username) {
        return username + "您还,欢迎访问 XXX 网站";
    }
}

在这里插入图片描述

3.3 @RequestBody 封装 JSON

@RequestBody 注解的作用是把 URL 地址中的「键值对注入方法参数中」如果 URL 地址中的键值对的数据类型是 JSON 类型,那么 @RequestBody 注解可以把这个 JSON 类型的数据直接封装成实体类对象

来看个例子:将 URL 地址中的 JSON 数据封装成 People 类对象。

  1. 首先,定义 URL 地址中的 JSON 数据的结构。JSON 数据中只包含两个键,分别是 id 和 name。其中,id 的值为 26,name 的值为 “David”。因此,JSON 数据的结构如下:
​​​​{ "id": 26, "name": "David" }​​
  1. 然后,定义与 JSON 数据的结构对应的实体类。创建 People 类,其中包含 id 和 name 这两个属性。
public class People {
    private Integer id;
    private String name;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

People 类的属性与 JSON 数据中的字段是一一对应的。这样,就可以使用 @RequestBody 注解将 JSON 数据封装成 People 类的对象。

  1. 修改 TestController 控制器类,用于映射的 index() 方法的参数为 People 类型,并使用 @RequestBody 注解对该参数进行标注。 index() 方法具有返回值,返回的是 People 类型的对象中的 id 和 name 这两个属性的值。
@RestController
public class TestController {
    @RequestMapping("/index")
    public String index(@RequestBody People someone) {
        return "编号:" + someone.getId() + ",用户名:" + someone.getName();
    }
}

使用 Postman 模拟用户通过 URL 地址发送的请求,访问 http://127.0.0.1:8080/index 地址,在 Postman 中设置 JSON 数据,单击 Send 按钮。

在这里插入图片描述

注意:
只有用户通过 URL 地址发送的是 POST 类型的请求,在这个请求中才会包含键值对。

3.4 request response/session

在开发 Spring Boot 项目的过程中,有非常多的程序开发人员都在通过获取 requestresponsesession 这 3 个对象解决问题。

这 3 个对象的各自作用如下:

对象说明
request用于从用户通过 URL 地址发送的请求中接收数据
response用于对已经接收到的数据做出响应
session用于保存用户通过 URL 地址发送的请求,并跟踪这个请求的操作状态

那么,Spring Boot 是如何获取上述的 3 个对象呢?有如下两种方式。

注入属性

Spring Boot 会自动创建 request 对象和 response 对象的 Bean,控制器类可以直接注入这两个 Bean。其中:

  • request 对象的类型是:HttpServletRequest
  • response 对象的类型是:HttpServletResponse。

通过下面这段代码即可在控制器类中注入 request 对象和 response 对象的 Bean:

​​​​@Controller
​​​​public class TestController {
​​​​    @Autowired
​​​​    HttpServletRequest request;
​​​​    @Autowired
​​​​    HttpServletResponse response;
​​​​}​​

具备上述代码后,就可以直接在方法中调用 request 对象和 response 对象。通过 request 对象调用 getSession() 方法,就可以获取 session 对象。其中,session 对象的类型是 HttpSession。获取 session 对象的代码如下:

HttpSession session= request.getSession();​​

注入参数

所谓注入参数,就是直接向控制器类的用于处理 HTTP 请求的方法添加 HttpServletRequest、HttpServletResponse 和 HttpSession 类型的对象(即 request、response 和 session 这 3 个对象)的过程。

例如,同时在一个用于处理 HTTP 请求的方法中调用 request、response 和 session 对象。代码如下:

@RestController
public class TestController {
    @RequestMapping("/index")
    public String index(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
        request.setAttribute("id", "test");
        response.setHeader("Host", "www.baidu.com");
        session.setAttribute("userLogin", "true");
        return "";
    }
}

因为注入参数的方式只对参数类型有要求,对参数名称、参数顺序没有要求,所以上述用于定义 index() 方法的代码可以写成下面的形式:

​​​​public String index( HttpServletResponse rp,HttpSession s,HttpServletRequest rq) { }​​

在实际开发中,HttpServletRequest、HttpServletResponse 和 HttpSession 类型的对象可以与其他参数一起使用。例如:

​​​​public String index(@RequestParam("tk") String token, HttpServletRequest rq, Integer id) { }​​

我们测试一下:服务器返回图片

程序开发人员可以通过 response 对象向浏览器页面发送任何类型的数据,例如文字、图片、文件。我们修改 TestController 控制器类,映射 “/image” 地址,读取 URL 地址中参数 massage 的值。通过 BufferedImage 类创建图片对象,将参数 massage 中的文字显示在图片上。

@RestController
public class TestController {
    @RequestMapping("/image")
    public void image(String message, HttpServletResponse response) {
        // 创建宽 300、高 100 的缓冲图片
        BufferedImage image = new BufferedImage(300, 100, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();                           // 获取绘图对象
        g.setColor(Color.BLUE);                                     // 画笔为蓝色
        g.fillRect(0, 0, image.getWidth(), image.getHeight());      // 覆盖图片的实心矩形
        g.setColor(Color.WHITE);                                    // 画笔为白色
        g.setFont(new Font("Times New Roman", Font.BOLD, 20));      // 字体
        g.drawString(message, 10, 50);                              // 将参数字符串绘制在制定坐标上
        try {
            // 将绘制好的额图片,写入 response 的输出流中
            ImageIO.write(image, "jpg", response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

打开浏览器,访问 “http://127.0.0.1:8080/image?massage=好好学习,天天向上” 地址:

在这里插入图片描述


四、RESTful 风格映射动态 URL 地址

4.1 RESTful 风格

REST 是一种软件架构规则,其全称是 representational state transfer,中文直译是「表述性状态传递」,可以简单地理解成“让 HTTP 请求用最简洁、最直观的方式来表达自己想要做什么”。基于 REST 构建的 API 就属于 RESTful 风格。

很多网站的 HTTP 请求中的参数都是通过 GET 方式发送的。例如,某用户访问某电商网站的 “/details” 地址,想获取编号为 5678 的图书的详细信息;在传入商品编号和商品类型这两个参数后,URL 地址可能是这样的:

​​​​http://127.0.0.1:8080/shop/details?id=5678&item_type=book​​

如果后台服务采用的是 RESTful 风格,那么获取商品详情就不需要传入任何参数了。因此,在使用 RESTful 风格后,上述的 URL 地址可能会是这样的:

​​​​http://127.0.0.1:8080/shop/book/5678​​

从这个地址可以看出,“商品的类型”和“编号”从 URL 地址的参数变为了 URL 地址的其中一层。这种地址也被叫作资源地址(URI),它看起来更像是在访问一个 Web 资源而不是用户通过 URL 地址发送的请求。当下,用户在互联网上已经可以看到很多采用 RESTful 风格的网站了。

例如,在京东商城上查看具体商品的地址如下:

​​​​https://item.jd.com/12185501.html​​

再例如,在 GitHub 上访问某个开源软件的地址如下:

​​​​https://github.com/spring-projects/spring-boot​​

说明:
URI 全名为 uniform resource identifier,翻译过来叫「统一资源标识符」。URL 的全名为 uniform resource locator,翻译过来叫「统一资源定位器」。注意两者的区别。

RESTful 风格也存在一个问题:仅从 URL 地址上看不出这个请求是要查询商品还是删除商品。为此,在 REST 规则中,规定以用户通过 URL 地址发送的请求的类型来决定要执行哪种业务。

例如,对于同一个 URL 地址:

  1. 如果用户通过 URL 地址发送的请求为 GET 请求,则表示「查询指定商品的数据」;
  2. 如果用户通过 URL 地址发送的请求为 POST 请求,则表示「向数据库添加指定商品」;
  3. 如果用户通过 URL 地址发送的请求为 DELETE 请求,则表示「删除指定商品」。

HTTP 请求包含多种类型,常用类型及其相关内容如表所示:

请求类型约定的义务对应枚举对应注解
GET查询资源RequestMethod.GET@GetMapping
POST创建资源RequestMethod.POST@PostMapping
PUT更新资源RequestMethod.PUT@PutMapping
PATCH只更新资源中一部分内容RequestMethod.PATCH@PatchMapping
DELETE删除资源RequestMethod.DELETE@DeleteMapping

与各个 HTTP 请求类型对应的「枚举」用于为 @RequestMapping 注解的 method 属性赋值,进而用于指定映射的是哪种类型的 HTTP 请求。例如,只映射 POST 请求的写法如下:

@RequestMapping(value="/index",method = RequestMethod.POST)​​

与各个 HTTP 请求类型对应的「注解」则是在@RequestMapping 注解的基础上延伸出来的新注解,这些注解的功能与 @RequestMapping 注解相同,但只能映射固定类型的请求。例如下面这个注解:

​​​​@GetMapping(value="/index")​​

上述注解等同于如下的注解:

​​​​@RequestMapping(value="/index",method = RequestMethod.GET)​​

说明:
不是所有的浏览器都支持发送多种请求类型,例如 HTML 格式的浏览器仅支持发送 GET 请求和 POST 请求,这种浏览器对于其他类型的 HTTP 请求需要借助 JavaScript 格式的浏览器予以发送。

4.2 映射动态 URL 地址

服务器使用 RESTful 风格就意味着控制器类所映射的 URL 地址不是唯一的,每一个数据的 URL 地址都不同。这就需要让控制器类能够映射动态 URL 地址。Spring Boot 使用英文格式的 “{}” 作为动态 URL 地址中的「占位符」,例如 “/shop/book/{type}” 就表示这个 URL 地址中的 “/shop/book/” 是固定的,“/{type}” 是动态的,该动态地址可以成功匹配下面这些地址:

http://127.0.0.1:8080/shop/book/music
​​​​http://127.0.0.1:8080/shop/book/FOOD
​​​​http://127.0.0.1:8080/shop/book/123456789
​​​​http://127.0.0.1:8080/shop/book/+-*_!#&$
​​​​http://127.0.0.1:8080/shop/book/{}()<>
​​​​http://127.0.0.1:8080/shop/book/数学
​​​​http://127.0.0.1:8080/shop/book/(空格)
​​​​……​​

为了解析动态 URL 地址中的占位符,Spring Boot 提供了 @PathVariable 注解。@PathVariable 注解可以将 URL 相应位置的值注入方法参数。例如下面这段代码:

​​​​@RequestMapping(value = "/shop/{type}/{id}")
​​​​public String shop(@PathVariable String type, @PathVariable String id) { }​​

@PathVariable 注解可以将 URL 地址中与 {type} 占位符对应位置的值注入 shop() 方法中的参数 type,将 URL 地址中与 {id} 对应位置的值注入 shop() 方法中的参数 id。因为 @PathVariable 注解会根据方法参数的名称进行匹配,所以方法参数的先后顺序不影响注入的结果。

在实际开发中,程序开发人员也可以指定方法参数对应哪个占位符,只需将 @PathVariable 注解的 value 属性赋值为占位符名称即可。例如,方法参数的名称分别为 goodsType 和 goodsID,它们各自要匹配的占位符分别是 {type} 和 {id}。代码如下:

​​​​@RequestMapping(value = "/shop/{type}/{id}")
​​​​public String shop(@PathVariable(value = "type") String goodsType,
​​​​        @PathVariable(value = "id") String goodsID) { }​​

因为 @PathVariable 注解的 value 属性可以被隐式调用,所以上述代码可以做如下修改:

​​​​@RequestMapping(value = "/shop/{type}/{id}")
​​​​public String shop(@PathVariable("type") String goodsType, @PathVariable("id") String goodsID) { }​​
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值