Spring Web MVC 入门:从一个 URL 到 Java 后端完整处理请求

目录

1.进入Spring MVC的准备工作

2.什么是Spring MVC

3.学习Spring MVC的三个要点

一.进入Spring MVC的准备工作

前言:

在前面的博客里面,我们写了一个简单的controller,我们在浏览器里面输入网址:http://127.0.0.1:8080/sayHi之后,就会看到一个hello,spring的返回,这就是一个简单的前后端的交互,那我们还有好多问题要问:为什么浏览器能够访问我们的Java程序,8080是谁在监听,我们为什么要写8080这个端口,以及后续我们要传一些数据,类似name,age这些,后端又如何进行接收,在进入Spring MVC之前,我们好需要学习一些基础的知识

Tomcat

在我们之前写的慢方法中,我们不会有浏览器的交互,因为没有东西帮助我们监听网络端口,等待浏览器连接,解析http请求以及返回响应,而这些都是我们现在要介绍的这个Tomcat所干的活,可以理解为Tomcat 是一个能够运行 Java Web 应用的 Servlet 容器,同时能够提供 HTTP 服务。并且Tomcat就是负责监听8080端口的

整个流程可以理解为这样

Spring Boot--------启动内嵌 Tomcat-----------Tomcat 监听 8080-----------等待客户端请求

Servlet

这个是一套Java web开发规范,规定程序怎么接收请求,怎么处理请求以及产生响应,而tomcat就是实现了servlet规范,所以又称Tomcat这类的为Servlet容器,但是Tomcat只是实现了说请求的接收,可以调用Servlet,这里的Servlet并不是规范了,而是一个接口,提供了一些必要的方法,当然我们可以通过Tomcat来调用我们自己重写实现的Servlet类,但是这样很麻烦,这样我们需要自己在代码中处理读取请求参数,判断URL,处理JSON之类的,所以我们后面引入了今天的主角spring mvc

二、什么是 Spring MVC?

Spring MVC是什么

Spring MVC 是又在 Servlet 的基础上进行了进一步封装。

所以我们处理请求只需要写这样的方法,大量底层的读取判断构造都由这个框架做好了的

java 复制代码
@GetMapping("/user")
public String getUser() {
    return "hello";
}

所以我们结合一下刚刚说的Tomcat

**Tomcat:**主要负责接收 HTTP 请求、提供 Servlet 运行环境、发送 HTTP 响应。

**Spring MVC:**主要负责找到对应的 Controller 方法、接收和转换参数、处理返回结果。

Spring MVC不仅是一个web框架,也是mvc思想的一种巨像表现

什么是MVC

M:Model(负责数据和业务的相关处理)

V:View(负责和用户交互,展示数据)

C:Controller(负责接收请求,并连接model和view)

可以将view理解为是去饭店的顾客,controller是前台,model是后厨,MVC就是为了让每一步都更加的明确

那我们可能会说之前的spring boot框架我们还没怎么熟悉,怎么就又有一个新的框架了,其实我们早就解除了spring mvc,当我们创建spring boot项目的时候,我们就选择的是创建spring web项目,因为我们是网页的开发,当我们选择这个时候,spring web就把web开发需要的spring mvc能力带进来了,所以我们之前也是使用过的,只是没有系统的认识这个

三.学习Spring MVC的三个要点

spring mvc入门需要掌握建立连接,请求,以及响应这三点

准确来说就是怎么找到对应的Java方法,请求里的数据怎么进入Java,处理完的结果怎么返回客户端

路由映射(如何找到Java方法)

当我们在浏览器访问之前的那个网址,怎么就对应了说是这个sayHi方法,前面的127.0.0.1:8080是找到具体的网络服务,而后面的sayHi就是找到了具体的对应方法,所以显示了hello,spring,这里就引入了一个注解:**@RequestMapping,**这个后面填对应的路径,通过这个就可以用URL将请求对应到具体的Java方法上了

java 复制代码
@RestController
@RequestMapping("/hi")
public class UserController {
    @RequestMapping("/sayHi")
    public String sayHi() {
        return "hello,Spring";
    }
}

这个可以写到类上,也可以写到方法上面,所以当我们这次要找这个方法的时候,我们就需要在URL里面添加一段,最后就是127.0.0.1:8080/hi/sayHi这样,我们也可以通过这个例子来了解一下,这样的话我们的方法就是一级一级的下去寻找

GET 和 POST 到这里终于真正开始使用

