[Java EE 进阶]Spring Web MVC 入门 (请求)

Spring Web MVC 是 Spring Framework Web 模块的核心组件 , 基于经典的 MVC (Model-View-Controller) 设及模式实现 , 是 Java 生态中主流的 Servlet 栈 Web 开发框架(运行在 Servlet 容器之上 , 如 Tomcat , Jetty)

通过解耦请求处理 , 业务逻辑 , 视图渲染等核心环节 , 简化 Java Web 应用开发 , 同时提供灵活的配置 , 强大的请求处理能力和丰富的扩展点 , 是开发传统服务端渲染 Web 应用 , RESTful API 的首选框架之一 , 也是 SpringBoot 中 Spring-Boot-Start-Web 的核心底层依赖

1. 核心设计理念

1.1 前端控制器模式

通过的那一入口 DispatcherServlet 接收所有的 HTTP 请求 , 统一分发至后续处理器 , 解耦请求入口与业务处理

1.2 组件化解耦

将请求处理流程拆分为多个独立组件(映射器 , 适配器 , 视图解析器等) , 组件间通过接口交互 , 可灵活替换或者自定义

1.3 注解驱动

通过简介的注解(如@Controller@RequestMapping)替代传统 XML 配置 , 大幅简化开发

1.4 与 Spring 生态深度融合

天然支持 Spring 的依赖注入(DI) , 面向切面编程(AOP) , 事务管理等核心能力 , 业务层组件可直接注入 Web 层使用

2.核心概念

2.1MVC 模式

  • **View(视图)**指在应用程序中专门用来与浏览器进行交互 , 展示数据的资源
  • **Modek(模型)**是应用程序的主体部分 , 用来处理程序中数据逻辑的部分
  • **Controller(控制器)**可以理解为一个分发器 , 用来决定对于视图发来的请求 , 需要用哪一个模型来处理 , 以及处理完后需要跳回到哪一个视图(连接视图和模型)

2.2 SpringMVC

基于 Servlet API 的 Web 框架,是 MVC 设计模式的具体实现,Spring Boot 通过引入 Spring Web 依赖即可使用 Spring MVC

2.3 与 SpringBoot 的关系

SpringBoot 是快速开发工具 , SpringMVC 是 Web 功能的核心 , SpringBoot 通过引入 Spring Web 依赖集成 SrpingMVC

3.核心功能与关键注解

Spring MVC 的核心是建立连接 , 处理请求 , 生成响应 , 关键注解及用法如下

3.1 建立连接

java 复制代码
package com.boop.demo01;

import org.springframework.web.bind.annotation.*;

@RequestMapping("/User2")
@RestController
public class UserController2 {
    @RequestMapping("/m1")
    public String m1(){
        return "m1";
    }

    @RequestMapping(value = "/m2",method = RequestMethod.GET)
    //@GetMapping("/m2")
    public String m2(){
        return "m2";
    }

    @RequestMapping(value = "/m3",method = RequestMethod.POST)
    //@PostMapping("/m3")
    public String m3(){
        return "m3";
    }
}

① @RequestMapping()

使用 @RequestMapping("/User2")注册路由路径 , 可以修饰类和方法 , 访问路径为类路径+方法路径 , 支持 GET , POST 多种请求方式 ;

② @RestController

起到标识作用 , @RestController = @Controller+@ResponseBody 标识控制器类 , 使方法返回数据而非视图

如果去掉 @RestController

程序会报 404

③ 指定 GET / POST 方法类型

注意: 如果 method 有多个参数 , 则需要用大括号括起来

3.2 使用 PostMan 传参

下载链接:https://www.postman.com/downloads/

java 复制代码
package com.boop.demo01;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/request")
@RestController
public class RequestController {

    @RequestMapping("/r1")
    public String r1 (String keyword){
        return "接收参数"+ keyword;
    }
}

① 普通传参

使用查询字符串传参

②form-data

