欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringBoot前后端分离参数传递方式总结

程序员文章站 2022-06-28 18:50:50
因为总是需要使用不同的参数传递方式,所以特地来总结一下SpringBoot中常用的传递参数的方式。SpringBoot参数传递注意:虽然Restful风格很流行,但是大部分还是主要是GET和POST的内容,所以这里只是列举GET和POST请求为例。GET请求方式01.单个键值对参数/** * GET 方式传递参数 单个参数 * */@GetMapping("/get01")public String get01(String comment) {return comment == n...

前言: 因为总是需要使用不同的参数传递方式,所以特地来总结一下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;
}

请求不带参数
SpringBoot前后端分离参数传递方式总结

请求报文中也没有数据,响应报文体中有数据
SpringBoot前后端分离参数传递方式总结

请求带参数
SpringBoot前后端分离参数传递方式总结

请求携带数据,数据在请求行中,注意数据被编码了
SpringBoot前后端分离参数传递方式总结

使用了@RequestParam注解,请求必须携带参数,否则就会报错,否则就是:错误码400 Bad Request

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

请求不携带参数,请求错误 400 Bad Reqeust
SpringBoot前后端分离参数传递方式总结

请求携带参数,接收成功
SpringBoot前后端分离参数传递方式总结
请求和响应报文
SpringBoot前后端分离参数传递方式总结

如果参数不添加 @RequestParam 注解,那么这个参数即可不传递,而使用了注解的话,默认是必须传递参数的,当然了也可以配置为false。但是,我倾向于还是显示使用注解,这样比较清晰,也可配置,更加灵活。

SpringBoot前后端分离参数传递方式总结

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;
}

请求行携带多个参数
SpringBoot前后端分离参数传递方式总结

请求和响应报文
SpringBoot前后端分离参数传递方式总结

03.键值对映射对象

/**
 *  使用对象对参数进行封装,这样在多个参数时,优势很明显。
 *  但是这里无法使用 @RequestParam注解,否则会出错。
 * */
@GetMapping("/get04")
public Comment get04(Comment comment) {
	if (Objects.isNull(comment)) {
		return null;  // 需要对 null 值进行处理
	}
	System.out.println(comment);
	return comment;
}

请求携带多个参数,直接映射成对象,但是这里无法使用@RequestParam,同时也无法使用@RequestBody,因为参数不在请求体中,至于为什么无法使用,暂时还没明白!
SpringBoot前后端分离参数传递方式总结

请求和响应报文
SpringBoot前后端分离参数传递方式总结

因为没有使用注解,可以不携带参数
SpringBoot前后端分离参数传递方式总结

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;
}

SpringBoot前后端分离参数传递方式总结

多个键值对参数
SpringBoot前后端分离参数传递方式总结

请求和响应报文
SpringBoot前后端分离参数传递方式总结

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;
}

请求直接写在路径上,成为路径的一部分
SpringBoot前后端分离参数传递方式总结

请求和响应体

注: 请求参数就在路径上面。
SpringBoot前后端分离参数传递方式总结
SpringBoot前后端分离参数传递方式总结

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();
	}
}

响应体中含有图片的数据
SpringBoot前后端分离参数传递方式总结

请求体报文
SpringBoot前后端分离参数传递方式总结

响应报文,注意图片的二进制数据无法解码,会显示出乱码的效果
SpringBoot前后端分离参数传递方式总结

使用ImageView的方式查看图片
SpringBoot前后端分离参数传递方式总结

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类型
SpringBoot前后端分离参数传递方式总结

请求参数以键值对的形式放在请求体中,注意它也是被编码的
SpringBoot前后端分离参数传递方式总结

请求体中携带键值对参数,注意Content-Type类型为form-data

SpringBoot前后端分离参数传递方式总结

请求体中的数据以表单数据的形式存放,注意其形式
SpringBoot前后端分离参数传递方式总结

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
SpringBoot前后端分离参数传递方式总结

SpringBoot前后端分离参数传递方式总结

Content-Type选择:x-www-form-urlencoded

SpringBoot前后端分离参数传递方式总结

SpringBoot前后端分离参数传递方式总结

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都会改变请求参数,通过上面的对比都能看出来了。

SpringBoot前后端分离参数传递方式总结

请求体中的数据就是原始的传递数据,并不会改变
SpringBoot前后端分离参数传递方式总结

04.json数组映射对象数组

/**
 * 传递对象数组
 * */
@PostMapping("/post04")
public Comment[] post04(@RequestBody Comment[] comments) {
	return comments;
}

请求参数为一个json数组,这个东西以前可是难到我了,我去网上找了一个直接映射成List的写法,但是后来遇到这个问题我一想,既然单个json是一个对象,那么json数组,不就是一个对象数组吗?试了一下,果然是这样的!
SpringBoot前后端分离参数传递方式总结
SpringBoot前后端分离参数传递方式总结

05.json数组映射List

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

请求参数直接映射成List
SpringBoot前后端分离参数传递方式总结
SpringBoot前后端分离参数传递方式总结

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中进行了改进,现在框架又进行了进一步的封装,使用起来更加方便了。
SpringBoot前后端分离参数传递方式总结
SpringBoot前后端分离参数传递方式总结

SpringBoot前后端分离参数传递方式总结

请求报文
SpringBoot前后端分离参数传递方式总结
SpringBoot前后端分离参数传递方式总结

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来隔开不同的数据。
SpringBoot前后端分离参数传递方式总结
SpringBoot前后端分离参数传递方式总结
SpringBoot前后端分离参数传递方式总结

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";
}

SpringBoot前后端分离参数传递方式总结

SpringBoot前后端分离参数传递方式总结

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();
	}
}

SpringBoot前后端分离参数传递方式总结

SpringBoot前后端分离参数传递方式总结
SpringBoot前后端分离参数传递方式总结

baseDir路径下的文件

SpringBoot前后端分离参数传递方式总结

增补拾遗

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

注:无法接收参数
SpringBoot前后端分离参数传递方式总结

SpringBoot前后端分离参数传递方式总结

Content-Type: form-data
注:可以接收数据。
SpringBoot前后端分离参数传递方式总结
SpringBoot前后端分离参数传递方式总结

/**
  * 使用对象封装参数要求必须具有一个对象,所以可以使用 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;
}

SpringBoot前后端分离参数传递方式总结
SpringBoot前后端分离参数传递方式总结

SpringBoot前后端分离参数传递方式总结

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;
}

SpringBoot前后端分离参数传递方式总结

SpringBoot前后端分离参数传递方式总结

全部代码

目录结构
SpringBoot前后端分离参数传递方式总结

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. 大家可以数一下,我总共列举了多少种参数传递的情况。

本文地址:https://blog.csdn.net/qq_40734247/article/details/110151421