之前我们在学习http的时候,我们知道get以及post是http的请求方法,到了我们这里,我们就需要学习Java后端该如何处理这些请求,当我们在浏览器输入刚刚的网址的时候,我们通过抓包工具也可以看到,浏览器其实是帮我们构造了一个GET请求的

介绍几个相关的注解

@GetMapping:只有向对应方法发送GET请求的时候,方法才会执行

@PostMapping:只有向对应方法发送POST请求的时候,方法才会执行

当我们没有指定一定要哪种的时候,就是@RequestMapping,这个可以默认匹配多种请求方法

Postman

这个之前我们也介绍过,这是一款构造请求的工具,当然我之前介绍的好像是apifox,不过两款软件都可以模拟前端构造请求给后端,就不需要我们再写一个前端的页面了

我们学习这一块的知识就可以配合着postman以及fiddler抓包工具来学习

简单来介绍一下这个软件的使用,先介绍里面的传参位置

**Params:**当我们需要用户端返回一个数据的时候,就可以在这里面构造

java 复制代码
@RequestMapping("/r1")
    public String r1(String name,Integer age) {

        return name+age+"岁";
    }

当我们把方法改成post之后,我们就可以在Body 的raw 里面写JSON了

接收请求参数(请求里的数据怎么进入Java)

1,常见的接收方式

这张表格是我们接下来学习接收参数需要一直围绕的一张表格!!!

参数来源 接收方式
URL 查询参数 ?name=Tom @RequestParam
URL 路径 /user/10 @PathVariable
JSON 请求体 @RequestBody
请求头 @RequestHeader
Cookie @CookieValue
表单数据 普通参数 / JavaBean
上传的文件 MultipartFile

当我们已经会如何找到对应的方法之后,我们就要进入一个重头戏,就是接收请求来的参数了,我们需要根据用户传来的数据,通过Spring MVC里面的解析以及类型转换,将对应的数据对应到Java方法的参数,当然这个过程是Spring MVC自己会做的

java 复制代码
@RequestMapping("/r1")
    public String r1(String name,Integer age) {

        return name+age+"岁";
    }

我们通过postman构造的请求中有age以及name,spring会根据对应的参数名去进行匹配,我哪一个参数写在前面都没事,主要是age以及name相对应(参数名相对应),

推荐包装类型

因为如果当我们写的是public String r1(int age)的时候,我们传入的参数不能为空,也就是说我们必须传入参数,要不然就会出现报错,所以稳妥的时候尽量使用包装类

2,多参数的时候定义类

当我们的参数很多的时候,我们最好是可以定义一个类来保存这些,也就是比如我们像上面一样我们需要对象传入一个个人信息,我们有两种写法

第一种:

java 复制代码
@RequestMapping("/r2")
    public String r2(String name,Integer age,Integer userid,String email) {

        return name+age+userid+email;
    }

第二种:

java 复制代码
public class User {
    private Integer userid;
    private String name;
    private String email;
    private Integer age;

    public Integer getUserid() {
        return userid;
    }

    public void setUserid(Integer userid) {
        this.userid = userid;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "userid=" + userid +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", age=" + age +
                '}';
    }
}
java 复制代码
@RequestMapping("/r3")
    public String r3(User user){
        return user.toString();
    }

这样如果参数更加多的时候,我们最好就是将所有的参数定义到统一的一个类里面,这样也方便我们管理,当然定义了User类,我们在postman里面构造的时候也需要跟前面一样,一个参数一个参数拆开来构造

3,@RequestParam(URL 查询参数 ?name=zahngsan)

它主要负责:从 HTTP 请求参数中获取数据,并将其绑定到 Controller 方法的形参上。

两个重要的作用1:改变参数名,2:获取对应的参数并绑定到方法中

1:改变参数名

当我们前端传来的参数名字叫username的时候,但是我们后端又想使用name这个参数名进行存储,这个时候就可以使用到这个了

java 复制代码
@RequestMapping("/r4")
    public String r4(@RequestParam("username") String name){
        return name;
    }

也就是可以理解为说,找到名字叫username这个参数的数据,传过来对应到我后端自己的name中

2:获取对应的参数并绑定到方法中

java 复制代码
@RequestMapping("/r5")
    public String r5(@RequestParam("like")List<String> list){
        return list.toString();
    }