multipart/form-data : 是一种 http 请求内容的格式 , 常用于 提交包含二进制数据的请求

  • 前端的使用方式 : 在 HTML 的<form>标签中,需添加属性enctype="multipart/form-data",以此指定表单提交时使用该格式 ; 核心用途:专门用于提交图片、文件等二进制数据(普通文本数据也可兼容,但更适配二进制内容的传输)
  • 对应的请求头 : 该格式对应的 HTTP 请求头Content-Type值为multipart/form-data,用于告知服务器请求体的编码方式

使用抓包工具

③x-www-form-urlencoded

form 表单 , 对应 Content-Type: application/x-www-from-urlencoded

④raw

可以上传任意格式的文本 , 可以上传 text,json,xml,html 等

3.3 请求

访问不同的路径 , 就是发送不同的请求 , 在发送请求时 , 可能会带一些参数 , 所以学习 Spring 的请求 , 主要是学习如何传递参数到后端以及后端如何接收

以下为完整代码 :

java 复制代码
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import model.UserInfo;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

@RequestMapping("/request")
@RestController
public class RequestController {

    @RequestMapping("/r1")
    public String r1 (String keyword){
        return "接收参数"+ keyword;
    }

    @RequestMapping("/r2")
    public String r2(int keyword){
        return "接收参数"+keyword;
    }

    @RequestMapping("/r3")
    public String r3(Integer keyword){
        return "接收参数"+keyword;
    }
    @RequestMapping("/r4")
    public String r4(String userName, String password) {
        return "接收参数: userName:"+userName + ", password:"+password;
    }

    //传递对象
    @RequestMapping("/r5")
    public String r5(UserInfo userInfo){
        return "接收参数 : userInfo = " +userInfo.toString();
    }

    //重命名参数
    //从前端接收 key , 赋值给keyword
    @RequestMapping("/r6")
    public String r6(@RequestParam("key") String keyword){
        return "接收参数 + keyword="+keyword;
    }
    //    @RequestMapping("/r6")
    //    public String r6(@RequestParam(value = "key",required = false) String keyword){
    //        return "接收参数 + keyword="+keyword;
    //    }

    //传递数组
    @RequestMapping("/r7")
    public String r7(String[] arr){
        return "接收参数:arr=" +Arrays.toString(arr);
    }

    //传递集合
    @RequestMapping("/r8")
    public String r8(@RequestParam List<String> list){
        return "接收参数:list="+list;
    }

    //传递JSON
    @RequestMapping("/r9")
    public String r9(@RequestBody UserInfo userInfo){
        return userInfo.toString();
    }
    //    @RequestMapping("/r9")
    //    public String r9( UserInfo userInfo){
    //        return userInfo.toString();
    //    }

    //获取URL 中参数
    @RequestMapping("/r10/{type}/{ID}")
    public String r10(@PathVariable("type") String Booktype,//将URL中 type 与参数 Booktype 进行绑定
                      @PathVariable Integer ID){//每个参数前都需要加 @PathVariable
        return "获取文章类型:"+Booktype+"获取文章ID:"+ID;
    }

    //    @RequestMapping("/article/{articleID}")
    //    public String r10(@PathVariable Integer articleID){
    //        return "获取文章ID : "+articleID;
    //    }

    //上传文件
    @RequestMapping("/r11")
    public String r11(@RequestParam("wxfile") MultipartFile file) throws IOException {
        //获取从前端上传的原始文件名
        String filename = file.getOriginalFilename();
        //直接将内存中的文件流写入本地指定目录
        file.transferTo(new File("D:/Temp/"+filename));
        return "接收文件为:"+filename;
    }

