通过一个简单的Spring Boot应用程序,我试图理解Spring Boot注释@RequestParam和@RequestBody之间的区别以及它们各自的用法。Spring文档将@RequestParam定义为“指示方法参数应绑定到web请求参数的注释”,将@RequestBody定义为“指示方法参数应绑定到web请求主体的注释”。因此,可以公平地假设注释和相关方法都可以用作相同的备选方案(类似结果的不同方法),以将参数传递到后端应用程序,例如在用户数据库中插入值“email”、“username”和“password”(只是为了简化必要的密码加密而忘记)。
因此,可以预期下面的代码会被截断
@PostMapping
public User save(@RequestBody User user) {
return userService.createUser(user);
}等同于
@PostMapping(value = "/users")
public ResponseEntity<User> createUser(
@RequestParam String email,
@RequestParam String username,
@RequestParam String password
) {
try {
User user = new User();
user.setEmail(email);
user.setUsername(username);
user.setPassword(password);
userService.createUser(user);
return ResponseEntity.noContent().build();
} catch (Exception exception) {
return new ResponseEntity<>(HttpStatus.I_AM_A_TEAPOT);
}
}尝试将@RequestBody替换为Postman会得到一个200OK响应:

但尝试@RequestParam替代方法会导致400“错误请求”错误:

为什么这个@RequestParam方法会触发一个错误,而显然等效的@RequestBody方法却如预期的那样工作?
为了完整起见,下面给出了用户模型:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
private String username;
private String password;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}存储库如下所示:
public interface UserRepository extends JpaRepository<User, Long> {}用户服务接口如下所示:
public interface UserService {
List<User> getAllUsers();
User getUserById(Long id);
User createUser(User user);
void deleteUser(Long id);
}用户服务如下所示:
@Service
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
@Autowired
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
@Override
public User createUser(User user) {
return userRepository.save(user);
}
@Override
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
@Override
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
}发布于 2021-07-06 19:23:10
这两种方法在功能上是等效的,如果不是在预期用途上的话。
您不应该在参数值两边使用引号。如果你删除了",那么它应该可以工作,至少在一个新的Spring Boot项目上是这样的,我把你的代码复制到了一个最新版本的Postman上。
Postman在“设置”标签上也有一个选项,可以自动对url进行编码。如果启用该选项,则将对引号进行编码并将其作为参数值的一部分。它在默认情况下是启用的,但在您的屏幕截图上有一个绿点,表示您更改了某些内容。我猜是你设置的一个设置导致了错误的请求(可能是禁用了url的编码)。
当我禁用编码时,我在日志中得到这个错误:
java.lang.IllegalArgumentException:
Invalid character found in the request target
[/users?email=bla&username=hello&password="bla" ].
The valid characters are defined in RFC 7230 and RFC 3986发布于 2021-07-07 18:30:19
首先,感谢您的支持和您花时间测试代码!@requestParam和@requestBody确实都可以用作两种将值传递到后端的替代方法,正如前面的答案(谢谢!)所断言的那样。仍然需要一个解释,为什么使用我的问题中提供的代码不是这种情况,因为按照指示修改postman中的请求没有帮助,使用cUrl发布请求会导致完全相同的405错误。因此,必须在代码中找到解决方案。我花了一段时间才找到它,但是在我的控制器类上面有一个@RequestMapping(value="/users")注释,它必须被禁用。注意,显然,@RequestMapping的错误使用只会影响@RequestParam,而不会影响@RequestBody。下面的代码现在可以工作了(这里省略了@GetMapping和@deleteMapping ),并给出了与问题中给出的@RequestBody完全相同的结果。
@RestController
//@RequestMapping(value="/users")
@CrossOrigin
public class UserController {
@Autowired
private UserServiceImpl userService;
@PostMapping(value = "/users")
public ResponseEntity<Object> createUser(
@RequestParam(name = "email") String email,
@RequestParam(name = "username") String username,
@RequestParam(name = "password") String password
) {
try {
User user = new User();
user.setEmail(email);
user.setUsername(username);
user.setPassword(password);
userService.createUser(user);
return ResponseEntity.noContent().build();
} catch (Exception exception) {
return new ResponseEntity<>(HttpStatus.I_AM_A_TEAPOT);
}
}发布于 2021-07-06 18:51:54
在url中为@RequestParam传递请求参数值的方式不正确。对于多个参数,可以传递如下所示的值:
http://<url>:<port>/user/101?param1=10¶m2=20并参考以下答案:
https://stackoverflow.com/questions/68268962
复制相似问题