SpringBoot前後端分離參數傳遞方式總結

前言: 因爲總是需要使用不同的參數傳遞方式,所以特地來總結一下SpringBoot中常用的參數的綁定方式,給有需要的朋友查閱。

SpringBoot參數傳遞

在這裏插入圖片描述

注意:雖然Restful風格很流行,但是大部分還是主要是GET和POST的內容,所以這裏只是列舉GET和POST請求爲例。 而且,無論怎麼樣的花樣傳參,它都是符合上面這個報文結構的!正所謂:萬變不離其宗嘛!

GET請求方式

注意:我這裏是示例形式是:代碼+Postman測試截圖+Fiddler抓包截圖。

01.單個鍵值對參數

/**
 * GET 方式傳遞參數  單個參數
 * */
@GetMapping("/get01")
public String get01(String comment) {
   
   
	return comment == null ? "no parameter" : comment;
}

請求不帶參數
在這裏插入圖片描述

請求報文中也沒有數據,響應報文體中有數據
在這裏插入圖片描述

請求帶參數
在這裏插入圖片描述

請求攜帶數據,數據在請求行中,注意數據被編碼了
在這裏插入圖片描述

使用了@RequestParam註解,請求必須攜帶參數,否則就會報錯,否則就是:錯誤碼400 Bad Request

@GetMapping("/get02")
public String get02(@RequestParam("comment") String comment) {
   
   
	return comment;
}

請求不攜帶參數,請求錯誤 400 Bad Reqeust
在這裏插入圖片描述

請求攜帶參數,接收成功
在這裏插入圖片描述
請求和響應報文
在這裏插入圖片描述


如果參數不添加 @RequestParam 註解,那麼這個參數即可不傳遞,而使用了註解的話,默認是必須傳遞參數的,當然了也可以配置爲false。但是,我傾向於還是顯示使用註解,這樣比較清晰,也可配置,更加靈活。

在這裏插入圖片描述

02.多個鍵值對參數

/**
 * GET 方式傳遞參數  多個參數
 * */
@GetMapping("/get03")
public String get03(@RequestParam("id") String id,
		@RequestParam("name") String name,
		@RequestParam("comment") String comment) {
   
   
	System.out.println(id + " " + name + " " + comment);		
	return id + " " + name + " " + comment;
}

請求行攜帶多個參數
在這裏插入圖片描述

請求和響應報文
在這裏插入圖片描述

03.鍵值對映射對象

/**
 *  使用對象對參數進行封裝,這樣在多個參數時,優勢很明顯。
 *  但是這裏無法使用 @RequestParam註解,否則會出錯。
 * */
@GetMapping("/get04")
public Comment get04(Comment comment) {
   
   
	if (Objects.isNull(comment)) {
   
   
		return null;  // 需要對 null 值進行處理
	}
	System.out.println(comment);
	return comment;
}

請求攜帶多個參數,直接映射成對象,但是這裏無法使用@RequestParam,同時也無法使用@RequestBody,因爲參數不在請求體中,至於爲什麼無法使用,暫時還沒明白!
在這裏插入圖片描述

請求和響應報文
在這裏插入圖片描述

因爲沒有使用註解,可以不攜帶參數
在這裏插入圖片描述

04.鍵值對映射Map

/**
  * 使用對象封裝參數要求必須具有一個對象,所以可以使用 Map 來封裝,這樣可以減少對象的數
  * 量。 
  * * */
@GetMapping("/get05")
public Map<String, String> get05(@RequestParam Map<String, String> map) {
   
   
	map.forEach((k, v) -> {
   
   
		System.out.println(k + " --> " + v);
	});
	System.out.println(map.size());
	return map;
}

在這裏插入圖片描述

多個鍵值對參數
在這裏插入圖片描述

請求和響應報文
在這裏插入圖片描述

05.路徑參數

/**
 * 參數和路徑結合,適用於單個參數的情況
 * */
