SpringMVC探秘: 实现MVC模式的Web应用

文章目录

  • 1. SpringMVC概述
    • 1.1. 什么是SpringMVC?
      • 1.1.1. MVC与SpringMVC
    • 1.2. SpringMVC项目的优势
  • 2. SpringMVC项目的创建与使用
    • 2.1. 创建SpringMVC项目
    • 2.2. 设置路由
    • 2.3. 获取参数
      • 2.3.1. 获取一个参数
      • 2.3.2. 获取多个参数
      • 2.3.3. 获取日期参数
      • 2.3.4. 参数重命名@RequestParam
      • 2.3.5. 获取json字符串
      • 2.3.6. 获取URL中的参数
      • 2.3.7. 上传文件
      • 2.3.8. 获取Cookie
      • 2.3.9. 获取Header
      • 2.3.10. 存储和获取Session
    • 2.4. 返回数据
      • 2.4.1. 返回普通的text文本/静态页面
      • 2.4.2. 返回JSON对象
      • 2.4.3. 实现请求转发与重定向

1. SpringMVC概述

1.1. 什么是SpringMVC?

Spring Web MVC (Model View Controller)是基于 Servlet API 构建的原始 Web 框架,从一开始就包含在 Spring Framework 中。正式名称“Spring Web MVC”来自其源模块的名称(spring-webmvc),但它通常被称为“Spring MVC”。

1.1.1. MVC与SpringMVC

MVC 是 Model View Controller 的缩写,它是软件工程中的一种软件架构模式,它把软件系统分为模型、视图和控制器三个基本部分。

img

Model(模型)是应用程序中用于处理应用程序数据逻辑的部分,通常模型对象负责在数据库中存取数据。
View(视图)是应用程序中处理数据显示的部分,通常视图是依据模型数据创建的,是前端(客户端)的可视的页面。
Controller(控制器)是应用程序中处理用户交互的部分,通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。

🎯MVC 执行流程:

  1. 用户(客户端)向发送的请求首先到服务器端的 Controller。
  2. Controller 将请求继续转发给 Model。
  3. Model 处理业务并将数据结果返回给 Controller。
  4. Controller 会将数据交给 View 引擎。
  5. View 会将数据进行转换渲染生成最终的页面给用户(客户端)。

🎯MVC与Spring MVC之间的区别?
MVC 是一种思想,而 Spring MVC 是对 MVC 思想的具体实现。
Spring MVC 是⼀个实现了 MVC 模式,并继承了 Servlet API 的 Web 框架,当⽤户在浏览器中输⼊了 URL 之后,我们的 Spring MVC 项目就可以感知到用户的请求。

1.2. SpringMVC项目的优势

现在绝大部分的 Java 项目都是基于 Spring(或 Spring Boot)的,而 Spring 的核心就是 Spring MVC;也就是说 Spring MVC 是 Spring 框架的核心模块,而 Spring Boot 是 Spring 的脚手架,因此我们可以推断出,现在市面上绝大部分的 Java 项目基本上都是 Spring MVC 项目,这也是我们要学 Spring MVC 的原因。

学习SpringMVC项目,主要学习的是三个功能:

  1. 连接:将用户(浏览器)和 Java 程序连接起来,也就是访问⼀个地址(URL)能够调用到我们的
    Spring 程序。
  2. 获取参数:用户访问的时候会携带⼀些参数数据,在程序中要能够获取到这些参数。
  3. 输出数据:执行了业务逻辑之后,要把程序执行的结果返回给用户。

2. SpringMVC项目的创建与使用

2.1. 创建SpringMVC项目

首先,我们需要新建 SpringBoot 项目。

img

要注意,在添加依赖的时候,一定要添加 Web 模块,它其实就是 SpringMVC 的依赖。

img

2.2. 设置路由

设置路由可以实现程序与用户之间的映射(路由映射),也就是当用户访问某一个 URL 时,会将用户的请求对应到程序中的某个类的某个方法中,实现浏览器连接程序的作用,设置路由可以使用@RequestMapping注解实现,注意要实现路由访问,必须使用 @Controller 将对象储存到 Spring 中。

package com.example.demo.controller;

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