    //获取Cookie
    @RequestMapping("/r12")
    public String r12(HttpServletRequest request){
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for(Cookie cookie:cookies){
                System.out.println(cookie.getName()+":"+cookie.getValue());
            }
        }
        return "返回Cookie成功";
    }

    //获取Cookie
    @RequestMapping("/r13")
    public String r13(@CookieValue("Java") String str1){
        return "从Cookie获取Java的Value值"+str1;
    }

    //存储Session
    @RequestMapping("/setSession")
    public String setSession(HttpServletRequest request){
        //从cookie中获取sessionID , 根据sessionID 获取session对象
        //如果sessionID不存在 , 则创建
        HttpSession session = request.getSession();
        //默认存储在内存中
        //登录的用户名称

        session.setAttribute("userName","zhangsan");
        session.setAttribute("age",17);
        return "设置session成功";
    }

    //存储Session
    @RequestMapping("/setSession2")
    public String setsess(HttpSession session) {
        // 直接注入的Session,不存在则自动创建
        session.setAttribute("username2", "java123");
        return "session 存储成功";
    }

    //获取session
    @RequestMapping("/getSession1")
    public String getSession(HttpServletRequest request){
        //从cookie中获取sessionID , 根据sessionID 获取session对象
        HttpSession session = request.getSession(false);
        //如果用户登录 , session有值 ; 未登录 , sessionID为null
        if (session == null) {
            return "用户未登录";
        }else {
            //从session中获取登录用户的信息
            String userName = (String) session.getAttribute("userName");
            return "登录用户为 : "+userName;
        }
    }

    //获取Session
    @RequestMapping("/getSession2")
    public String getSession2(HttpSession session){
        //从session中获取登录用户的信息
        String userName = (String)session.getAttribute("userName");
        return "登录用户为 : "+userName;

    }

    //获取Session
    @RequestMapping("/getSession3")
    public String getSession3(@SessionAttribute("userName") String userName) {
        return "登录用户为 : " + userName;
    }

    //获取Header
    @RequestMapping("/getheader1")
    public String getheader1(HttpServletRequest request){
        String UserAgent = request.getHeader("User-Agent");
        return "User-Agent:"+UserAgent;
    }

    //获取Header
    @RequestMapping("/getheader2")
    public String getheader2(@RequestHeader("User-Agent") String userAgent){
        return "userAgent:"+userAgent;
    }
}

① 传递单个参数

使用 postman 发送请求并传参

java 复制代码
@RequestMapping("/r1")
public String r1 (String keyword){
return "接收参数"+ keyword;
}

@RequestMapping("/r2")
public String r2(int keyword){
return "接收参数"+keyword;
}

@RequestMapping("/r3")
public String r3(Integer keyword){
return "接收参数"+keyword;
}

http://127.0.0.1:8080/request/r1?keyword=abc

http://127.0.0.1:8080/request/r2?keyword=123

http://127.0.0.1:8080/request/r3?keyword=123

注意 :

  • 使用基本类型来接收参数时 , 参数必须传 , boolean 类型除外 , 否则报 500 错误
  • 类型不匹配 , 则会报 400 错误
  • 对于参数可能为空的数据建议使用包装类型

② 传递多个参数

java 复制代码
@RequestMapping("/r4")
public String r4(String userName, String password) {
    return "接收参数: userName:"+userName + ", password:"+password;
}

使用 postman 发送请求并传参http://127.0.0.1:8080/request/r4?userName=zhangsan&password=123456

注意 : 但有多个参数时 , 前后端进行参数匹配时 , 是以参数的名称进行匹配的 , 因此参数的位置是不影响后端获取参数的结果

③ 传递对象

参数比较多时 , 方法声明就需要很多形参 . 并且后续每次新增一个参数 , 也需要修改方法声明 ; 不妨把这些参数封装为一个对象

java 复制代码
package model;

public class UserInfo {
    private String name;
    private int gender;
    private int age;

    public UserInfo() {
    }

    public UserInfo(String name, int gender, int age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
        "name='" + name + '\'' +
        ", gender=" + gender +
        ", age=" + age +
        '}';
    }
}
java 复制代码
//传递对象
@RequestMapping("/r5")
public String r5(UserInfo userInfo){
return "接收参数 : userInfo = " +userInfo.toString();
}

使用 postman 发送请求并传参http://127.0.0.1:8080/request/r5?name=zhansan&gender=1&age=18

④ 后端参数重命名 @ResquestParam