@GetMapping("/get06/{id}")
public Comment getById(@PathVariable("id") String id) {
   
   
	Comment comment = new Comment();
	comment.setId(id);
	comment.setName("Alfred");
	comment.setComment("I love you yesterday and today!");
	return comment;
}

請求直接寫在路徑上,成爲路徑的一部分
在這裏插入圖片描述

請求和響應體

注: 請求參數就在路徑上面。
在這裏插入圖片描述
在這裏插入圖片描述

06.返回值爲二進制

前面都是文本數據,現在我們嘗試來獲取二進制數據,注意這個方法需要下面的上傳文件方法向上傳文件,或者你自己在文件夾下面放入一個文件。

/**
 * 返回值爲二進制
 * 其實這裏可以使用 Files.readAllBytes()這個方法,這樣就簡單了。這裏我就不改了,我習慣了使用這種
 * 循環讀取的方式,不過確實有點繁瑣了。
 * */
@GetMapping("/get07/{name}")
public void getFile(@PathVariable("name") String name, HttpServletResponse response) {
   
   
	try (OutputStream out = new BufferedOutputStream(response.getOutputStream())) {
   
   
		try (InputStream in = new BufferedInputStream(new FileInputStream(new File(baseDir, name)))) {
   
   
			int len = 0;
			byte[] data = new byte[4*1024];
			while ((len = in.read(data)) != -1) {
   
   
				out.write(data, 0, len);
			}
		}
	} catch (IOException e) {
   
   
		e.printStackTrace();
	}
}

響應體中含有圖片的數據
在這裏插入圖片描述

請求體報文
在這裏插入圖片描述

響應報文,注意圖片的二進制數據無法解碼,會顯示出亂碼的效果
在這裏插入圖片描述

使用ImageView的方式查看圖片
在這裏插入圖片描述

POST請求方式

01.多個鍵值對參數

/**
 * POST方式傳遞參數
 * @return 
 * */
@PostMapping("/post01")
public String post01(@RequestParam("id") String id,
		@RequestParam("name") String name,
		@RequestParam("comment") String comment) {
   
   
	System.out.println(id + " " + name + " " + comment);		
	return id + " " + name + " " + comment;
}

請求體中攜帶鍵值對參數,注意Content-Type類型
在這裏插入圖片描述

請求參數以鍵值對的形式放在請求體中,注意它也是被編碼的
在這裏插入圖片描述

請求體中攜帶鍵值對參數,注意Content-Type類型爲form-data

在這裏插入圖片描述

請求體中的數據以表單數據的形式存放,注意其形式
在這裏插入圖片描述

02.鍵值對映射Map

@PostMapping("/post02")
public Map<String, String> post02(@RequestParam Map<String, String> map) {
   
   
	map.forEach((k, v) -> {
   
   
		System.out.println(k + " --> " + v);
	});
	return map;
}

Content-Type選擇:form-data
在這裏插入圖片描述

在這裏插入圖片描述

Content-Type選擇:x-www-form-urlencoded

在這裏插入圖片描述

在這裏插入圖片描述

03.傳遞json數據映射對象

@PostMapping("/post03")
public Comment post03(@RequestBody Comment comment) {
   
   
	System.out.println(comment);
	return comment;
}

請求參數形式爲json字符串,並且選擇Content-Type選擇 raw,不能選擇其它形式的原因的,form-data和x-www-form-urlencoded都會改變請求參數,通過上面的對比都能看出來了。

在這裏插入圖片描述

請求體中的數據就是原始的傳遞數據,並不會改變
在這裏插入圖片描述

04.json數組映射對象數組

/**
 * 傳遞對象數組
 * */
@PostMapping("/post04")
public Comment[] post04(@RequestBody Comment[] comments) {
   
   
	return comments;
}

請求參數爲一個json數組,這個東西以前可是難到我了,我去網上找了一個直接映射成List的寫法,但是後來遇到這個問題我一想,既然單個json是一個對象,那麼json數組,不就是一個對象數組嗎?試了一下,果然是這樣的!
在這裏插入圖片描述
在這裏插入圖片描述