@Controller // 让 spring 框架启动时, 加载
@ResponseBody // 返回⾮⻚⾯数据
@RequestMapping("/user") // 一级路由
@Slf4j // 日志框架
public class UserController {
    @RequestMapping("/hi") // 二级路由
    public String sayHi() {
        return "<h1>Hi, Spring MVC.</h1>";
    }
}

其中,访问方法所返回数据或页面的 URL 为http://IP:端口号/类上的路由地址/方法上加的路由地址,这样就能访问到每个方法所返回的页面或者数据了,也可以只在方法上设置路由地址;且当类中只有一个方法时,类上/方法上也可以不设置路由地址(挑一个设置即可),此时直接使用一级路由就能直接访问了。

由于历史原因,方法返回默认是一个静态页面,如果要设置返回一个页面数据,需要搭配使用 @ResponseBody注解,放在类上,类里面所有的方法都会默认放回的是页面数据,而不是一个静态页面,放到方法上生效的只是当前方法。

启动程序,我们去浏览器通过访问http://127.0.0.1:8080/user/hi地址,观察结果。
img

@RequestMapping默认情况下,同时支持 GET 和 POST 等其他请求方式;而如果我们想要仅支持一种请求方式,需要做一些配置。

我们需要设置 @RequestMapping 的method属性为RequestMethod.xxx,RequestMethod 其实是一个枚举,里面储存了多种的请求方式。
img

我们这里设置一个 POST 请求。

@Controller
@ResponseBody
@Slf4j
@RequestMapping("/user")
public class UserController {
    @RequestMapping(method = RequestMethod.POST, value = "/onlypost")
    public String func1() {
        return "post";
    }
}

我们使用 postman 构造 GET 与 POST 请求看看能不能进行处理。

首先我们来试一试GET请求,
img
通过抓包,可以看到返回给用户的结果是405,出错了,这就表示该方法是不支持 GET 请求的,我们再来试一试POST请求。

img
构造 POST 请求:
img
返回结果:
img
返回了一个post数据,与我们预期相同,所以像上面那样进行配置,可以使一个方法只支持 GET 或 POST 一种请求格式。

当然还有另外的方法,如果只支持 POST 请求,我们还可以使用@PostMapping注解来实现。

@PostMapping("/hello")
public String func2() {
    return "hello";
}

img

img

同理,如果需要只支持 GET 请求我们也可以使用@GetMapping注解来进行实现。

2.3. 获取参数

在 MVC 项目中,因为 SpringMVC 是基于 Servlet 的,所以使用 Servlet 那一套操作也是可以的,只不过有点繁琐,Spring 中就有了更方便获取参数的方式。

2.3.1. 获取一个参数

假设有一个类User,客户端会传一个用户id到后端,我们需要返回一个User对象。

package com.example.demo.model;

import lombok.Data;

@Data
public class User {
    private int id;
    private String name;
    private String password;
}

根据 id 查询 User 对象,这里主要是示范 Spring Web API 的使用,就不添加数据库查询了,这里直接构造相应 id 的对象,返回即可,重点是模拟数据的传入与传出。

我们直接在方法中加上一个形参(要与前端传入的参数名相同),访问路径时就会后会自动根据形参的名字以前端传来参数的 key 进行匹配,如果能够对应,后端就能成功接收参数。

@RequestMapping("/getuserbyid")
public User getUserById(Integer id) {
    User user = new User();
    user.setId(id);
    user.setName("张三");
    user.setPassword("zs12345678");
    return user;
}

我们再通过 postman 来构造一个 id 参数,并获取到一个 User 对象。

Spring 会根据响应的返回值,决定返回数据的类型,比如你返回一个对象或 Map,就会返回一个 json 格式的数据,你返回一个页面,那它返回的就是页面类型的数据。

img

因为 SpringMVC 是基于 Servlet 的,所以 Servlet 中的那一套 API 在这里也适用,在MVC中,方法中的HttpServletRequest 与 HttpServletResponse 参数默认是隐藏的,如果想要使用,需要显示地进行声明,获取请求参数时需要通过 HttpServeletRequest 对象手动获取,我们只声明一个即可,相较来看写法上就比较繁琐了。

