31-Spring MVC与RESTful服务实战

31-Spring MVC与RESTful服务实战上篇学习了《30-Spring MVC与Restful服务》原理,本篇我们学习Spring MVC与RESTful服务实战。下面使用Spring

大家好,欢迎来到IT知识分享网。

上篇学习了《30-Spring MVC与Restful服务》原理,本篇我们学习Spring MVC与RESTful服务实战。

31-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组合,我们下面使用RequestEntityResponseEntity组合,当然岔开组合也是可以的。

@PostMapping
public ResponseEntity<Person> save(RequestEntity<Person> personRequestEntity){
    Person p = personRepository.save(personRequestEntity.getBody());//使用RequestEntity来获得请求体信息
    return new ResponseEntity<Person>(p, HttpStatus.CREATED); //使用ResponseEntity设置返回信息
}
31-Spring MVC与RESTful服务实战

2.2.2 获取指定的资源

@GetMapping("/{id}")public Person getOne(@PathVariable Long id){ //使用@PathVariable获取路径中的{id}的值    return personRepository.findOne(id);}
31-Spring MVC与RESTful服务实战

2.2.3 替换更新资源

@PutMapping("/{id}")public Person replace(@PathVariable Long id, @RequestBody Person person){    return personRepository.replace(id, person); //整个对象替换更新}
31-Spring MVC与RESTful服务实战

2.2.4 更新资源

@PatchMapping("/{id}")public Person patch(@PathVariable Long id, @RequestBody Person person) {    return personRepository.patched(id, person); //只更新修改的部分}
31-Spring MVC与RESTful服务实战

2.2.5 查找资源

@GetMapping("/findByName")public Person findByName(@RequestParam String name){ //通过@RequestParam获取?后的参数内容    return personRepository.findByName(name);}
31-Spring MVC与RESTful服务实战

2.2.6 删除资源

@DeleteMapping("/{id}")@ResponseStatus(HttpStatus.NO_CONTENT) //状态修改成无内容:204public void delete(@PathVariable Long id){    personRepository.delete(id);}
31-Spring MVC与RESTful服务实战

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};}
  1. 可以使用@RequestHeader注解在HttpHeadersMap<String, String>MultiValueMap<String, String>上来获取请求头信息;
  2. 通过@RequestHeader设置头的key,获得指定的请求头的值;
  3. 我们同样可以在方法参数里注入RequestEntity对象,从而获得请求头的信息。
31-Spring MVC与RESTful服务实战

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
31-Spring MVC与RESTful服务实战


更多关于
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};
}
31-Spring MVC与RESTful服务实战

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());
}
31-Spring MVC与RESTful服务实战

  1. 通用Web请求接口;
  2. 继承WebRequest,通用的请求和返回的接口;
  3. Servlet请求;
  4. Servlet返回;
  5. Http会话;
  6. 本地信息;
  7. 时区信息;
  8. 时区id;
  9. 新建一个对象(非注入Bean)。

这些值都不为空,都成功地注入了。还有其它的一些参数如PushBuilderPrinciple等也是可以直接注入的,我们将在对应的知识章节进行讲解。

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

}
  1. 我们可以用上面的四种形式来接受multipart/form-data的文件上传;
  2. 我们可以通过这几句比较出得到的是相同的文件;
  3. Resource是Spring对资源的抽象,它的实现可以有文件资源、网址资源、类路径资源、输入流资源等;
  4. 设置返回值类型MediaType.IMAGE_PNG即指定返回格式是图片PNG;
  5. 将图片资源作为返回体。
31-Spring MVC与RESTful服务实战

若需要上传多个文件,我们只需使用@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();
}
31-Spring MVC与RESTful服务实战


我们可以在外部配置中使用
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");
}
  1. @Valid注解也来自JSR的注解,使用@RequestBody意味数据来源于请求体,BindingResult可直接注入作为参数获得校验的错误;
  2. 可以获得BindingResult是否有错误;
  3. BindingResult中有错误时,向返回体写入所有错误。
31-Spring MVC与RESTful服务实战

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

}
31-Spring MVC与RESTful服务实战

31-Spring MVC与RESTful服务实战

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/47423.html

(0)
上一篇 2024-04-20 09:26
下一篇 2024-04-23 12:15

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信