05.json數組映射List

@PostMapping("/post05")
public List<Comment> post05(@RequestBody List<Comment> commentList) {
   
   
	return commentList;
}

請求參數直接映射成List
在這裏插入圖片描述
在這裏插入圖片描述

06.傳遞二進制數據(文件)

/**
 * 傳遞二進制數據
 * */
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) {
   
   	
	if (!file.isEmpty()) {
   
   
		String fileName = file.getOriginalFilename();
		try {
   
   
			file.transferTo(new File(baseDir, fileName)); // 對於 SpringBoot 中使用路徑還是懵逼!
			return "success";
		} catch (IllegalStateException e) {
   
   
			e.printStackTrace();
		} catch (IOException e) {
   
   
			e.printStackTrace();
		}
	} 
	return "Fail";
}

以前使用Servlet的時候,上傳文件是很複雜的,後來Servlet3.0中進行了改進,現在框架又進行了進一步的封裝,使用起來更加方便了。
在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

請求報文
在這裏插入圖片描述
在這裏插入圖片描述

8.表單數據(文本+文件)

/**
 *  表單數據,含文本和二進制
 * */
@PostMapping("/submitInfo01")
public String submitInfo(@RequestParam("id") String id,
		@RequestParam("name") String name,
		@RequestParam("file") MultipartFile file) {
   
   
	
	System.out.println("id: " + id);
	System.out.println("name: " + name);
	System.out.println("fileName: " + file != null ? file.getOriginalFilename() : "null");
	
	if (!file.isEmpty()) {
   
   
		String fileName = file.getOriginalFilename();
		try {
   
   
			file.transferTo(new File(baseDir, fileName));
			return "success";
		} catch (IllegalStateException e) {
   
   
			e.printStackTrace();
		} catch (IOException e) {
   
   
			e.printStackTrace();
		}
	} 
	return "Fail";
}

表單通常是可以攜帶不同的數據,主要是因爲它的形式很適合這樣做,所以可以同時接收文件和文本數據。表單數據使用一個boundary來隔開不同的數據。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述


09.表單數據,進一步封裝成對象

上面那樣如果表單項比較多的話,映射還是比較麻煩的,可以選擇創建一個對象封裝所有的屬性,這樣處理起來就會更加方便,並且也是面向對象思想的應用。

/**
 *    表單數據,含文本和二進制 進一步封裝!
 * */
@PostMapping("/submitInfo02")
public String submitInfo02(User user) {
   
   
	
	MultipartFile file = user.getFile();
	System.out.println("id: " + user.getId());
	System.out.println("name: " + user.getName());
	System.out.println("fileName: " + user != null ? file.getOriginalFilename() : "null");
	
	if (!file.isEmpty()) {
   
   
		String fileName = file.getOriginalFilename();
		try {
   
   
			file.transferTo(new File(baseDir, fileName));
			return "success";
		} catch (IllegalStateException e) {
   
   
			e.printStackTrace();
		} catch (IOException e) {
   
   
			e.printStackTrace();
		}
	} 
	return "Fail";
}

在這裏插入圖片描述

在這裏插入圖片描述

10.ajax2.0傳遞二進制數據

/**
 * POST以二進制形式傳遞文件,通常的web表單是做不到的,但是ajax2.0以後是支持的,我們來嘗試一下。
 * 注意它和 Multipart的區別,Multipart實際上不只包含文件本身的數據,還有文件的其它的信息,例如剛纔獲取的文件名。
 * 但是如果以二進制的形式傳遞,它就是完全的文件數據流,不包含任何其它信息,只有文件本身的二進制數據流。
 * 
 * 使用這種形式,只能傳輸單個文件,無法傳輸多個文件,因爲它只是文件本身的二進制數據,如果是多個的話,
 * 那麼誰也別想從一個連續的二進制流中把圖片切分出來了。
 * */
