[Spring] Spring Web MVC基礎理論

🌸個人主頁:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵?熱門專欄:
🧊 Java基本語法(97平均質量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482
🍕 Collection與數據結構 (92平均質量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀Java EE(96平均質量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql數據庫(93平均質量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均質量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
🍃 Spring(97平均質量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482
感謝點贊與關注~~~
在這里插入圖片描述

目錄

  • 1. 什么是Spring Web MVC
    • 1.1 什么是MVC
    • 1.2 什么是Spring MVC
  • 2. Spring MVC深入學習
    • 2.1 建立連接
      • 2.2.1 @RequsetMapping注解介紹
      • 2.2.2 @RequsetMapping的使用
      • 2.2.3 @RequsetMapping支持哪些方法類型的請求
    • 2.3 請求
      • 2.3.1 傳遞單個參數
      • 2.3.2 傳遞多個參數
      • 2.3.3 傳遞對象
      • 2.3.4 后端參數重命名
      • 2.3.5 傳遞數組
      • 2.3.6 傳遞集合
      • 2.3.7 傳遞json數據
      • 2.3.8 獲取URL中的參數@PathVariable
      • 2.3.9 上傳文件@RequestPart
      • 2.3.10 獲取Cookie/Session
      • 2.3.11 獲取Header
    • 2.4 響應
      • 2.4.1 返回靜態頁面
      • 2.4.2 返回數據@ResponseBody
      • 2.4.3 返回html代碼片段
      • 2.4.4 返回json
      • 2.4.5 設置狀態碼

1. 什么是Spring Web MVC

Spring Web MVC是基于Servlet API構建的原始Web框架,從?開始就包在Spring框架中。它的正式名稱“Spring Web MVC”來自其源模塊的名稱(Spring-webmvc),但它通常被稱為"Spring MVC".
總結來說,Spring Web MVC是一個Web框架.
想要理解什么是Spring MVC我們首先先要理解什么是MVC

1.1 什么是MVC

MVC是Model View Controller的縮寫,是軟件工程中共的一種軟件架構的設計模式.把軟件系統分為模型,控制器,視圖三個部分.
在這里插入圖片描述

  • view(視圖): 指的是在應用中專門用來與瀏覽器交互,展示數據的資源.
  • model(模型): 只應用程序的主題部分,用來處理程序中數據邏輯的部分.
  • controller(控制器): 可以理解為一個分發器,用來決定對于視圖發來的請求,需要哪一個模型來處理,以及處理之后需要跳回哪個視圖.即用來連接視圖和模型.

比如我們去飯店吃飯:
顧客進店之后,服務員來接待客戶點餐,客戶點完餐之后,把客戶菜單交給前廳,前廳根據客戶菜單給后廚下達命令.后廚負責做飯,做完之后,再根據菜單告訴服務員,這是X號餐桌客人的飯.
在這個過程中:

  • 服務員就是view(視圖):負責接待顧客,給顧客點餐,給顧客端飯.
  • 前廳就是controller(控制器):用來給后廚下達做菜的命令.
  • 后廚就是model(模型):根據前廳發來的要求來做菜.

1.2 什么是Spring MVC

MVC是一種架構模式,也是一種思想.而Spring MVC是對MVC思想的具體實現.除此之外,Spring MVC還是一個Web框架.
總結:Spring MVC是一個實現了MVC軟件設計模式的Web框架.
其實Spring MVC在前面我們就使用過了.在我們創建Spring Boot項目的時候,選擇Spring Web的時候,其實就是Spring MVC框架.也就是在創建的Spring Boot項目中添加了Spring Web MVC 的相關依賴,使得該項目具有了網絡通信的功能.
在這里插入圖片描述

  • 那么這時候問題又來了,Spring Boot和Spring MVC究竟有什么關系?
    Spring Boot只是實現Spring MVC的一種方式而已.Spring Boot中可以添加很多依賴,我們在Spring Boot項目中添加了Spring MVC的框架,那么這個Spring Boot項目就可以實現Web的功能.

不過Spring MVC在實現MVC模式的時候,也結合了自身的一些特點,下面這個圖更加適合描述Spring MVC.
在這里插入圖片描述
通過瀏覽器來向后端發送請求的時候,沒有經過view,而是直接把請求傳遞給了controller,之后controller選擇合適的模型,傳遞給model,model處理數據之后,把響應返回給controller,之后controller再把響應返回給view,之后view把響應返回給瀏覽器.

就比如我們去公司面試:我們(瀏覽器)想要面試的時候,我們可以直接找到公司某部門的負責人(controller),說我要面試(請求),之后部門負責人會找到面試你的那個人(model),面試之后,加入你通過了面試,面試你的那個人會把面試結果傳遞給部門負責人,之后部門負責人把消息通知給HR(view),之后HR會給你發offer.

2. Spring MVC深入學習

學習Spring MVC,重點也就是學習用戶通過瀏覽器與服務端交互的過程.
主要分為一下三個點:

  • 建立連接:將用戶(瀏覽器)和Java程序連接起來,也就是Java程序打開了大門,允許外界訪問.此時訪問的地址能夠調用我們的Spring程序.
  • 傳遞參數:用戶請求的時候會帶一些參數.這些參數會傳遞到后端,在后端程序中要想辦法獲取到參數.
  • 返回結果:執行了業務邏輯之后,需要把執行的結果返回給用戶,也就是響應.

比如去銀行存款:

  1. 建立連接:去柜臺
  2. 傳遞參數:拿著身份證,銀行卡去存款
  3. 返回結果:銀行返回一張存折.

掌握了上面的三個功能就相當于掌握了Spring MVC.

2.1 建立連接

在Spring MVC中使用@RequestMapping來實現URL的路由映射,也就是通過這個注解來使得Spring項目與瀏覽器建立連接.代碼如下:

package com.example.demo;
import org.springframework.web.bind.annotation.*;
@RestController
public class DemoController {@RequestMapping("/hello")//可以理解為資源路徑public String hello() {return "Hello World";}
}

接下來訪問http://127.0.0.1:8080/hello就可以看到返回的程序了.
在這里插入圖片描述

2.2.1 @RequsetMapping注解介紹

@RequestMapping是Spring Web MVC應用程序最常被用到的注解之一.它用來注冊接口的路由映射.表示的是,服務器在接收到請求的時候,路徑為/hello的請求就會調用hello這個方法的代碼.

何為路由映射?
當用戶訪問?個URL時,將用戶的請求對應到程序中某個類的某個方法的過程就叫路由映射.

  • 問題:既然@RequestMapping已經達到了我們的目的,我們為什么還要加@RestController呢?
    @RestController在資源訪問的過程中起著相當重要的作用,在Spring項目接收到一個請求之后,Spring會對所有的類進行掃描,如果家里注解@RestController,Spring才會去看這個類里面有沒有加@RequestMapping這個注解,才可以通過瀏覽器中輸入的URL對應到這個類中注冊的路由映射.
    如果我們把@RestController去掉,就訪問不到了.
package com.example.demo;
import org.springframework.web.bind.annotation.*;
@RequestMapping("/demo")
public class DemoController {@RequestMapping("/hello")//可以理解為資源路徑public String hello() {return "Hello World";}
}

在這里插入圖片描述

2.2.2 @RequsetMapping的使用

不僅僅方法前面可以加上@RequestMapping注解,類的前面也可以加該注解,即@RequestMapping不僅僅可以修飾方法,還可以修飾類.當修飾類和方法的時候,訪問的地址是類路徑+方法路徑

package com.example.demo;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/demo")
public class DemoController {@RequestMapping("/hello")//可以理解為資源路徑public String hello() {return "Hello World";}
}

那么在訪問hello這個方法的時候,路徑就會變為/demo/hello.
在這里插入圖片描述
注: 注解中的/hello"demo"雖然不加/也可以正確響應,但是為了編程的規范,還是建議加上.

2.2.3 @RequsetMapping支持哪些方法類型的請求

首先給出結論,@RequsetMapping支持所有方法類型的請求.下面我們來通過Postman構造請求來實驗一下.
在這里插入圖片描述
GET方法支持

POST方法支持
剩下的方法都是同樣的道理,顯示的結果都是Hello World,這里不再一一展示.

  • 那么如何使@RequsetMapping只接受指定的幾種方法的請求呢?
    我們就需要再注解中加上另外的一個參數,method鍵值對
@RequestMapping(value = "/hello1",method = RequestMethod.GET)
public String hello1() {return "Hello World 1";
}

在這里插入圖片描述
在這里插入圖片描述
我們看到/hello1對應的路由映射只支持GET方法.
現在我們去看看method鍵值對截取的部分源碼

public @interface RequestMapping {String name() default "";@AliasFor("path")String[] value() default {};@AliasFor("value")String[] path() default {};RequestMethod[] method() default {};//method返回的是一個RequestMethod類型的數組String[] params() default {};String[] headers() default {};String[] consumes() default {};String[] produces() default {};
}

在源碼中,我們可以看到,method返回的是一個RequestMethod類型的數組.那么這個RequestMethod中都有什么,我們再去查看截取的部分源碼.

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

這里我們可以看到RequestMethod是一個枚舉類型,這里面存放的都是請求中的方法.
由于method那里接收的是一個關于RequestMethod枚舉類型的數組,所以我們在注解后的鍵值對的值上傳入的也是枚舉類型的數組,比如我們想支持GET和POST兩種方法:

@RequestMapping(value = "/hello1",method = {RequestMethod.GET, RequestMethod.POST})
public String hello1() {return "Hello World 1";
}

當然當元素只有一個的時候,大括號是可以省略的.就比如上面那個只有GET方法的例子.

  • 如果指定的方法只有一種,我們也可以采用其他注解來解決
    比如只支持GET方法,我們就可以使用@GetMapping注解來解決.
@GetMapping("/hello3")
public String hello3() {return "Hello World 3";
}

在比如只支持POST方法,可以使用@PostMapping來解決

@PostMapping("/hello4")
public String hello4() {return "Hello World 4";
}

2.3 請求

訪問不同的路徑,就是發送不同的請求.在發送請求時,可能會帶?些參數,所以學習Spring的請求,主要是學習如何傳遞參數到后端以及后端如何接收.
傳遞參數,主要是以瀏覽器和Postman來模擬.

2.3.1 傳遞單個參數

在前面,我們的方法都是沒有參數存在的,如果給我們的方法加上參數之后會怎么樣呢?

@RequestMapping("/name1")
public String name1(String name) {System.out.println("接收到了" + name);return "接收到了" + name;
}

我們在請求的URL中加上查詢字符串,即參數:http://127.0.0.1:8080/demo/name1?name=zhangsan(?后面的是參數)
在URL中加上的參數傳入到后端之后,Spring MVC會根據方法的參數名,找到對應的參數,賦值給方法.之后拿到傳入的參數在方法中進行一系列操作之后返回給前端.比如我們將這個請求通過Postman發送給Spring項目.
在這里插入圖片描述
我們發現成功返回了響應,并且返回了正確的響應.
如果參數不一致,則獲取不到參數.
在這里插入圖片描述

  • 注意事項:參數類型是包裝類型和基本類型的區別
    • 當參數類型是包裝類型和基本類型的時候,傳入的參數Spring進行隱式轉換之后發現參數類型不一致均會報400錯誤.
    @RequestMapping("/age1")
    public String age1(int age) {System.out.println("接收到了" + age);return "接收到了" + age;
    }
    @RequestMapping("/age2")
    public String age2(Integer age) {return "接收到了" + age;
    }
    
    在這里插入圖片描述
    在這里插入圖片描述
    • 但是如果我們不傳遞任何參數的時候,這時候基本類型和包裝類型就會有所區別,基本類型會直接拋出500錯誤,而包裝類型會輸出默認的空值null.
      在這里插入圖片描述
      在這里插入圖片描述
      所以,我們在企業開發中,對于參數可能為空的數據,我們建議使用包裝類型.

2.3.2 傳遞多個參數

和接收單個參數?樣,直接使用方法的參數接收即可.使用多個形參.

@RequestMapping("/person1")
public String person1(String name,Integer age) {return "接收到了name" + name + "接收到了age" + age;
}

在這里插入圖片描述
注:

  1. 也可以通過構造form表單來發送請求,這時候在URL中就沒有了參數的存在,參數跑到了請求中的正文內容.
  2. 如果在項目運行的過程中,我們要對方法的參數進行修改,我們不建議在原來的方法上直接進行修改,而是另起一個方法重新寫.

2.3.3 傳遞對象

如果需要傳遞的參數比較多的時候,我們不妨把這些參數封裝成一個對象.

@RequestMapping("/person3")
public String person3(Person person) {return person.getName()+person.getAge()+person.getSex();
}
package com.example.demo;public class Person {public String name;public int age;public String sex;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}
}

之后我們使用Postman進行請求發送.http://127.0.0.1:8080/demo/person3?name=zhangsan&age=20&sex=男
在這里插入圖片描述
注意:

  1. 如果某個基本類型的參數未傳遞,如果這個基本類型參數是一個類中的成員變量,如果這個參數未傳遞,那么這個參數會有默認的初始值,這一點是和上面直接把基本參數類型的參數寫在方法的參數列表中是不一樣的.比如我沒有傳入age參數.
    在這里插入圖片描述
    我們可以看到age的初始值被默認賦值為了0.
  2. 如果我們針對參數是對象的使用form表單進行參數傳遞,在GET和POST兩種方法中,只有POST方法會返回正確的結果,而GET方法會返回默認的空值.
    在這里插入圖片描述
    在這里插入圖片描述

2.3.4 后端參數重命名

在一些特殊的情況下,前端傳遞的參數key和我們后端接收的key可能不?致,比如我們傳遞了一個Name給后端,但是后端需要接收的參數是name,這時候就需要用到@RequestParam(翻譯:請求參數)來對后端的參數進行重命名.

@RequestMapping("/person2")
public String person2(@RequestParam("Name") String name,Integer age) {return "接收到name" + name + "接收到age" + age;
}

上面這段代碼,其中Name就是前端要傳遞的參數,而name是后端使用的參數,此時Spring可以正確的把請求傳遞的參數Name綁定到后端參數name參數上.
我們使用http://127.0.0.1:8080/demo/person2?Name=zhangsan&age=20來進行請求傳遞
在這里插入圖片描述
如果我們把Name改成name,就無法進行正確的參數傳遞了.

在這里插入圖片描述
[注意事項]
在使用@RequestParam對參數進行重命名的時候,參數就變成了必傳參數.
在這里插入圖片描述
那么造成上面這種情況的原因是什么呢,又該如何讓他變成一個非必傳參數呢?現在我們來查看@RequestParam的源碼:

public @interface RequestParam {@AliasFor("name")String value() default "";@AliasFor("value")String name() default "";boolean required() default true;String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}

我們可以看到,required那一欄默認的值是true,表示的含義就是,該注解修飾的參數是必傳參數.既然如此,我們可以通過設置@RequestParam的required參數=false來實現讓這個參數成為非必傳參數.

@RequestMapping("/person2")
public String person2(@RequestParam(value = "Name",required = false) String name,Integer age) {return "接收到name" + name + "接收到age" + age;
}

在這里插入圖片描述
此時Name為傳遞的時候,會有默認的初始值null來返回.

2.3.5 傳遞數組

Spring MVC可以自動綁定數組參數的賦值.

@RequestMapping("/param1")
public String param1(String[] arrayParam) {return Arrays.toString(arrayParam);
}

使用Postman進行傳參:

  • 請求參數名與形參數組名稱相同且請求參數為多個,后端的方法形式參數中即可自動接收傳輸過來的參數.
    在這里插入圖片描述
  • 把所要傳遞的參數合并在一起傳遞,中間用逗號隔開.
    在這里插入圖片描述
    可以看到以上兩種方法均返回了正確的響應.
  • 我們如果使用from表單進行發送的話,這時候請求GET和POST就只有POST返回的是正確的結果,而GET返回的是null.
    在這里插入圖片描述

2.3.6 傳遞集合

集合參數: 和數組傳遞參數的方法類似,可以是相同的參數名多個參數,也可以把參數寫到一起,但是在后端那里,需要加上@RequestParam來綁定參數關系.
默認的情況下,請求中的參數名相同的多個值,封裝的時候是一個數組,而如果要封裝集合的話,就需要在參數前面加上@RequestParam來綁定參數關系,表示傳過來的是一個數組,需要轉換成集合.

@RequestMapping("/param2")
public String param2(@RequestParam("ArrayParam") List<String> arrayParam) {return Arrays.toString(arrayParam.toArray());
}

在這里插入圖片描述

如果不加注解后面的參數的話,在前端傳遞參數的時候默認就和后端的方法參數是一樣的.
在這里插入圖片描述

2.3.7 傳遞json數據

  • 什么是json
    JSON就是?種數據格式,有自己的格式和語法,使用文本表示一個對象或數組的信息,因此JSON本質是字符串.主要負責在不同的語言中數據傳遞和交換.
  • json的語法
    json是一個字符串,其格式非常類似于python中的字典和JavaScript對象字面量的格式.
    1. 數據存儲在鍵值對(key/value)中,key和value之間用:分割.
    2. 鍵值對之間由,分割.
    3. 對象用{ }表示
    4. 數組用[ ]表示
    5. 值可以為對象,數組,字符串等等.
  • json的兩種結構
    1. 對象: 大括號{}保存的對象是?個無序的鍵值對集合.?個對象以左括號{ 開始,右括號}結束。每個"鍵"后跟?個冒號: ,鍵值對使用逗號,分隔
    2. 數組: 中括號[]保存的數組是值(value)的有序集合.?個數組以左中括號[開始,右中括號]結束,值之間使用逗號, 分隔,數組中可以存放多個對象,對象和對象之間用,分割.

下面我們展示一段json字符串:

{"squadName": "Super hero squad","homeTown": "Metro City","formed": 2016,"secretBase": "Super tower","active": true,//數據保存在鍵值對中//鍵和值之間使用:分割,鍵值對之間使用,分割"members": [{"name": "Molecule Man","age": 29,"secretIdentity": "Dan Jukes","powers": ["Radiation resistance", "Turning tiny", "Radiation blast"]//數組中可以包含多個元素}, {//這個元素也可以是對象"name": "Madame Uppercut","age": 39,"secretIdentity": "Jane Wilson","powers": ["Million tonne punch", "Damage resistance", "Superhuman reflexes"]}, {"name": "Eternal Flame","age": 1000000,"secretIdentity": "Unknown","powers": ["Immortality", "Heat Immunity", "Inferno", "Teleportation", "Interdimensional travel"]}]
}

也可以壓縮表示為:

{"squadName":"Super hero squad","homeTown":"Metro 
City","formed":2016,"secretBase":"Super tower","active":true,"members":
[{"name":"Molecule Man","age":29,"secretIdentity":"Dan Jukes","powers":
["Radiation resistance","Turning tiny","Radiation blast"]},{"name":"Madame 
Uppercut","age":39,"secretIdentity":"Jane Wilson","powers":["Million tonne 
punch","Damage resistance","Superhuman reflexes"]},{"name":"Eternal 
Flame","age":1000000,"secretIdentity":"Unknown","powers":["Immortality","Heat 
Immunity","Inferno","Teleportation","Interdimensional travel"]}]}

可以使用json在線工具來校驗json的書寫,如https://www.json.cn/.

  • json字符串和Java對象的互轉
    Spring MVC框架集成了json的轉換工具,我們可以直接拿來使用.我們可以使用ObjectMapper中的一系列方法來對json和Java對象兩者之間進行轉換.
package com.example.demo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class json {private static ObjectMapper objectMapper = new ObjectMapper();public static void main(String[] args) throws JsonProcessingException {Person person = new Person();person.setName("zhangsan");person.setAge(18);person.setSex("男");String json = objectMapper.writeValueAsString(person);System.out.println(json);Person person2 = objectMapper.readValue(json, Person.class);//傳入一個json字符串和一個類的類對象System.out.println(person2.toString());}
}package com.example.demo;
public class Person {public String name;public int age;public String sex;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", sex='" + sex + '\'' +'}';}
}

在這里插入圖片描述

  • 如何傳遞json對象
    接收json對象,需要使用@RequestBody注解,這個注解翻譯過來就是請求正文的意思,意思是這個注解起到的作用是從請求中的正文部分獲取數據.請求的參數必須寫在正文中.而我們的json字符串就寫在請求的正文中.
    后端是這樣實現的:
@RequestMapping("/param3")
public String param3(@RequestBody Person person) {return person.toString();
}

接下來我們使用Postman構造請求,把json字符串寫入請求的正文中:

{"name":"zhangsan","age":18,"sex":"male"
}

響應結果如下:
在這里插入圖片描述
如果去掉注解@RequestBody,那么后端就無法正確接收json數據,前端返回的響應也不正確.
在這里插入圖片描述
由于后端沒有接收到關于person對象傳遞的任何信息,所以都默認都賦值為了初始值.

2.3.8 獲取URL中的參數@PathVariable

PathVariable翻譯過來之后,是"路徑可變"的意思,意思是我們要通過URL獲取路徑作為傳遞過來的參數,而這個路徑是不固定的.
這個注解主要的作用就是在請求URL路徑上的數據綁定.之前我們通過http請求傳遞參數都是在?后面的查詢字符串中.而現在我們要通過URL中的資源路徑來傳遞參數.
后端代碼實現如下:
@RequestMapping的參數中在加上一些路徑的標識,每個參數外面使用{ }括起來.

@RequestMapping("/param4/{name}/{age}")
public String param4(@PathVariable String name,@PathVariable Integer age){return name+age;
}

我們通過Postman來構造請求http://127.0.0.1:8080/demo/param4/zhangsan/18
在這里插入圖片描述
我們看到前端返回了正確的響應.
[注意事項]
如果方法參數名稱和需要綁定的URL中的變量名稱不?致時,需要@PathVariable的屬性value賦值.

@RequestMapping("/param4/{Name}/{Age}")
public String param4(@PathVariable("Name") String name,@PathVariable("Age") Integer age){return name+age;
}

在這里插入圖片描述
前端還是會返回正確的響應.

2.3.9 上傳文件@RequestPart

后端代碼實現:

@RequestMapping("/param5")
public String param5(@RequestPart("file") MultipartFile file) throws IOException {file.transferTo(new File("D:/personal/" + file.getOriginalFilename()));//上傳文件,前面寫的是存儲文件的路徑,后面加上原文件的名字return "已經獲取到" + file.getOriginalFilename();
}

使用Postman向指定位置發送文件.
在這里插入圖片描述
指定位置接收到了文件.
在這里插入圖片描述

2.3.10 獲取Cookie/Session

  • 回顧Cookie和Session
    Cookie中的內容和URL中的query string內容類似,都是鍵值對內容,它們都是程序員自定義的.每個鍵值對之間用";“隔開,鍵和值之間用”="隔開.
    在這里插入圖片描述

    • Cookie是瀏覽器本地存儲的一種機制,Cookie本質上可以在客戶端的硬盤上持久化保存的.是瀏覽器給網頁的一種能夠持久化存儲數據的機制.
    • 有的網站,需要在客戶端這邊存儲一些必要的信息,希望可以持久化存儲,于是瀏覽器就給網頁提供了Cookie,是瀏覽器對于硬盤的操作做了一些特殊的封裝,相當于提供了一個或者一組特殊的文件,并且內容只能是鍵值對.
    • 那么Cookie是具體如何保存的呢?
      瀏覽器會針對不同的域名,每個網站都有自己的Cookie文件保存在硬盤中.
    • Cookie從哪里來?
      Cookie中的數據,來自于服務器(服務器返回給瀏覽器的數據),訪問網站的時候,網站的服務器會返回http響應,在http響應中,會包含Set-Cookie這樣的header,它就會把一些鍵值對保存到瀏覽器的Cookie中.Cookie保存到瀏覽器之后,后續瀏覽器訪問該網站的時候,就會在請求的header中,把之前保存的鍵值對都帶入進去,在返回給服務器.
    • 那么問題又來了,為什么還要返回給服務器?
      這是因為Cookie可以使客戶端存儲一些必要的"配置信息",從而讓服務器對于用戶提供的服務更加"個性化".

舉例說明:剪頭發
A去了一家理發店之后,理發師就問他:“你想怎么剪?” 但是這時候A就會反問理發師:“你能怎么剪”?于是理發師就說:“可以剪平頭,毛寸…”,于是A就說:"給我剪個平頭."在A下次去了之后,就可以直接告訴理發師,剪個平頭.同理,B也經歷了上述過程,他最終選擇了毛寸.

和上面剪頭發是相同的道理,客戶端也不止有一個,每個客戶端都會有自己的偏好,此時就需要讓每個客戶端保存這樣的數據,之后就可以通過Cookie隨時把這樣的信息返回給服務器.例如:瀏覽器的夜間模式和白日模式,一次設置好了之后,下次再打開服務器的時候,瀏覽器的顏色模式不會改變.

  • Cookie自動登錄
    Cookie中雖然有很多的鍵值對都是程序員自定義的,但是往往會有一個特殊的鍵值對,用來標識用戶的身份信息.
    在這里插入圖片描述
    • 首先在獲取登錄頁面與返回登錄登錄頁面的html的過程中不包含任何的Cookie.
    • 在用戶輸入用戶名和密碼之后,這時候用戶名和密碼就會交給服務器,驗證它們的正確性,在確認正確之后,就會創建會話(session),(會話可以理解為一個類,其中類中具體包含什么,要看業務邏輯,但是其中一定有sessionId,也就是令牌)并把sessionId返回給瀏覽器,這個sessionId存在于響應報文header中的Set-Cookie中,我們也可以把他叫做"令牌",令牌中存儲的是一個字符串,類似于"身份標識",不會存儲太多的信息,在瀏覽器收到sessionId之后,就會把Id存儲在硬盤中,即創建了Cookie.
    • 在之后客戶端要訪問該域名下的其他頁面的時候,就可以把sessionId交給服務器,服務器獲取到sessionId之后,就可以根據這個值,知道用戶的詳細信息.也就是直接通過之前創建的sessionId的Cookie就可以訪問到,無需再次登錄.

舉例說明:去醫院看病

  1. 到了醫院先掛號.掛號時候需要提供?份證,同時得到了?張"就診卡",這個就診卡存儲著關于患者身份信息的sessionId,就相當于患者的"令牌".
  2. 后續去各個科室進行檢查,診斷,開藥等操作,都不必再出示?份證了,只要憑就診卡即可識別出當前患者的?份.在就診室的刷卡機上刷一下就診卡,醫生就會知道你的所有信息.
  3. 看完病了之后,不想要就診卡了,就可以注銷這個卡.此時患者的?份和就診卡的關聯就銷毀了.(類似于?站的注銷操作)
  4. ?來看病,可以辦?張新的就診卡,此時就得到了?個新的"令牌"
  • 使用Spring獲取Cookie
    • 傳統獲取Cookie
      @RequestMapping("/param6")
      public String param6(HttpServletRequest request, HttpServletResponse response) throws IOException  {Cookie[] cookies = request.getCookies();for (Cookie cookie : cookies) {System.out.print(cookie.getName()+":"+cookie.getValue());}return "已經獲取到Cookie";
      }
      
      之后我們通過瀏覽器偽造Cookie,來向后端傳遞請求.http://127.0.0.1:8080/demo/param6
      在這里插入圖片描述
      我們看到了后端已經成功拿到了Cookie中的數據.
      在這里插入圖片描述
      Spring MVC是Spring基于Servlet實現的.其中上面一段代碼中的HttpServletRequestHttpServletResponse是Servlet提供的兩個類.這兩個類在每一個接口中均默認存在,需要的時候寫出來就可以.
      HttpServletRequest 對象代表客戶端的請求.請求中的所有信息均可以通過這個類拿到.
      HttpServletResponse對象代表服務器的響應.響應中的所有信息均可以通過這個類拿到.
    • 獲取Cookie中的某一個鍵值對
      方法一: 使用equals()方法
      @RequestMapping("/param7")
      public String param7(HttpServletRequest request) {Cookie[] cookies = request.getCookies();for (Cookie cookie : cookies) {if ("bite".equals(cookie.getName())) {return cookie.getValue();}}return "為獲取到指定的Cookie";
      }
      
      通過瀏覽器構造Cookie,我們看到了前端返回了正確的響應.
      在這里插入圖片描述
      后端打印了相應的日志:
      在這里插入圖片描述
      方法二:通過@CookieValue注解獲取.
          @RequestMapping("/param8")public String param8(@CookieValue("bite") String bite) {System.out.println("獲取到了" + bite);return bite;}
      
      前端與后端響應均正確:
      在這里插入圖片描述
      在這里插入圖片描述
  • Session的存儲和獲取
    Session是服務器端的機制,它需要先存儲,才能獲取到.
    • Session的存儲
      @RequestMapping("/param9")
      public String param9(HttpServletRequest request){//獲取Session對象,如果不存在Session對象,getSession之后不加或參數為true會自動創建HttpSession session = request.getSession();if (session != null) {//確保Session成功創建session.setAttribute("bite", "888");}return "Session存儲成功";
      }
      
      方法解釋:
      getSession(boolean create): 如果參數為true的時候,就會在Session不存在的時候,自動創建Session,如果參數為false,就不會創建.
      getSession():和上一種方法參數為true的時候效果相同.
      setAttribute(String s,String o):設置Session中的參數.
    • Session的讀取
      讀取Session依然使用HttpServletRequest
      @RequestMapping("/param10")
      public String param10(HttpServletRequest request){HttpSession session = request.getSession(false);if (session != null){String s = (String) session.getAttribute("bite");System.out.println("獲取到了Session");return s;}return "未獲取到Session";
      }
      
    • 運行
      [注意事項] 在重啟服務器之后,上一次存在內存中的Session數據會被清空,需要重新設置Session之后才可以獲取到.
      在這里插入圖片描述
      我們可以看到,存儲了Session之后,瀏覽器把SessionID存儲在了Cookie中.
      在這里插入圖片描述
      之后我們可以使用瀏覽器存儲的令牌獲取Session.
      在這里插入圖片描述
    • 簡潔獲取Session
      上面獲取Session的方法比較傳統,我們下面展示兩種簡潔的方法.
      方法一:使用@SessionAttribute注解
      @RequestMapping("/param11")
      public String param11(@SessionAttribute(value = "bite",required = false)String bite){return bite;
      }
      
      運行結果如下:在這里插入圖片描述
      @SessionAttribute的后面兩個注解表示的意思和前面@RequestParam注解后面的兩個參數的作用非常像.第一個作用是參數綁定的作用,第二個參數如果為true或者不寫,表示這個參數是必傳參數,如果為false,就是非必傳參數,如果Session傳遞未成功,就返回null.
      方法二:直接使用HttpSession作為參數
      @RequestMapping("/param12")
      public String param12(HttpSession session){String string = (String) session.getAttribute("bite");System.out.println("成功獲取到了Session");return string;
      }
      
      HttpSession作為參數的時候,效果和getSession()方法一樣,在沒有Session的時候,會自動創建Session.
      運行結果如下:
      在這里插入圖片描述

2.3.11 獲取Header

  • 傳統獲取方法
    傳統的方法依然是從HttpServletRequst中獲取.
@RequestMapping("/param13")
public String param13(HttpServletRequest request){return request.getHeader("User-Agent");
}

我們使用getHeader方法來獲取Header.在后面的參數是Header中的"key".
運行測試:
在這里插入圖片描述
下面是我們通過抓包軟件抓取的網絡通行信息.我們發現Header中的User-Agent一欄與瀏覽器上的一致.
在這里插入圖片描述

  • 簡潔獲取方法
    通過@RequestHeader注解來獲取,在注解后面加上需要獲取Header中的"key".
@RequestMapping("/param14")
public String param14(@RequestHeader("sec-ch-ua-platform") String string){return string;
}

運行結果:
在這里插入圖片描述
與抓包工具中的結果一致:
在這里插入圖片描述

2.4 響應

在我們前面代碼的例子中,每次瀏覽器都會返回響應的響應,而前面的響應都是數據響應,響應還可以是頁面,狀態碼,Header等.

2.4.1 返回靜態頁面

首先我們需要穿件一個前端頁面index.html.創建的文件放在static目錄下.

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>我是index文件</h1>
</body>
</html>

[注意] 在寫完前端文件之后,不要通過idea右上角的瀏覽器小圖標的方式打開,我們需要通過后端返回頁面的方式打開.
在這里插入圖片描述
下面我們展示一種后端代碼的寫法:

@RequestMapping("/demo2")
@RestController
public class DemoController2 {@RequestMapping("/param1")public Object param1(){return "/index.html";}
}

在這里插入圖片描述
我們看到,瀏覽器并沒有返回對應的html頁面,而是直接返回了一串字符串數據.那么怎么解決呢.我們需要把@RestController注解變成@Controller注解.

@RequestMapping("/demo2")
@Controller
public class DemoController2 {@RequestMapping("/param1")public Object param1(){return "/index.html";}
}

我們看到這次瀏覽器返回了對應的html頁面.
在這里插入圖片描述

  • 那么@RestController@Controller有什么區別呢?
    @RestController一般用來返回數據,@Controller一般用來返回視圖.就是前面我們在MVC設計模式中提到過的視圖(view).
    @RestController == @Controller + @ResponseBody.其中@Controller表示的是我們前面我們MVC模式中的控制器.@ResponseBody表示的是響應正文,定義返回的數據為非視圖模式.
    @RestController的源碼如下,我們也可以看到這個注解上面也標有@Controller + @ResponseBody兩個注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {@AliasFor(annotation = Controller.class)String value() default "";
}

2.4.2 返回數據@ResponseBody

上面我們提到了@ResponseBody注解表示的是返回數據.
@ResponseBody即是類注解又是方法注解.給類加上該注解之后,就表示類中所有的方法返回的都是數據,給方法加上之后,表示的是只有這個方法返回的是數據.

同樣,如果類上有@RestController注解的時候,就證明給所有的方法都加了@ResponseBody注解,所有的方法都以數據的方式返回.

如果一個類中即有頁面要返回,也有數據要返回,就在需要返回數據的方法上面加上@ResponseBody.類的控制器使用@Controller.

@RequestMapping("/demo2")
@Controller
public class DemoController2 {@RequestMapping("/param1")public Object param1(){return "/index.html";}@RequestMapping("param2")@ResponseBodypublic Object param2(){return "String";}
}

瀏覽器返回的視圖與數據如下:
在這里插入圖片描述
在這里插入圖片描述

2.4.3 返回html代碼片段

    @RequestMapping("/param3")@ResponseBodypublic String param3(){return "<h1>我是html~</h1>";}

這里需要注意的是,返回html也需要加上@ResponseBody注解.
在這里插入圖片描述

2.4.4 返回json

后端返回json的方法是使用對象返回.可以使用HashMap返回,也可以另外創建一個對象,按屬性返回.
方法一:創建HashMap

@RequestMapping("/param4")
@ResponseBody
public HashMap<String,Integer> param4(){HashMap<String, Integer> map = new HashMap<>();map.put("aa",1);map.put("bb",2);map.put("cc",3);return map;
}

瀏覽器返回響應,是json格式的數據:
在這里插入圖片描述
方法二:通過類中的屬性

@RequestMapping("/param5")
@ResponseBody
public Person param5(){Person person = new Person();person.setName("zhangsan");person.setAge(19);person.setSex("male");return person;
}

瀏覽器返回響應,依然是json格式的數據,返回的是對象中屬性的值:
在這里插入圖片描述

2.4.5 設置狀態碼

SpringMVC也為程序員提供了自定義狀態碼的功能,可以讓程序員手動指定狀態碼.
使用HttpServletResponse+setStatus方法訪問到響應中的狀態碼.

@RequestMapping("/param6")
@ResponseBody
public String param6(HttpServletResponse response){response.setStatus(400);return "設置狀態碼為400";
}

瀏覽器返回響應,需要注意的是,我們自定義的錯誤狀態碼返回的不一定是瀏覽器報錯的一大坨信息,狀態碼并不影響頁面的顯示.
在這里插入圖片描述
我們通過抓包工具發現,響應的狀態碼被設置為了400.圖標也變為了紅色.
在這里插入圖片描述
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/44494.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/44494.shtml
英文地址,請注明出處:http://en.pswp.cn/web/44494.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

n3.平滑升級和回滾

平滑升級和回滾 1. 平滑升級流程2. 平滑升級和回滾案例 有時候我們需要對Nginx版本進行升級以滿足對其功能的需求&#xff0c;例如添加新模塊&#xff0c;需要新功能&#xff0c;而此時 Nginx又在跑著業務無法停掉&#xff0c;這時我們就可能選擇平滑升級 1. 平滑升級流程 平…

使用ChatGPT來撰寫和潤色學術論文的教程(含最新升級開桶ChatGpt4教程)

現在有了ChatGPT4o更加方便了, 但次數太少了 想要增加次數可以考慮升級開桶ChatGpt4 一、引言 在學術研究中&#xff0c;撰寫高質量的論文是一項重要的技能。本教程將介紹如何利用ChatGPT來輔助完成從論文構思到潤色的全過程。 二、使用ChatGPT寫論文 1. 寫標題 Title/Topic…

【TB作品】51單片機,MSP430單片機,STM32單片機,簡易波形發生器

https://docs.qq.com/sheet/DUEdqZ2lmbmR6UVdU?tabBB08J2二、 簡易波形發生器 &#xff08;限MSP430、STM32單片機&#xff09; 任務要求&#xff1a; 制作一個簡易波形發生器&#xff0c;具有如下功能&#xff1a; 1、能夠產生方波、正弦波&#xff0c;并可通過示波器觀察到&…

QT 多線程 QThread

繼承QThread的線程 繼承 QThread 是創建線程的一個普通方法。其中創建的線程只有 run() 方法在線程里的。其他類內定義的方法都在主線程內。 通過上面的圖我們可以看到&#xff0c;主線程內有很多方法在主線程內&#xff0c;但是子線程&#xff0c;只有 run() 方法是在子線…

基于STM32設計的藥品柜溫濕度監測系統(華為云IOT)(184)

基于STM32設計的藥品柜溫濕度監測系統(華為云IOT)(184) 文章目錄 一、前言1.1 項目介紹【1】項目功能介紹【2】整體需求總結【3】項目硬件模塊組成1.2 設計思路【1】整體設計思路【2】ESP8266工作模式配置【3】華為云IOT手機APP界面開發思路1.3 項目開發背景【1】選題的意義【2…

R語言學習筆記6-數據框

R語言學習筆記6-數據框 數據框(DataFrame)介紹數據框用途創建數據框從矩陣創建數據框索引和切片添加和修改列數據框的預處理數據框的排序數據框的合并與拆分數據框的計算與匯總數據框的篩選處理缺失值應用函數處理數據重塑數據框使用 dplyr 進行數據框的管道操作數據框的時間序…

使用 WebSocket 進行實時數據傳輸

以下是使用 WebSocket 進行實時數據傳輸的一般步驟&#xff1a; 一、前端部分 &#xff08;一&#xff09;創建 WebSocket 連接 const socket new WebSocket(ws://your-server-url); 在上述代碼中&#xff0c;將 ws://your-server-url 替換為您實際的服務器 WebSocket 地…

SvANet:微小醫學目標分割網絡,增強早期疾病檢測

SvANet&#xff1a;微小醫學目標分割網絡&#xff0c;增強早期疾病檢測 提出背景前人工作醫學對象分割微小醫學對象分割注意力機制 SvANet 結構圖SvANet 解法拆解解法邏輯鏈 論文&#xff1a;SvANet: A Scale-variant Attention-based Network for Small Medical Object Segmen…

【JAVA poi-tl-ext 富文本轉word】

富文本轉word 環境使用poi-tl-ext的原因富文本轉word代碼 環境 jdk 1.8 <dependency><groupId>io.github.draco1023</groupId><artifactId>poi-tl-ext</artifactId><version>0.4.16</version> </dependency>poi-tl-ext已經包…

可靈重大升級!新增Web端上線、首尾幀控制、單次生成視頻時長增加至10s!

快手視頻生成大模型“可靈”&#xff08;Kling&#xff09;&#xff0c;作為全球首個真正用戶可用的視頻生成大模型&#xff0c;自面世以來&#xff0c;憑借其無與倫比的視頻生成效果&#xff0c;在全球范圍內贏得了用戶的熱烈追捧與高度評價。截至目前&#xff0c;申請體驗其內…

修正版頭像上傳組件

修正版頭像上傳組件 文章說明核心源碼展示運行效果展示源碼下載 文章說明 在頭像剪切上傳一文中&#xff0c;我采用div做裁剪效果&#xff0c;感覺會有一些小問題&#xff0c;在昨天基于canvas繪制的功能中改進了一版&#xff0c;讓代碼變得更簡潔&#xff0c;而且通用性相對高…

永恒之藍:一場網絡風暴的啟示

引言 在網絡安全的漫長歷史中&#xff0c;“永恒之藍”&#xff08;EternalBlue&#xff09;是一個不可忽視的里程碑事件。它不僅揭示了網絡世界的脆弱性&#xff0c;還促使全球范圍內對網絡安全的重視達到了前所未有的高度。本文將深入探討“永恒之藍”漏洞的起源、影響及其對…

【WebGIS】從設計層面設計系統

本項目在通過現代信息技術手段&#xff0c;對古村古鎮進行多方位、多角度的數字化記錄、展示與傳播&#xff0c;實現文化遺產的數字化保護、活化利用與共享。項目內容主要包括&#xff1a;1&#xff09;古村古鎮數據庫的建立&#xff1a;通過多種渠道收集古村古鎮的各類信息&am…

期貨量化交易客戶端開源教學第八節——TCP通信服務類

private FReciveStr: AnsiString; {接收到的數據} IsConErr: Boolean; {網絡連接是否失敗} FSocket_LB: Integer; {TCP連接類別,0為交易,1為行情,2為查詢} FRetryCount: Integer; {網絡連接重試次數} FLoginErrEvent: TLoginErrEvent; {…

如何從 PDF 中刪除背景

您是否曾經收到過充滿分散注意力背景的掃描 PDF 文檔&#xff1f;也許是帶有繁忙水印的舊收據或背景光線不均勻的掃描文檔。雖然這些背景可能看起來沒什么大不了的&#xff0c;但它們會使您的工作空間變得混亂&#xff0c;并使您難以專注于重要信息。輕松刪除這些不需要的元素并…

短視頻SEO矩陣系統:源碼開發與部署全攻略

在數字化時代&#xff0c;短視頻已成為人們獲取信息、娛樂休閑的重要方式。隨著短視頻平臺的興起&#xff0c;如何讓自己的內容在眾多視頻中脫穎而出&#xff0c;成為每個創作者和內容運營者關注的焦點。本文將為您深入解析短視頻SEO矩陣系統的源碼開發與部署&#xff0c;助您在…

MT6825磁編碼IC在智能雙旋機器人中的應用

MT6825磁編碼IC在智能雙旋機器人中的應用&#xff0c;無疑為這一領域的創新和發展注入了新的活力。作為一款高性能的磁性位置傳感器&#xff0c;MT6825以其獨特的優勢&#xff0c;在智能雙旋機器人的運動控制、定位精度以及系統穩定性等方面發揮了關鍵作用。 www.abitions.com …

django ninja get not allowed 能用 put delete

遇到一個奇怪的問題&#xff0c;django-ninja 編寫的 get post 方法不能使用 # 獲取Material router.get(/material, responseList[MaterialSchemaOut]) paginate(MyPagination) def list_material(request, filters: Filters Query(...)):qs retrieve(request, Material, f…

Midjourney v6.5 可能會在“7月底”發布,并改進了真實感和皮膚紋理

Midjourney v6.5即將發布&#xff0c;這一更新將大幅提升圖像的真實感和皮膚紋理&#xff0c;為用戶帶來更逼真的視覺體驗。首席執行官David Holz在電話會議中宣布&#xff0c;新版本將提高圖像清晰度&#xff0c;特別是在手部和皮膚細節上&#xff0c;同時改進Web應用程序和個…

ABAP調用BAPI時COMMIT WORK AND WAIT未按照預期同步提交問題分析

背景&#xff1a; 在做ABAP開發時&#xff0c;經常會有連續調用BAPI的需求&#xff0c;比如先創建銷售訂單&#xff0c;再依據銷售訂單創建交貨單&#xff0c;再對交貨單進行過賬等類似的一連串調用&#xff0c;這種類似的場景往往需要前一步操作的數據完全寫入數據庫才能進行…