因为List是一个接口,我们想要传入参数的时候,必须要是实现这个接口的类创建的对象,比如Arraylist,但是当我们传入几个like也同样可以被接受到,这是为什么,因为

Spring MVC 会自动完成:

  1. 根据 @RequestParam("tag") 找到请求中的三个值。

  2. 根据 List<String> 判断需要转换为字符串集合。

  3. 通过类型转换机制得到一个实现了 List 接口的集合对象。

  4. 将这个对象传递给 r1() 方法

也就是说相当于它帮我们创建了一个Arraylist对象,找到对应的like数据,然后add进去了,并且我们在postman中使用的时候,使用一个个like传可以,使用,分割一次性传进去也可以,

4,JSON

我们现在很多的项目并不是像我刚刚那么传输数据的,就类似于说?name=zhangsan&age=18这样,而是通过JSON这样的数据格式进行传输的,JSON是脱离编程语言的一种数据传输格式,也就是说我在C语言里面可以使用跟Java中同样的一份JSON文件,并且JSON还很轻量化

java 复制代码
键值对使用 :

多个数据使用 ,

对象使用 {}

数组使用 []

5,Jackson

当我们的JSON数据传过来的时候,我们就需要有人讲JSON数据转为Java对象,这样的工作就是Jackson来干的,JSON转为Java对象叫做反序列化,反过来就叫序列化

6,@RequestBody(JSON请求体)

JSON一般传输给后端都是写在http请求的body里面的,所以我们在后端中要使用一个注解进行提示

java 复制代码
@PostMapping("/r6")
    public String add(@RequestBody User user) {
        return user.toString();
    }

7,一些方法的约定

前面你肯定也听说了说这些get,post方法有一些规定,但是程序员是可以不按照这些规定来的,那这到底是什么意思呢:

比如说GET一般是规定获取数据的,然后DELETE是删除数据,但是这只是一个规定,并不是强制的执行,所以我们可以有两种代码的写法

java 复制代码
@DeleteMapping("/user/{id}")
public String deleteUser(@PathVariable Integer id) {
    // 删除用户
    return "删除成功";
}
java 复制代码
@GetMapping("/deleteUser")
public String deleteUser(@RequestParam Integer id) {
    // 删除用户
    return "删除成功";
}

这两种方式都是正确的,下面这种方式只需要让问GET/deleteUser?id=1001也就是可以删除的,但是这样是违反了语义安全,所以还是不是很建议你们写下面这种,一般就是一种方法对应一类问题

8,@PathVariable(URL 路径 /user/10)

当我们的数据是写在URL里面的时候,我们就需要使用到这个参数,这里的{id}是一个路径占位符,也就是说这个位置是可以接收不同的值的

你可能会想那什么情况会写在路径里面,毕竟我们刚刚写的基本上都是在Query里面,那通常我们定义一个商品的id,一个文章的id的时候一般就会写在路径里面,可以让 URL 能够清晰地标识一个具体的资源。就例如:GET /article/10这种,就很好的标识了文章10

java 复制代码
 @RequestMapping("/r7/{id}")
    public String getUser(
            @PathVariable Integer id) {

        return "id=" + id;
    }

9,@RequestHeader(接收请求头)

再回顾一下http请求的内容,我圈出来的部分都是header的部分,那我们后端将如何获得呢(两种方式,上面获得请求都是有两种方式的,一种是使用HttpServletRequest这个类,还有一种就是可能会提供的注解)

java 复制代码
    @RequestMapping("/getHeader")
    public String getHeader(HttpServletRequest request){
        String UserAgent=request.getHeader("User-Agent");
        return "UserAgent:"+UserAgent;
    }
    @RequestMapping("/getHeader2")
    public String getHeader2(@RequestHeader("User-Agent")String UserAgent){
        return "UserAgent:"+UserAgent;
    }

10,cookie和session

我们再来回顾一下之前学习的cookie以及session的相关知识点,就是当我们登录一个网页,在短时间内,我们重新登录这个网页的时候,他就相当于是会记住我们,就是我们不需要再次重复登录也可以正常访问到刚刚的内容,这是怎么做到的呢,明明我们的http是无状态的,这意味着http协议本身不会自动记住两次请求之间的用户关系,这时候cookie以及session就派上用场了,整个流程就是浏览器登录的时候,发送用户名以及密码,后端验证用户信息没错之后创建或者获取session,来保存用户信息,然后服务端通过http响应的set-cookie,返回一个sessionid,客户端就会存储这个sessionid在cookie里面,后续我们访问的时候就会带着这个sessionid给后端,后端就根据这个来验证我们的信息就好了,我们真实在后端写设置session的时候,不会来一个用户设置一个,而是编写统一的登录逻辑。自动根据不同的用户创建不同的session,session一般就是我们后端来管理,前端一般是访问不到的,毕竟存储了用户的一些密码啥的,cookie一般存储的就是sessionid以及一些主题,语言之类的,而session一般存储的是id,登录信息以及权限,我们可以通过f12的方式来看我们客户端存储的cookie