@PostMapping("/binaryFile")
public String binaryFile(@RequestBody byte[] fileData) {
   
   
	try {
   
   
		Files.write(Paths.get(baseDir, UUID.randomUUID().toString() + ".jpg"), fileData);
		return "success";
	} catch (IOException e) {
   
   
		e.printStackTrace();
		return e.getMessage();
	}
}

在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

baseDir路徑下的文件

在這裏插入圖片描述

增補拾遺

GET請求方式,也是可以在映射請求體中的數據的,但是對報文的Content-Type有要求,並且不推薦這樣使用!

/**
 *  使用對象對參數進行封裝,這樣在多個參數時,優勢很明顯。
 *  但是這裏無法使用 @RequestParam註解,否則會出錯。
 * */
@GetMapping("/get04")
public Comment get04(Comment comment) {
   
   
	if (Objects.isNull(comment)) {
   
   
		return null;  // 需要對 null 值進行處理
	}
	System.out.println(comment);
	return comment;
}

Content-Type: x-www-form-urlencoded

注:無法接收參數
在這裏插入圖片描述

在這裏插入圖片描述

Content-Type: form-data
注:可以接收數據。
在這裏插入圖片描述
在這裏插入圖片描述


/**
  * 使用對象封裝參數要求必須具有一個對象,所以可以使用 Map 來封裝,這樣可以減少對象的數
  * 量。 
  * * */
@GetMapping("/get05")
public Map<String, String> get05(@RequestParam Map<String, String> map) {
   
   
	map.forEach((k, v) -> {
   
   
		System.out.println(k + " --> " + v);
	});
	System.out.println(map.size());
	return map;
}

在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

GET請求,但是數據在請求體中

這種方式違背了通常的web數據傳輸,因爲我們通常是規定GET方式沒有請求體,POST方式數據在請求體中的。但是GET方式是可以有請求體的,POST方式也可以把參數方式放到請求行中,甚至於GET和POST的請求行和請求體中都可以攜帶數據。但是這樣的話,可就苦了前端了,因爲表單的發送請求的形式基本是固定的,出了ajax可以多一些花樣。所以,如果你是一個GET請求,但是使用@RequestBody來接收參數,這個可就夠前端難受的了。年輕人要講究武德,不要亂用,但是不是不能用,如果不是Web項目,那就可以隨便用了。

@GetMapping("/not_use_like_this")
public Comment not_use_like_this(@RequestBody Comment comment) {
   
   
	System.out.println(comment);
	return comment;
}

在這裏插入圖片描述

在這裏插入圖片描述

全部代碼

目錄結構

package request_learn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class LearnApplication {
   
   
	
	public static void main(String[] args) {
   
   
		SpringApplication.run(LearnApplication.class, args);
	}
	
}

package request_learn.entity;

import java.io.Serializable;

public class Comment implements Serializable {
   
   

	private static final long serialVersionUID = 1L;
	
	private String id;
	private String name;
	private String comment;
	
	// 省略getter、setter和toString方法
}

package request_learn.entity;

import org.springframework.web.multipart.MultipartFile;

public class User {
   
   
	
	private String id;
	private String name;
	private MultipartFile file;
	
	// 省略getter、setter和toString方法
}

package request_learn.controller;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import request_learn.entity.Comment;
import request_learn.entity.User;

@RestController
@RequestMapping("/test")
public class LearnController {
   
   

	private static final String baseDir = "D:/test/img/";
	
	/**
	 * GET 方式傳遞參數  單個參數
	 * */
	@GetMapping("/get01")
	public String get01(String comment) {
   
   
		return comment == null ? "no parameter" : comment;
	}
	
	
	@GetMapping("/get02")
	public String get02(@RequestParam("comment") String comment) {
   
   
		return comment;
	}
	
	/**
	 * GET 方式傳遞參數  多個個參數
	 * */
	@GetMapping("/get03")
	public String get03(@RequestParam("id") String id,
			@RequestParam("name") String name,
			@RequestParam("comment") String comment) {
   
   
		System.out.println(id + " " + name + " " + comment);		
		return id + " " + name + " " + comment;
	}
	
