大家好,欢迎来到IT知识分享网。
上篇学习了《30-Spring MVC与Restful服务》原理,本篇我们学习Spring MVC与RESTful服务实战。
下面使用Spring MVC开发一个标准的RESTful服务的示例。我们的资源是一个叫Person的领域模型。
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(exclude = {"name", "age"}) //两个对象equals比较,此处id相同即equal
@ToString
public class Person {
private Long id;
private String name;
private Integer age;
}
控制器声明:
@RestController //组合了@Controller和@ResponseBody,类中所有的方法的返回值都是通过返回体了。
@RequestMapping("/people") //资源访问的路径为"/people"
public class PeopleController {
private PersonRepository personRepository; // 参照本节最后部分“数据操作代码”
public PeopleController(PersonRepository personRepository) { //注入数据操作的Bean
this.personRepository = personRepository;
}
}
2.2.1 新增资源
@PostMapping
@ResponseStatus(HttpStatus.CREATED) // 指定状态为资源已创建201,不指定皆为HttpStatus.OK:200
public Person save(@RequestBody Person person){ //从请求体获取数据
return personRepository.save(person); //返回的数据写入返回体
}
我们使用了@RequestBody和@ResponseBody组合,我们下面使用RequestEntity和ResponseEntity组合,当然岔开组合也是可以的。
@PostMapping
public ResponseEntity<Person> save(RequestEntity<Person> personRequestEntity){
Person p = personRepository.save(personRequestEntity.getBody());//使用RequestEntity来获得请求体信息
return new ResponseEntity<Person>(p, HttpStatus.CREATED); //使用ResponseEntity设置返回信息
}
2.2.2 获取指定的资源
@GetMapping("/{id}")public Person getOne(@PathVariable Long id){ //使用@PathVariable获取路径中的{id}的值 return personRepository.findOne(id);}
2.2.3 替换更新资源
@PutMapping("/{id}")public Person replace(@PathVariable Long id, @RequestBody Person person){ return personRepository.replace(id, person); //整个对象替换更新}
2.2.4 更新资源
@PatchMapping("/{id}")public Person patch(@PathVariable Long id, @RequestBody Person person) { return personRepository.patched(id, person); //只更新修改的部分}
2.2.5 查找资源
@GetMapping("/findByName")public Person findByName(@RequestParam String name){ //通过@RequestParam获取?后的参数内容 return personRepository.findByName(name);}
2.2.6 删除资源
@DeleteMapping("/{id}")@ResponseStatus(HttpStatus.NO_CONTENT) //状态修改成无内容:204public void delete(@PathVariable Long id){ personRepository.delete(id);}
2.2.7 获取请求头
@GetMapping("/getRequestHeaders")public String[] getHeaders(@RequestHeader HttpHeaders httpHeaders, //1 @RequestHeader Map<String, String> headerMap, //1 @RequestHeader MultiValueMap<String, String> multiValueMap, //1 @RequestHeader("accept") String accept, //2 RequestEntity requestEntity) { //3 String userAgent = httpHeaders.getFirst("user-agent"); String host = headerMap.get("host"); String cacheControl = multiValueMap.getFirst("cache-control"); HttpHeaders sameHttpHeaders = requestEntity.getHeaders(); return new String[]{userAgent, host, cacheControl, accept};}
- 可以使用@RequestHeader注解在HttpHeaders、Map<String, String>、MultiValueMap<String, String>上来获取请求头信息;
- 通过@RequestHeader设置头的key,获得指定的请求头的值;
- 我们同样可以在方法参数里注入RequestEntity对象,从而获得请求头的信息。
2.2.8 ResponseEntity
ResponseEntity可以定制整个返回,包含status、header和body:
@GetMapping("/responseEntityShowcase")
public ResponseEntity<Person> responseEntityShowcase(){
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("my-header", "hello header");
return new ResponseEntity<Person>(new Person(1234l,"bar", 22),
responseHeaders,
HttpStatus.OK);
}
我们可以用ResponseEntity来构造来设置body、header和status;ResponseEntity还为我们提供了建造者模式的Fluent API,下面的代码是等同的:
return ResponseEntity.ok() //中间操作,设置status
.header("my-header", "hello header") //中间操作,设置header
.body(new Person(1234l,"bar", 22)); //终结操作,设置body
更多关于ResponseEntity提供的方法,可参考ResponseEntity的API。
2.2.9 获取Cookie信息
我们可以使用@CookieValue来获得客户端的Cookie的值:
@GetMapping("/getCookie")
public String[] getCookie(@CookieValue("myCookie") String myCookie,
@CookieValue("anotherCookie") String anotherCookie) {
return new String[]{myCookie, anotherCookie};
}
2.2.10 方法参数
Spring MVC支持注入一些参数到方法中:
@GetMapping("/methodArguments")
public void methodArguments(WebRequest request,//1
NativeWebRequest nativeWebRequest,//2
ServletRequest servletRequest, //3
ServletResponse servletResponse,//4
HttpSession httpSession, //5
Locale locale, //6
TimeZone timeZone,//7
ZoneId zoneId, //8
DemoService demoService //9) throws Exception{
System.out.println(request);
System.out.println(nativeWebRequest);
System.out.println(servletRequest);
System.out.println(servletResponse);
System.out.println(httpSession);
System.out.println(locale);
System.out.println(timeZone);
System.out.println(zoneId);
System.out.println(demoService.sayHello());
}
- 通用Web请求接口;
- 继承WebRequest,通用的请求和返回的接口;
- Servlet请求;
- Servlet返回;
- Http会话;
- 本地信息;
- 时区信息;
- 时区id;
- 新建一个对象(非注入Bean)。
这些值都不为空,都成功地注入了。还有其它的一些参数如PushBuilder、Principle等也是可以直接注入的,我们将在对应的知识章节进行讲解。
DemoService的代码:
@Service
public class DemoService {
public String sayHello(){
return "Hello World";
}
}
2.2.11 文件上传和返回文件
在Spring MVC使用MultipartFile来接受上传的文件,同样也支持Servlet 3.0的javax.servlet.http.Part来接受文件。
@PostMapping("/upload")
public ResponseEntity<Resource> upload(MultipartFile file, //1
@RequestPart("file") MultipartFile file2, //1
@RequestParam("file") MultipartFile file3, //1
@RequestParam("file")Part part //1
) throws Exception{
System.out.println(file.equals(file2)); //2
System.out.println(file.equals(file3)); //2
System.out.println(file.getSize() == part.getSize());//2
Resource imageResource = new InputStreamResource(file.getInputStream()); //3
return ResponseEntity.ok()
.contentType(MediaType.IMAGE_PNG) //4
.body(imageResource); //5
}
- 我们可以用上面的四种形式来接受multipart/form-data的文件上传;
- 我们可以通过这几句比较出得到的是相同的文件;
- Resource是Spring对资源的抽象,它的实现可以有文件资源、网址资源、类路径资源、输入流资源等;
- 设置返回值类型MediaType.IMAGE_PNG即指定返回格式是图片PNG;
- 将图片资源作为返回体。
若需要上传多个文件,我们只需使用@RequestParam注解Map<String, MultipartFile> 或MultiValueMap<String, MultipartFile>即可:
@PostMapping("/multipleUpload")
public String uploads(@RequestParam Map<String, MultipartFile> fileMap){
StringBuilder info = new StringBuilder();
fileMap.forEach((key, file) -> {
info.append(file.getName()).append("'s length is ").append(file.getSize()).append("\n");
});
return info.toString();
}
我们可以在外部配置中使用spring.servlet.multipart.*来配置文件上传:
spring.servlet.multipart.max-file-size: 10MB # 最大文件大小为10MB
2.2.12 参数校验
Spring Boot支持基于JSR-303/349/380规范的Bean校验API,功能实现者为hibernate-validator。
JSR的含义是Java Specification Requests(Java规范请求),它是对Java新功能的请求,是JCP组织的一部分,这个组织是Java社区参与者使用自己创意来影响Java语言的发展。JSR的jar的包名一般以javax开头,如javax.validation
JSR只提供功能规范定义,不提供实现。Spring Boot的Web依赖给我添加了spring-boot-starter-validation它给我们添加了规范包:jakarta.validation-api-2.0.1.jar和实现包:hibernate-validator-6.0.16.Final.jar
JSR-303:Bean Validation API 1.0
JSR-349:Bean Validation API 1.1
JSR-380:Bean Validation API 2.0
我们将我们的Person类加上校验注解:
@NotNull(message = "id不能为空")
private Long id;
@Size(min = 3, max = 5, message = "name在3到5个字符之间")
private String name;
@Min(value = 18, message = "age不能低于18岁")
private Integer age;
JSR校验注解在javax.validation.constraints包中,Hibernate的提供的更多检验注解在org.hibernate.validator.constraints中。
使用@Valid注解在参数前即可校验该参数:
@PostMapping("/validate")
public ResponseEntity validate(@Valid @RequestBody Person person, BindingResult result){ //1
if (result.hasErrors()){ //2
return ResponseEntity.badRequest()
.body(result.getAllErrors()); //3
}
return ResponseEntity.ok()
.body("everything is fine");
}
- @Valid注解也来自JSR的注解,使用@RequestBody意味数据来源于请求体,BindingResult可直接注入作为参数获得校验的错误;
- 可以获得BindingResult是否有错误;
- 当BindingResult中有错误时,向返回体写入所有错误。
2.2.13 数据操作代码
前面例子的数据操作代码在PersonRepository类中:
@Repository
public class PersonRepository implements CommonRepository{
private static Set<Person> people = new HashSet<>();
@Override
public Person save(Person person) {
people.add(person);
return person;
}
@Override
public Person findOne(Long id) {
Person person = people.stream()
.filter(p -> p.getId().equals(id))
.findFirst()
.get();
return person;
}
@Override
public Person replace(Long id, Person person) {
Person oldPerson = people.stream()
.filter(p -> p.getId().equals(id))
.findFirst().get();
people.remove(oldPerson);
people.add(person);
return person;
}
@Override
public Person patched(Long id, Person person) {
Person patchedPerson = people.stream()
.filter(p -> p.getId().equals(id))
.map(p -> {
String[] ignoredNullPropertyNames = MyBeanUtils.ignoredNullPropertyNames(person);
BeanUtils.copyProperties(person, p, ignoredNullPropertyNames);
return p ;
})
.findFirst().get();
return patchedPerson;
}
@Override
public void delete(Long id) {
people.removeIf(p -> p.getId().equals(id));
}
@Override
public Person findByName(String name) {
Person person = people.stream()
.filter(p -> p.getName().equals(name))
.findFirst().get();
return person;
}
}
操作规范接口:
public interface CommonRepository {
Person save(Person person);
Person findOne(Long id);
Person replace(Long id, Person person);
Person patched(Long id, Person person);
void delete(Long id);
Person findByName(String name);
}
找出对象中为空的属性的工具方法:
public class MyBeanUtils {
public static String[] ignoredNullPropertyNames(Object source){
final BeanWrapper wrappedSource = new BeanWrapperImpl(source);
return Stream.of(wrappedSource.getPropertyDescriptors())
.map(FeatureDescriptor::getName)
.filter(propertyName -> wrappedSource.getPropertyValue(propertyName) == null)
.toArray(String[]::new);
}
}
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/47423.html