@RequestMapping("/getUserById2")
public User getUserById2(HttpServletRequest request) {
    User user = new User();
    user.setId(Integer.parseInt(request.getParameter("id")));
    user.setName("李四");
    user.setPassword("ls12345678");
    return user;
}

img

还需要知道默认情况下,前端多传或者少传参数都是可以的,少传参数的情况下,结果就是 null,而多传参数并不会被 SpringMVC 读取。

2.3.2. 获取多个参数

我们还可以获取多个参数,此时可以给方法设置多个参数,也可以设置一个对象参数直接获取多个前端传来的参数。

比如,一个登录的逻辑,需要用户传来账户名与密码,这里就不实现一个登录逻辑了,我们直接将获取到的账户名与密码作为响应返回出去。

@RequestMapping("/login") 
public String login(String name, String password) {
    return "用户名:" + name + "密码:" + password;
}

我们从前端传入同名的键值对,后端就能够自动获取到。
img
当然,我们也可以使用对象来进行接收前端的参数,后端会根据 key 与对象中的属性名自动地进行匹配。

@RequestMapping("/object")
public String getObject(User user) {
    return user.toString();
}

User 中的属性有 id,name 和 password,我们直接从前端传递三个对应名称的键值对,SpringMVC 会自动进行对象的初始化(参数映射)。

img

除此之外,我们还可以接收form表单的形式的参数,可以使用是 GET/POST 请求方式。
img

2.3.3. 获取日期参数

使用@DateTimeFormat注解完成日期参数格式转换,该注解有个pattern的属性,值为我们所传递的日期参数的格式即可。

@RequestMapping("/dateParam")
public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime) {
    return updateTime.toString();
}

img

2.3.4. 参数重命名@RequestParam

如果后端方法形参名和与客户端请求参数名不匹配时,就会出现参数接收不到的情况,这种情况,我们可以使用@RequestParam来重命名后端的参数值(后端参数映射)。

其中@RequestParam里面的参数为前端传来的参数名字,它可以自动匹配到注解所在的形参中。

@RequestMapping("/rename")
public String getNameAndPass(@RequestParam("name") String username, 
                             @RequestParam("pass") String password) {
    return "用户名:" + username + "密码:" + password;
}

img
还需要注意,使用 @RequestParam 注解修饰的参数,默认情况下,参数是必传,如果不传或少传就会报 400 请求错误。
img
这个情况是因为 @RequestParam 注解里面还有一个属性required,默认情况下值为true,这个属性的意思是是否是必须参数,所以参数重命名后,重命名的参数默认情况下不能缺少,如果不想将参数设置成必须,但是想重命名,那么就需要额外设置required属性值为false

@RequestMapping("/rename")
public String getNameAndPass(@RequestParam(name = "name", required = false) String username, 
                             @RequestParam(name = "pass", required = false) String password) {
    return "用户名:" + username + " 密码:" + password;
}

img

2.3.5. 获取json字符串

如果我们想要获取 json 格式的参数,需要使用@RequestBody注解修饰参数(声明了这个参数接收 json 数据)。

@RequestMapping("/object2")
public String getObject2(@RequestBody User user) {
    return user.toString();
}

像上面这样,我们能够获取到 json 格式的参数了。
img
但这样设置后,就不能接收其他格式的数据了。

2.3.6. 获取URL中的参数

这里所说的从 URL 获取参数,并不是获取查询字符串部分的参数,而是直接拿 URL 地址中的参数,简单来说,就是从 URL 中?前面的字段中获取参数;这种方式的优点是搜索引擎抓取关键字权重更高,有利于提高曝光度,且让 URL 更简洁。

获取方法:使用特定 URL 格式和在参数前加上注解@PathVariable

@RequestMapping("/hero/{id}/{name}")
public String geuURL(@PathVariable Integer id, @PathVariable String name) {
    return "id:" + id + "  name:" + name;
}

我们访问http://127.0.0.1:8080/user/hero/1234/梧桐网页就能够获取到如下内容:
img
其中hero表示二级目录地址,后面使用{}的就表示参数。
img

2.3.7. 上传文件