**前面的传参都需要保持 后端参数 和 前端参数 命名统一 ;**如果参数命名不一致就会出现参数接收不到的情况 ; 这种情况需要使用@ResquestParam 来重命名后端参数值

java 复制代码
//重命名参数
//从前端接收 key , 赋值给keyword
@RequestMapping("/r6")
public String r6(@RequestParam("key") String keyword){
    return "接收参数 + keyword="+keyword;
}

使用 postman 发送请求并传参http://127.0.0.1:8080/request/r6?key=abc

注意 :

  • 使用@ResquestParam 进行参数重命名时 , 请求参数只能和@ResquestParam 声明的名称一致 , 才能进行参数绑定
  • 使用@ResquestParam 进行参数重命名时,参数就成了必传参数
  • 设置成非必传参数

⑤ 传递数组

java 复制代码
//传递数组
@RequestMapping("/r7")
public String r7(String[] arr){
    return "接收参数:arr=" +Arrays.toString(arr);
}
方法 1:

传参时 , 参数名都相同 , 就会按数组接收

使用 postman 发送请求并传参: http://127.0.0.1:8080/request/r7?arr=abc&arr=hsa&arr=hja

方法 2:

直接传数组

使用 postman 发送请求并传参: http://127.0.0.1:8080/request/r7?arr=abc,def,hjk,plk

⑥ 传递集合

和数组类似 , 且需要使用@RequestParam

java 复制代码
//传递集合
@RequestMapping("/r8")
public String r8(@RequestParam List<String> list){
    return "接收参数:list="+list;
}

⑦ 传递 JSON 数据 @RequestBody

https://blog.csdn.net/Boop_wu/article/details/157877351?fromshare=blogdetail&sharetype=blogdetail&sharerId=157877351&sharerefer=PC&sharesource=Boop_wu&sharefrom=from_linkhttps://blog.csdn.net/Boop_wu/article/details/157877351?fromshare=blogdetail&sharetype=blogdetail&sharerId=157877351&sharerefer=PC&sharesource=Boop_wu&sharefrom=from_link

使用@RequestBody 接收 请求体中的 JSON 数据

第一步 : 后端实现
java 复制代码
//传递JSON
@RequestMapping("/r9")
public String r9(@RequestBody UserInfo userInfo){
    return userInfo.toString();
}
第二步:使用 postman 来发送 JSON 请求参数

http://127.0.0.1:8080/request/r9

java 复制代码
{
  "name": "张三",
  "gender": 1,
  "age": 18
}

注意 : 如果去掉@RequestBody

⑧ 获取 URL 中参数 @PathVariable

pathvariable : 路径变量

作用 : 在请求 URL 路径上的数据绑定

java 复制代码
//获取URL 中参数
@RequestMapping("/article/{articleID}")
public String r10(@PathVariable Integer articleID){
    return "获取文章ID : "+articleID;
}

多个参数 :

java 复制代码
    //获取URL 中参数
    @RequestMapping("/r10/{type}/{ID}")
    public String r10(@PathVariable("type") String Booktype,//将URL中 type 与参数 Booktype 进行绑定
                      @PathVariable Integer ID){//每个参数前都需要加 @PathVariable
        return "获取文章类型:"+Booktype+"获取文章ID:"+ID;
    }

使用 postman 发送请求http://127.0.0.1:8080/request/r10/Geography/122548

注意 :

  • 每个参数前都需要加@PathVariable;
  • 变量名称规范(不一致需要 对@PathVariable 加参数)

⑨ 上传文件 @RequestPart

java 复制代码
//上传文件
@RequestMapping("/r11")
public String r11(@RequestParam("wxfile") MultipartFile file) throws IOException {
    //获取从前端上传的原始文件名
    String filename = file.getOriginalFilename();
    //直接将内存中的文件流写入本地指定目录
    file.transferTo(new File("D:/Temp/"+filename));
    return "接收文件为:"+filename;
}

使用 POSTMAN 发送请求 : http://127.0.0.1:8080/request/r11

本地目录上传成功

⑩ 获取 Cookie/Session