1,设置和获取cookie

cookie由于是在客户端的,所以我们可以自己手动添加一些信息,你想写什么写什么,但是你在百度的网站下面写的话他们的后端是不会去认可识别的

java 复制代码
@RequestMapping("/getcookie")
    public String getcookie(HttpServletRequest request){
        Cookie[] cookies=request.getCookies();
        if (cookies!=null){
            for (Cookie cookie:cookies){
                System.out.println(cookie.getName()+"="+cookie.getValue());
            }
        }
        return "获取cookie成功";
    }
    @RequestMapping("/getcookie2")
    public String getcookie2(@CookieValue("username") String username){
        return "获取cookie成功,username:"+username;
    }
    @RequestMapping("/setcookie")
    public String setcookie(HttpServletResponse response) {
        // 1. 创建 Cookie 对象
        Cookie cookie = new Cookie("username", "zhangsan");
        // 2. 设置 Cookie 的有效期(单位:秒)
        cookie.setMaxAge(60 * 60);
        // 3. 设置 Cookie 的路径
        cookie.setPath("/");
        // 4. 将 Cookie 添加到 HTTP 响应中
        response.addCookie(cookie);
        return "添加Cookie成功";
    }
2,设置和获取session
java 复制代码
    @RequestMapping("/setsession")
    public String setsession(HttpServletRequest request){
        //先获取session对象,没有的话会先创建一个
        HttpSession session = request.getSession();
        session.setAttribute("username","zhangsan");
        session.setAttribute("userid","1234");
        return "设置session成功";
    }
    @RequestMapping("/getsession")
    public String getsession(HttpServletRequest request){
        //先获取session对象,没有的话会先创建一个
        HttpSession session = request.getSession();
        System.out.println(session.getAttribute("username"));
        System.out.println(session.getAttribute("userid"));
        return "获取session成功";
    }
    @RequestMapping("/getsession2")
    public String getsession2(@SessionAttribute("username")String username){
        return "获取session成功,name:"+username;
    }
    @RequestMapping("/getsession3")
    public String getsession3(HttpSession session){
        return "获取session成功,name:"+session.getAttribute("username");
    }

响应(处理完的结果怎么返回客户端)

1,返回静态HTML页面(利用网址/利用代码)

我们先在这个目录底下创建一个html文件,然后在浏览器里面输入网址就可以访问到网页了

复制代码
http://localhost:8080/index.html
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Spring MVC 测试</title>
</head>
<body>
    <h1>欢迎学习 Spring MVC!</h1>
    <p>这是 Spring Boot 提供的静态页面。</p>

    <button onclick="alert('点击成功')">
        点击我
    </button>
</body>
</html>

这是我们不写controller,也可以直接访问到网页的,并且当你的网址不写后面的/index.html也是可以正常访问到相同的界面的,因为我们的Spring Boot会默认自动去处理static下的index页面,所以这个取名也是比较关键的

那如果我们想要用户不直接输入网页名称的后缀名index.html也可以访问到网页的话就需要写controller来跳转了

java 复制代码
    @Controller
    public class PageController {
        @GetMapping("/html")
        public String html() {
            return "/index.html";
        }
    }

2,@Controller 和 @RestController 有什么区别?

因为我们前面写的代码前面有一个注解叫RestController,那我们现在写的这个Controller又是什么呢,他们有什么区别,最大的区别就是RestController主要用途是返回数据,所以如果当我们在刚刚的代码前面也写这个的话,返回的就不是网页了,而是一个字符串,但是Controller主要的用途是返回网页以及视图,所以我们这么写就会去找前面我们写的html代码了,然后返回网页

3,@ResponseBody(返回数据)

因为我们前面讲到的@RestController也是返回数据,这个也是返回数据,你会发现,当你要返回一个数据的时候,写这两个哪一个都是一样的结果,那怎么理解这两个注解呢:

这两个的主要区别是适应范围和方式:

对比 @ResponseBody @RestController
注解位置 方法上或类上 类上
核心作用 将返回值写入响应体 声明控制器,并默认将返回值写入响应体
是否能返回 HTML 代码 可以 可以
是否能返回 JSON 可以 可以

我写两个代码,这两个代码可以很很好的理解这个

java 复制代码
@Controller
public class TestController {

    @ResponseBody
    @GetMapping("/hello")
    public String hello() {
        return "Hello Java";
    }

    @GetMapping("/home")
    public String home() {
        return "forward:/index.html";
    }
}

这个正常返回网页

java 复制代码
@RestController
public class TestController {

    @GetMapping("/hello")
    public String hello() {
        return "Hello Java";
    }

    @GetMapping("/home")
    public String home() {
        return "forward:/index.html";
    }
}

这个不会返回网页,而是返回forward:/index.html,如果 @RestController也访问网页的话只能在方法里面直接书写html代码,并且设置一个响应类型为网页

返回带有 CSS 样式的 HTML

java 复制代码
    @RestController
    public class HtmlController {
        @RequestMapping("/htmlh1")
        public String html() {

            return "<h1>你好,一级标题</h1>";
        }
    }

这里spring会自动识别出来我们这是一个以及标题

Content-Type

这个决定了我们后端返回的参数的类型是什么,是html还是JSON等等

常见的类型

Content-Type 代表类型 常见用途
text/plain 普通文本 返回字符串
text/html HTML 返回网页内容
application/json JSON 前后端传输数据
application/xml XML 传输 XML 数据
application/x-www-form-urlencoded 表单编码 提交普通表单
multipart/form-data 多部分表单数据 上传文件、表单
application/octet-stream 二进制数据 通用文件传输
image/png PNG 图片 返回 PNG 图片
application/pdf PDF 文档 返回 PDF 文件

如何设置类型

通过produce来设置

java 复制代码
@RestController
public class TestController {

    @RequestMapping(
            value = "/user",
            method = RequestMethod.GET,
            //这里设置类型,我现在转为JSON类型
            produces = "application/json"
    )
    public Map<String, Object> getUser() {

        Map<String, Object> user = new HashMap<>();

        user.put("name", "张三");
        user.put("age", 20);
        user.put("major", "计算机科学与技术");

        return user;
    }
}

响应JSON

以下Java类可以自动转化为JSON格式

java 复制代码
@ResponseBody
    @RequestMapping("/returnJson")
    public User returnJson() {
        return new User("zhangsan", 20,123,"123qq.com");
    }

状态码更改

HTTP 状态码不是根据服务器想表达什么随便修改,而应该遵循 HTTP 标准含义。4xx 表示客户端请求问题,5xx 表示服务器处理问题。后端根据实际情况返回合适的状态码,前端根据状态码或者业务状态码决定展示什么页面或提示。

java 复制代码
    @ResponseBody
    @RequestMapping("/setType")
    public String setType(HttpServletResponse response) {
        response.setStatus(400);
        return "设置状态成功";
    }

到此Spring MVC三要素大致的内容已经讲解完毕,接下来的一篇文章会介绍一下相关的小练习

相关推荐
Mikko71 小时前
fastjson 1.2.84 能在 JDK 7 上跑吗?实测:编译能过,一启动就 UnsupportedClassVersionError
java·后端·jdk·fastjson·安全漏洞
G018_star sky♬1 小时前
为什么java新建一个对象,需要obj o = new obj()这种写法
java·开发语言
丨白色风车丨1 小时前
LangChain Agent 从入门到实战:创建与调用参数全解析
前端·python·langchain
驭渊的小故事4 小时前
MyBatis XML 开发实战:从配置到 CRUD 全解析
xml·java·mybatis
Jmyd01239 小时前
Web端3D营销展厅技术实现路径:从素材上云到线索回流的数据链路
前端·3d·性能优化·三维数字化
哦虎!9 小时前
【JavaEE】网络初识
java
砚凝霜9 小时前
【软考信息安全】第二十二章 Web服务器安全分析与增强——Apache与IIS
服务器·前端·安全
for_ever_love__9 小时前
MySQL ORDER BY 排序优化:从 Using filesort 到索引有序扫描(含双路 / 单路排序原理)
java·mysql·优化·存储·by·order·filesort
我是人✓10 小时前
Java文件上传三方式对比:从传统到SpringBoot
java·开发语言