文件的上传,我们需要使用到@RequestPart注解和MultipartFile,我们写方法形参定义的时候,需要一个MultipartFile变量来接收前端传递的文件参数,并设置@RequestPart的属性来表示接收前端参数的名字。

//上传文件
@RequestMapping("/upimg")
public boolean updateImg(@RequestPart("img") MultipartFile file) {
    boolean result = false;
    try {
        //将图片保存
        file.transferTo(new File("D:\\image\\yan.jpg"));
        result = true;
    } catch (IOException e) {
        log.error("图片上传失败!" + e.getMessage());
    }
    return result;
}

我们使用 postman 构造一个请求,给服务器端发送一个图片,我们来看看目标目录下是否得到了我们上传的文件。
img
目标目录:
img
在上面的代码中是将保存文件的路径写死的了,但是,在实际开发到上线的过程当中,是涉及到至少三个环境的(开发,测试,生产),所以,保存的文件路径并不会写死,而是将保存文件的路径放在配置文件当中,然后读取配置环境中的保存图片路径。

还要注意, 一次上传文件大小默认是1MB,一次请求上传文件大小默认是10MB,可以在配置文件中进行修改。

# 设置一次上传文件大小(默认是1MB)
spring.servlet.multipart.max-file-size=100MB
# 设置一次请求上传文件大小(默认是10MB)
spring.servlet.multipart.max-request-size=100MB

这里我们来实现一个小案例,就是实现一下上传头像的功能,那这个功能的实现有如下过程:

  1. 配置保存的路径,并获取
  2. 设置不会重名的图片名称(UUID)
  3. 获取图片格式,得到文件的格式
  4. 将随机 UUID 与图片格式拼接得到完整的图片文件名
  5. 储存图片

对于不重名文件名的设定,我们可以使用 UUID,UUID 可以自动生成一个唯一的标识序列,简单理解就是 UUID 可以生成一个全球唯一 id,它由MAC + 随机种子 + 加密算法得出。

# 设置文件保存路径
img.path=D:\\image\\
//获取配置文件的保存路径
@Value("${img.path}")
private String path;

@RequestMapping("/upload")
public String upLoad(@RequestPart("img") MultipartFile file) throws IOException {
    // 1.生成一个唯一的 id
    String name = UUID.randomUUID().toString().replace("-", "");
    // 2. 得到源文件的后缀名, 和 id 组合成新的文件名
    name += (file.getOriginalFilename().
            substring(file.getOriginalFilename().lastIndexOf(".")));
    // 保存文件
    System.out.println(path);
    path += name;
    file.transferTo(new File(path));
    return path;
}

img

img

2.3.8. 获取Cookie

一般用户是不会传递这种参数的,基本上都是浏览器构造发过来的,Cookie 和 Session 通常配合使用在登录验证的场景下。

这里同样可以使用 Servlet 那一套 API 获取。

@RequestMapping("/servlet")
public void get(HttpServletRequest req, HttpServletResponse resp) {
    //...
}

🍂使用 Servlet 方式获取 Cookie:

@RequestMapping("/cookie")
public void getCookie(HttpServletRequest req) {
    //获取请求中全部的 ookie
    Cookie[] cookies = req.getCookies();
    for (Cookie c : cookies) {
        log.info("CookieKey : " + c.getName() + "   CookieValue : " + c.getValue());
    }
}

我们可以在浏览器随便加上一些 Cookie。

img

此时我们访问http://127.0.0.1:8080/user/cookie,来看看控制台是否有输出我们所传的 Cookie。
img
有记录,就说明我们成功获取到了 Cookie。

🍂简洁地获取Cookie,使用@CookieValue注解,所传参数为你需要获取 Cookie 的 key,使用该注解修饰一个变量,变量里面就会自动获取对应的value值。

@RequestMapping("/cookie2")
public String getCookie2(@CookieValue("zhangsan") String cookie) {
    return "zhangsan : " + cookie;
}

img
如果需要多个 Cookie 值,就写多个 CookieValue 修饰变量就行。

2.3.9. 获取Header

Header 用的比较多的就是获取用户的浏览器的一些基本信息了。

🍂使用 Servlet 获取 Header