https://blog.csdn.net/Boop_wu/article/details/157972847?fromshare=blogdetail&sharetype=blogdetail&sharerId=157972847&sharerefer=PC&sharesource=Boop_wu&sharefrom=from_linkhttps://blog.csdn.net/Boop_wu/article/details/157972847?fromshare=blogdetail&sharetype=blogdetail&sharerId=157972847&sharerefer=PC&sharesource=Boop_wu&sharefrom=from_link

Cookie

客户端浏览器 的轻量级文本数据,由服务器通过Set-CookieHTTP响应头下发给客户端,客户端会将其保存,后续向同一服务器发送请求时,会自动通过CookieHTTP请求头携带该数据,实现客户端侧的会话标识

  • 存储位置:客户端浏览器(内存/本地文件,依Cookie属性而定);
  • 核心作用:存储会话令牌(SessionId)、少量非敏感的用户信息;
  • 特性:可被客户端修改/伪造,因此后端使用Cookie时必须做校验
Session

服务器端 为每个客户端创建的专属会话对象 ,本质是一个哈希表(键值对)Key唯一的SessionId (服务器生成的字符串),Value为自定义的用户信息(如登录名、用户ID)。

  • 存储位置:默认在服务器内存中;
  • 核心作用:存储敏感的用户会话信息,避免在客户端明文传输;
  • 核心流程
    • 客户端首次请求服务器时,服务器创建Session并生成唯一SessionId,通过Set-Cookie: JSESSIONID=xxx响应头下发给客户端;
    • 客户端将SessionId保存到Cookie中,后续请求时通过Cookie: JSESSIONID=xxx携带;
    • 服务器通过请求中的SessionId匹配对应的Session对象,实现服务端侧的会话跟踪
Cookie 和 Session 区别
  • Cookie 是客户端保存用户信息的一种机制 , Session 是服务器端保存用户信息的一种机制
  • Cookie 和 Session 之间主要通过 SessionID 关联起来 , SessionID 是 Cookie 和 Session 之间的桥梁
  • Cookie 和 Session 经常会在一起配合使用(完全可以用 Cookie 来保存一些数据在客户端 ; Session 中的 SessionID 也可以不通过 Cookie/Set-Cookie 传递)

Spring MVC 基于 Servlet API 构建 , 内置 HttpSevletRequest/HttpServletResponse 对象 , 可直接在方法参数中声明使用 , 也可以通过注解式的简介获取方式;

方法一 : 通过HttpServletRequest获取
java 复制代码
//获取Cookie
@RequestMapping("/r12")
public String r12(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
if (cookies != null) {
    for(Cookie cookie:cookies){
        System.out.println(cookie.getName()+":"+cookie.getValue());
    }
}
return "返回Cookie成功";
}
  • 通过 HttpServletRequest.getCookie()获取 所有 Cookie 数组 , 需遍历数组并判空 , 根据 Cookie 的 getName()匹配目标 Cookie
  • 必须对 cookies 数组做判空处理 , 否则客户端未携带 cookie 时 , 会抛出 NullPointerException

使用 postman 发送请求http://127.0.0.1:8080/request/r12

方法二 : 通过@CookieValue注解直接绑定
java 复制代码
//获取Cookie
@RequestMapping("/r13")
public String r13(@CookieValue("Java") String str1){
    return "从Cookie获取Java的Value值"+str1;
}
  • 通过@CookieValue 注解指定 Cookie 名称 , 直接将目标 Cookie 的值绑定到方法参数 , 简化遍历操作 (注解默认必传)
  • 或者设置为非必传 @CookieValue( value ="Java", required =false)
  • 缺点 是 一次只能处理一个

使用 postman 发送请求(和方法一一样的 cookie)http://127.0.0.1:8080/request/r13

存储 Session

Session 是服务器端的机制 , 需要先存储才能获取 ; Session 也是基于 HttpServletRequest 来存储和获取的