	/**
	 *  使用對象對參數進行封裝,這樣在多個參數時,優勢很明顯。
	 *  但是這裏無法使用 @RequestParam註解,否則會出錯。
	 * */
	@GetMapping("/get04")
	public Comment get04(Comment comment) {
   
   
		if (Objects.isNull(comment)) {
   
   
			return null;  // 需要對 null 值進行處理
		}
		System.out.println(comment);
		return comment;
	}
	
	/**
	 * 使用對象封裝參數要求必須具有一個對象,所以可以使用 Map 來封裝,這樣可以減少對象的數量。
	 * */
	@GetMapping("/get05")
	public Map<String, String> get05(@RequestParam Map<String, String> map) {
   
   
		map.forEach((k, v) -> {
   
   
			System.out.println(k + " --> " + v);
		});
		System.out.println(map.size());
		return map;
	}
	
	/**
	 * 參數和路徑結合,適用於單個參數的情況
	 * */
	@GetMapping("/get06/{id}")
	public Comment getById(@PathVariable("id") String id) {
   
   
		Comment comment = new Comment();
		comment.setId(id);
		comment.setName("Alfred");
		comment.setComment("I love you yesterday and today!");
		return comment;
	}
	
	/**
	 * 返回值爲二進制
	 * */
	@GetMapping("/get07/{name}")
	public void getFile(@PathVariable("name") String name, HttpServletResponse response) {
   
   
		try (OutputStream out = new BufferedOutputStream(response.getOutputStream())) {
   
   
			try (InputStream in = new BufferedInputStream(new FileInputStream(new File(baseDir, name)))) {
   
   
				int len = 0;
				byte[] data = new byte[4*1024];
				while ((len = in.read(data)) != -1) {
   
   
					out.write(data, 0, len);
				}
			}
		} catch (IOException e) {
   
   
			e.printStackTrace();
		}
	}
	
	
	/**
	 * POST方式傳遞參數
	 * @return 
	 * */
	@PostMapping("/post01")
	public String post01(@RequestParam("id") String id,
			@RequestParam("name") String name,
			@RequestParam("comment") String comment) {
   
   
		System.out.println(id + " " + name + " " + comment);		
		return id + " " + name + " " + comment;
	}
	
	@PostMapping("/post02")
	public Map<String, String> post02(@RequestParam Map<String, String> map) {
   
   
		map.forEach((k, v) -> {
   
   
			System.out.println(k + " --> " + v);
		});
		return map;
	}
	
	
	@PostMapping("/post03")
	public Comment post03(@RequestBody Comment comment) {
   
   
		System.out.println(comment);
		return comment;
	}
	
	
	/**
	 *    傳遞二進制數據
	 * */
	@PostMapping("/upload")
	public String uploadFile(@RequestParam("file") MultipartFile file) {
   
   	
		if (!file.isEmpty()) {
   
   
			String fileName = file.getOriginalFilename();
			try {
   
   
				file.transferTo(new File(baseDir, fileName)); // 對於 SpringBoot 中使用路徑還是懵逼!
				return "success";
			} catch (IllegalStateException e) {
   
   
				e.printStackTrace();
			} catch (IOException e) {
   
   
				e.printStackTrace();
			}
		} 
		return "Fail";
	}
	
	
	/**
	 * 傳遞對象數據
	 * */
	@PostMapping("/post04")
	public Comment[] post04(@RequestBody Comment[] comments) {
   
   
		return comments;
	}
	