@RequestMapping("/getua")
public String getUA(HttpServletRequest req) {
    return "用户设备信息User-Agent : " + req.getHeader("User-Agent");
}

img
🍂更简单地获取 Header,使用 @RequestHeader 注解

和前面使用注解 @CookieValue 用法类似,也是设置一个 key 来获取对应的 value。

@RequestMapping("/getua2")
public String getUA2(@RequestHeader("User-Agent") String ua) {
    return "用户设备信息User-Agent : " + ua;
}

img

2.3.10. 存储和获取Session

在登录逻辑中,我们需要存储 Session 对象,这一步存储操作,SpringMVC 与 Servlet 操作是一样的,获取方式则有两种方式。

🍂存储Session对象:

@RequestMapping("/setsession")
public boolean setSession(HttpServletRequest req) {
    boolean result = false;
    //1. 获取Session
    HttpSession session = req.getSession(true);
    //2. 设置Session里面的内容
    session.setAttribute("sunli", "sl1213456");
    result = true;
    return result;
}

访问路径,当新建一个Session对象后,后端会返回一个 Cookie 给前端,根据这个 Cookie 下次验证的时候就能自动登录,不用再次输入账号密码登录了。
img
🍂使用 Servlet 获取 Session**:**

@RequestMapping("/getsession")
public String getSession(HttpServletRequest req) {
    String result = "";
    //1.获取Session
    HttpSession session = req.getSession(false);
    //2.验证
    if (session != null && session.getAttribute("sunli") != null) {
        result = (String) session.getAttribute("sunli");
    }
    return result;
}

img
🍂更加简洁获取 Session,使用@SessionAttribute
该注解有两个属性,value表示需要获取 Session 对象里面内容的 key 值,还有一个require表示修饰的参数是否必须,一般需要设置为false,如果设置成true,没有获取到对应的value就会返回一个400的页面。

@RequestMapping("/getsession2")
public String getSession2(@SessionAttribute(value = "sunli", required = false) String session) {
    return session;
}

img

2.4. 返回数据

2.4.1. 返回普通的text文本/静态页面

SpringMVC/SpringBoot 默认返回的是视图(xxx.html),这是历史原因了,在方法中返回的数据默认是一个静态页面,如果没有找到这个静态页面,就会显示404。

@Controller
public class TestController {

    @RequestMapping("/sayhi")
    public String sayHi() {
        return "hello.html";
    }
}

页面访问结果:

img
我们再简单写一个静态页面,放入static目录,来验证一下是否能够获取到。
img

访问页面结果:

img

而如果想要返回一个 text 文本数据,则需要使用注解@ResponseBody修饰对应的方法或者修饰类才能实现。

@Controller
@ResponseBody
public class TestController {
    @RequestMapping("/sayhi")
    public String sayHi() {
        return "hello.html";
    }
}

img

@ResponseBody既可以修饰类,也可以修饰方法;修饰类时,类中所有方法都会返回一个非静态页面数据;修饰方法时,只有被修饰的方法返回的是一个非静态页面的数据;返回的值如果是字符会就转换为 text/html,如果返回的是对象会转换成 application/json 返回给前端。

组合注解:@RestController = @Controller + @ResponseBody

🍂小案例,实现一个加法计数器。

前端页面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>calc</title>
</head>
<body>
  <form action="calc">
    <h1>计算器</h1>
    数字1<input name="num1" type="text"><br>
    数字2<input name="num2" type="text"><br>
    <input type="submit" value=" 查看结果 ">
  </form>
</body>
</html>

后端处理代码,返回计算结果:

package com.example.demo.controller;

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

@RestController
public class CalcController {
    @RequestMapping("/calc")
    public String calc(Integer num1, Integer num2) {
        // 非空验证
        if (num1 == null || num2 == null) {
            return "<h2>参数错误!</h2><a href='javascript:history.go(-1)'>返回</a>";
        }
        return "<h1>" + (num1+num2) + "</h1>";
    }
}

前端输入数据:
img
后端返回结果:
img

2.4.2. 返回JSON对象

要将指定数据构造成 JSON 格式返回,直接返回一个HashMap<String, Object>对象即可,SpringMVC会自动处理成 JSON 对象。