方法一 : 通过 HttpServletRequest 获取 Session 后存储
java 复制代码
//存储Session
@RequestMapping("/setSession")
public String setSession(HttpServletRequest request){
//从cookie中获取sessionID , 根据sessionID 获取session对象
//如果sessionID不存在 , 则创建
HttpSession session = request.getSession();
//默认存储在内存中
//登录的用户名称

session.setAttribute("userName","zhangsan");
session.setAttribute("age",17);
return "设置session成功";
}

使用 Postman 来发送请求http://127.0.0.1:8080/request/setSession

使用抓包工具抓取

方法二 : 直接在方法参数中注入 HttpSession
java 复制代码
//存储Session
@RequestMapping("/setSession2")
public String setsess(HttpSession session) {
// 直接注入的Session,不存在则自动创建
session.setAttribute("username2", "java123");
return "session 存储成功";
}
获取session
方法一 : 通过 HeepServletRequest 获取 Session 后存储
java 复制代码
//获取session
@RequestMapping("/getSession1")
public String getSession(HttpServletRequest request){
//从cookie中获取sessionID , 根据sessionID 获取session对象
HttpSession session = request.getSession(false);
//如果用户登录 , session有值 ; 未登录 , sessionID为null
if (session == null) {
    return "用户未登录";
}else {
    //从session中获取登录用户的信息
    String userName = (String) session.getAttribute("userName");
    return "登录用户为 : "+userName;
}
}

使用 postman 发送请求http://127.0.0.1:8080/request/getSession1

Objetc.getAttritube(String name) : 返回在该 session 会话中具有指定名称的对象 , 如果没有指定名称的对象 , 则返回 null

方法二 : 通过@SessionAttribute注解直接绑定
java 复制代码
//获取Session
@RequestMapping("/getSession2")
public String getSession2(HttpSession session){
//从session中获取登录用户的信息
String userName = (String)session.getAttribute("userName");
return "登录用户为 : "+userName;
}

使用 postman 发送请求http://127.0.0.1:8080/request/getSession2

方法三 : 直接注入 HttpSession 对象查询
java 复制代码
//获取Session
@RequestMapping("/getSession3")
public String getSession3(@SessionAttribute("userName") String userName) {
    return "登录用户为 : " + userName;
}

使用 postman 发送请求http://127.0.0.1:8080/request/getSession3

⑪ 获取 Header

方法一 : 通过 HttpServletRequest 获取
java 复制代码
//获取Header
@RequestMapping("/getheader1")
public String getheader1(HttpServletRequest request){
String UserAgent = request.getHeader("User-Agent");
return "User-Agent:"+UserAgent;
}
  • 通过 Fiddler 观察 , 获取的 User-Agent 是否正确
方法二 : 通过注解@RequestHeader 的参数值为 Http 请求报头中的"Key"
java 复制代码
//获取Header
@RequestMapping("/getheader2")
public String getheader2(@RequestHeader("User-Agent") String userAgent){
    return "userAgent:"+userAgent;
}

使用 postman 发送请求http://127.0.0.1:8080/request/getheader2

相关推荐
mCell4 小时前
如何零成本搭建个人站点
前端·程序员·github
mCell5 小时前
为什么 Memo Code 先做 CLI:以及终端输入框到底有多难搞
前端·设计模式·agent
恋猫de小郭5 小时前
AI 在提高你工作效率的同时,也一直在增加你的疲惫和焦虑
前端·人工智能·ai编程
少云清5 小时前
【安全测试】2_客户端脚本安全测试 _XSS和CSRF
前端·xss·csrf
银烛木6 小时前
黑马程序员前端h5+css3
前端·css·css3
m0_607076606 小时前
CSS3 转换,快手前端面试经验,隔壁都馋哭了
前端·面试·css3
听海边涛声6 小时前
CSS3 图片模糊处理
前端·css·css3
IT、木易6 小时前
css3 backdrop-filter 在移动端 Safari 上导致渲染性能急剧下降的优化方案有哪些?
前端·css3·safari
0思必得06 小时前
[Web自动化] Selenium无头模式
前端·爬虫·selenium·自动化·web自动化
青云计划6 小时前
知光项目知文发布模块
java·后端·spring·mybatis