	@PostMapping("/post05")
	public List<Comment> post05(@RequestBody List<Comment> commentList) {
   
   
		return commentList;
	}
	
	
	/**
	 *    表單數據,含文本和二進制
	 * */
	@PostMapping("/submitInfo01")
	public String submitInfo(@RequestParam("id") String id,
			@RequestParam("name") String name,
			@RequestParam("file") MultipartFile file) {
   
   
		
		System.out.println("id: " + id);
		System.out.println("name: " + name);
		System.out.println("fileName: " + file != null ? file.getOriginalFilename() : "null");
		
		if (!file.isEmpty()) {
   
   
			String fileName = file.getOriginalFilename();
			try {
   
   
				file.transferTo(new File(baseDir, fileName));
				return "success";
			} catch (IllegalStateException e) {
   
   
				e.printStackTrace();
			} catch (IOException e) {
   
   
				e.printStackTrace();
			}
		} 
		return "Fail";
	}
	
	/**
	 *    表單數據,含文本和二進制 進一步封裝!
	 * */
	@PostMapping("/submitInfo02")
	public String submitInfo02(User user) {
   
   
		
		MultipartFile file = user.getFile();
		System.out.println("id: " + user.getId());
		System.out.println("name: " + user.getName());
		System.out.println("fileName: " + user != null ? file.getOriginalFilename() : "null");
		
		if (!file.isEmpty()) {
   
   
			String fileName = file.getOriginalFilename();
			try {
   
   
				file.transferTo(new File(baseDir, fileName));
				return "success";
			} catch (IllegalStateException e) {
   
   
				e.printStackTrace();
			} catch (IOException e) {
   
   
				e.printStackTrace();
			}
		} 
		return "Fail";
	}
	
	/**
	 * POST以二進制形式傳遞文件,通常的web表單是做不到的,但是ajax2.0以後是支持的,我們來嘗試一下。
	 * 注意它和 Multipart的區別,Multipart實際上不只包含文件本身的數據,還有文件的其它的信息,例如剛纔獲取的文件名。
	 * 但是如果以二進制的形式傳遞,它就是完全的文件數據流,不包含任何其它信息,只有文件本身的二進制數據流。
	 * 
	 * 使用這種形式,只能傳輸單個文件,無法傳輸多個文件,因爲它只是文件本身的二進制數據,如果是多個的話,
	 * 那麼誰也別想從一個連續的二進制流中把圖片切分出來了。
	 * */
	@PostMapping("/binaryFile")
	public String binaryFile(@RequestBody byte[] fileData) {
   
   
		try {
   
   
			Files.write(Paths.get(baseDir, UUID.randomUUID().toString() + ".jpg"), fileData);
			return "success";
		} catch (IOException e) {
   
   
			e.printStackTrace();
			return e.getMessage();
		}
	}
	
	
	@GetMapping("/not_use_like_this")
	public Comment not_use_like_this(@RequestBody Comment comment) {
   
   
		System.out.println(comment);
		return comment;
	}
}

總結

通過列舉多種參數傳遞的方式,並且實際使用Postman和Fiddler測試,我覺得自己對於大部分的參數傳遞方式都已經很熟練了,這確實是一種很好的學習方式。我本身是屬於視覺學習型的人,所以對我來說學習一樣東西,親眼所見的效果是最好的。大家也要多探索屬於自己的學習方式。

這裏只是一些具體的案例,但是關於@RequestPram和@RequestBody的實際作用,這裏我還不是太瞭解,還需要以後多學習,這裏貼一段它本身的API說明。

In Spring MVC, “request parameters” map to query parameters, form data,
and parts in multipart requests. This is because the Servlet API combines
query parameters and form data into a single map called “parameters”, and
that includes automatic parsing of the request body.


1.query paramters
2.from data
3.parts in multipart

這樣看來 @RequestParam 同時適用於GET和POST方法,但是更加準確的解釋應該是它
可以處理在請求行和請求體中的參數(上面三種情況)。

注意:雖然它適用範圍很廣,但是也不是說可以隨便使用的,也是有限制條件的。

PS:
1. 上面列舉的情況還是不夠,如果有漏網之魚,可以在評論區中提出來,我再增補上去。
2. 大家可以數一下,我總共列舉了多少種參數傳遞的情況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章