@RequestMapping("/getjson")
public HashMap<String, String> method_8() {
    HashMap<String, String> map = new HashMap<>();
    map.put("Java", "Java Value");
    map.put("MySQL", "MySQL Value");
    map.put("Redis", "Redis Value");
    return map;
}

img

🍂小案例,实现登录功能。

前端代码如下,实现的功能大致是发送含有账号与密码参数的请求,等待后端验证是否登录成功,给出简单的提示。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
  <title>login</title>
  <script>
    function mysub() {
      let username = jQuery("#username").val();
      let password = jQuery("#password").val();
      jQuery.getJSON("/login",
            {
              "username":username,
              "password":password
            },
            function (result) {
              if(result.succ==200){
                alert("返回结果:"+result.msg);
              }else{
                alert("操作失败,请重试。");
              }
            });
    }
  </script>
</head>
<body>
  <div style="text-align: center;">
    <h1>登录</h1>
    ⽤户:<input id="username">
    <br>
    密码:<input id="password" type="password">
    <br>
    <input type="button" value=" 提交 " onclick="mysub()" style="margin-top: 20px; margin-left: 50px;">
  </div>
</body>
</html>

在后端,我们首先需要接收账号与密码参数,然后对这些参数进行确认,为了方便起见,我们将账号与密码写死,即账号是zhangsan密码是123才能登录成功。

我们返回一个 json 格式的数据给前端,里面包含是否登录成功msg,状态码succ,将这两种参数以键值对的形式储存到哈希表中返回即可。

package com.example.demo.controller;

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

import java.util.HashMap;

@RestController
public class LoginController {
    @RequestMapping(value = "/login")
    public HashMap<String, Object> login(String username, String password){
        HashMap<String, Object> res = new HashMap<>();
        int succ = 200;
        if(username != null && password != null &&
                username.equals("zhangsan") && password.equals("123")){
            res.put("msg","登录成功");
        }else{
            res.put("msg","登录失败");
        }
        res.put("succ",succ);
        return res;
    }
}

实现效果:

img

2.4.3. 实现请求转发与重定向

return 不但可以返回一个视图,还可以实现跳转,跳转的方式分别为两种:

  1. forward:请求转发
  2. redirect:请求重定向

🎯请求转发是服务器端的行为,不管几次转发都是只发生服务器端,经过多次转发拿到数据后发送给浏览器。

请求转发实现方式1:

@RequestMapping("/fw")
    public String myForward() {
        //转发 html 静态页面
        return "forward:/hello.html";
    }

这种方式其实forward:/是可以省略的,因为我们知道默认情况下,返回的就是一个静态页面。

img

转发是发生在服务器内,所以,浏览器中的路由地址自始至终是不会发生改变的
请求转发实现方式2:

@RequestMapping("/fw2")
public void myForward2(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    req.getRequestDispatcher("hello.html").forward(req, resp);
}

效果是一样的:
img

🎯请求重定向其实是多次客户端和浏览器的交互,是浏览器和服务器共同的行为,浏览器发送请求后,服务端只是发送一个重定向响应,告诉浏览器要继续访问哪个 URL,浏览器收到服务器的重定向响应后,就会去访问目标地址。

请求重定向的实现方式1: 与请求转发类似,只不过将forward改为redirect

@RequestMapping("/rd")
public String myRedirect() {
    return "redirect:/hello.html";
}

效果图:
rd路径跳转到hello.html页面了,重定向浏览器中的路由地址是会发生改变的。

img

请求重定向的实现方式2:

@RequestMapping("/rd2")
public void myRedirect2(HttpServletResponse resp) throws IOException {
    resp.sendRedirect("hello.html");
}

效果和上面一样的,就不添效果图了。

🍂请求转发(forward)与请求重定向(redirect)之间的区别:

  • 定义不同:请求转发发生在服务端程序内部,当服务器端收到一个客户端的请求之后,会先将请求,转发给目标地址,再将目标地址返回的结果转发给客户端;请求重定向指的是服务器端接收到客户端的请求之后,会给客户端返回了一个临时响应头,这个临时响应头中记录了客户端需要再次发送请求(重定向)的 URL 地址,客户端再收到了地址之后,会将请求发送到新的地址上。
  • 请求方不同:请求转发是服务器端的行为,是服务器提出请求去获取资源,而请求重定向是客户端的行为,服务器只是告诉客户端去哪里拿资源,实际上是客户端提出请求去获取资源。
  • 数据共享不同,请求转发是服务器端实现的,所以整个执行流程中,客户端(浏览器)只需要发送一次请求,因此整个交互过程中使用的都是同一个 Request 请求对象和一个 Response 响应对象,所以整个请求过程中,请求和返回的数据是共享的;而请求重定向客户端发送两次完全不同的请求,所以两次请求中的数据是不同的,是不共享的。
  • 最终URL地址不同:请求转发是服务器端代为请求,再将结果返回给客户端的,所以整个请求的过程中 URL 地址是不变的;而请求重定向是服务器端告诉客户端去另外一个地方,于是客户端会再次发送一个请求,因此客户端最终访问的 URL 会发生变化,不是最初的那个URL。
  • 代码实现是不同的,写法上类似,但还是有区别,看上面的代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/105900.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MS COCO数据集的评价标准以及不同指标的选择推荐(AP、mAP、MS COCO、AR、@、0.5、0.75、1、目标检测、评价指标)

目标检测模型性能衡量指标、MS COCO 数据集的评价标准以及不同指标的选择推荐 0. 引言 0.1 COCO 数据集评价指标 目标检测模型通过 pycocotools 在验证集上会得到 COCO 的评价列表&#xff0c;具体参数的含义是什么呢&#xff1f; 0.2 目标检测领域常用的公开数据集 PASCAL …

vmware17.0|ubuntu22.04.0 解决灰色Vmware Tool 无法重新安装和 无法和win11相互拖拽文件问题

文章目录 版本&#xff1a;问题&#xff1a;解决方法 版本&#xff1a; vmware 17.0 ubuntu 22.04.0 win11 问题&#xff1a; 无法和windows互相复制粘贴文件 解决方法 1.关闭虚拟机 2.开启虚拟机&#xff0c;在开启虚拟机的过程中再次查看发现灰色图标可点击&#xff0c…

iOS iGameGuardian修改器检测方案

一直以来&#xff0c;iOS 系统的安全性、稳定性都是其与安卓竞争的主力卖点。这要归功于 iOS 系统独特的闭源生态&#xff0c;应用软件上架会经过严格审核与测试。所以&#xff0c;iOS端的作弊手段&#xff0c;总是在尝试绕过 App Store 的审查。 常见的 iOS 游戏作弊&#xf…

Java 基础面试题,JVM 内存模型?

我们在 Java 岗位的面试题中&#xff0c;大概率会碰到这样一个面试题&#xff1a;请你解释你对 JVM 内存模型的理解。 今天我们就来回答一下这个问题&#xff1a; JDK 11 中的 JVM 内存模型可以分为以下几个部分&#xff1a; 程序计数器&#xff08;Program Counter&#xff…

随笔:使用Python爬取知乎上相关问题的所有回答

项目中数据分析的需要自己从知乎某个专门的问题上爬数据&#xff0c;但众所周知&#xff0c;知乎的问题的显示方式有点胃疼&#xff08;指滑动后下翻加载更多回答&#xff0c;还经常卡住&#xff09;&#xff0c;翻了翻网上的教程发现有的要么就是很老了要么就是付费的&#xf…

支付宝证书到期更新完整过程

如果用户收到 支付宝公钥证书 到期通知后&#xff0c;可以根据如下指引更新证书 确认上传成功后就会生成新的证书&#xff0c;把新的证书替换到生产环境就可以了

uniapp:谷歌地图,实现地图展示,搜索功能,H5导航

页面展示 APP H5 谷歌地图功能记录,谷歌key申请相对复杂一些,主要需要一些国外的身份信息。 1、申请谷歌key 以下是申请谷歌地图 API 密钥的流程教程: 登录谷歌开发者控制台:打开浏览器,访问 Google Cloud Platform Console。 1、创建或选择项目:如果你还没有创建项目…

搭建docker本地仓库

1.拉取私有仓库镜像 [rootmaster1 ~]# docker pull registry [rootmaster1 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx v1 546db553f62a About an hour ago …

Java数据类型,变量与运算符

1.字面常量 常量是在程序运行期间&#xff0c;固定不变的量称为常量。 public class HelloWorld{public static void main(String[] args){System.out.println("Hello,world");} } 在以上程序中&#xff0c;输出的Hello Word&#xff0c;其中的“Hello Word”就是…

【STM32】GPIO控制LED(HAL库版)

STM32最新固件库v3.5/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c 林何/STM32F103C8 - 码云 - 开源中国 (gitee.com) STM32最新固件库v3.5/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_gpio.c 林何/STM32F103C8 - 码云 - 开源中国 (gitee.…

如何进行渗透测试以提高软件安全性?

对于各种规模的企业和组织来说&#xff0c;软件安全是一个至关重要的问题。随着网络攻击越来越复杂&#xff0c;软件中的漏洞越来越多&#xff0c;确保你的软件安全比以往任何时候都更重要。提高软件安全性的一个有效方法是渗透测试&#xff08;penetration testing&#xff09…

【AICFD案例操作】潜艇阻力AI预测分析

AICFD是由天洑软件自主研发的通用智能热流体仿真软件&#xff0c;用于高效解决能源动力、船舶海洋、电子设备和车辆运载等领域复杂的流动和传热问题。软件涵盖了从建模、仿真到结果处理完整仿真分析流程&#xff0c;帮助工业企业建立设计、仿真和优化相结合的一体化流程&#x…

哪些场景需要额外注意线程安全问题

今天我们主要学习哪些场景需要额外注意线程安全问题&#xff0c;在这里总结了四种场景。 访问共享变量或资源 第一种场景是访问共享变量或共享资源的时候&#xff0c;典型的场景有访问共享对象的属性&#xff0c;访问 static 静态变量&#xff0c;访问共享的缓存&#xff0c;…

Python爬虫网易云音乐,Tkinter制作音乐播放器

目录 一、效果展示 二、环境 三、实现过程 四、源码 一、效果展示 页面的美化以及功能还有待升级~ 先来说一下已有功能吧&#xff1a; 可以在搜索框中通过歌曲或歌手名称进行搜索&#xff0c;效果和在网易云官网搜索一样。 点击开始下载&#xff0c;就会将搜索结果的第一…

安装python虚拟环境

什么是虚拟环境&#xff1a; 虚拟环境的意义&#xff0c;就如同 虚拟机 一样&#xff0c;它可以实现不同环境中Python依赖包相互独立&#xff0c;互不干扰。 环境准备 安装python &#xff08;到官网下载Download Python​配置环境变量&#xff0c;cmd进入命令行输入 python…

Flutter——最详细(Scaffold)使用教程

Scaffold简介 相当于界面的主体&#xff08;类似于安卓最外层PhoneWindow&#xff09;&#xff0c;组件的展示都必须依附于它。 使用场景&#xff1a; 每一个界面都是脚手架&#xff0c;通过它来进行架构实现&#xff0c;优美的布局效果。 属性作用appBar顶部的标题栏body显示整…

磁盘监控:告警时发送邮件

1.配置邮箱 1.编辑邮箱配置文件 vim /etc/mail.rc2.在末尾输入自己的邮箱配置&#xff0c;以163邮箱为例 #开启ssl set ssl-verifyignore #证书目录&#xff0c;下方为centos系统证书默认位置&#xff0c;也自行生成证书并指定 set nss-config-dir/etc/pki/nssdb # 配置的第…

一文知晓Linux文件权限

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f…

聚量推客滴滴学生认证app地推网推拉新升级啦

“聚量推客”滴滴学生认证项目升级 滴滴学生认证升级后分为微信推广版本和支付宝推广版本两种码 根据自己需要选择推广场景&#xff0c;适合地推和网推

CSRF跨域请求伪造

1.SSRF服务端请求伪造&#xff08;外网访问内网&#xff09; SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下&#xff0c;SSRF是要目标网站的内部系统。&#xff08;因为他是从内部系统访问的&#xf…