《Pro Spring Boot 2》第四章:Web Applications with Spring Boot

《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第1张图片

 

 The Spring MVC is designed around the org.springframework.web.servlet. DispatcherServlet class. This servlet is very flexible and has a very robust functionality that you won’t find in any other MVC web framework out there. With the DispatcherServlet, you have several out-of-the-box resolutions strategies, including view resolvers, locale resolvers, theme resolvers, and exception handlers. In other words, the DispatcherServlet take a HTTP request and redirect it to the right handler (the class marked with the @Controller or @RestController and the methods that use the @RequestMapping annotations) and the right view (your JSPs).

《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第2张图片

 

 

《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第3张图片

 

 《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第4张图片

 

 《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第5张图片

 

 《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第6张图片

 

 

《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第7张图片

《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第8张图片

 

 

package com.apress.todo.domain;


import lombok.Data;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.UUID;

@Data
public class ToDo {

    @NotNull
    private String id;
    @NotNull
    @NotBlank
    private String description;
    private LocalDateTime created;
    private LocalDateTime modified;
    private boolean completed;

    public ToDo(){
        LocalDateTime date = LocalDateTime.now();
        this.id = UUID.randomUUID().toString();
        this.created = date;
        this.modified = date;
    }

    public ToDo(String description){
        this();
        this.description = description;
    }
}

shows you the ToDo class, which has all the required fields. It also uses the @Data annotation, which is a Lombok annotation that generates a default constructor (if you don’t have one) and all the setters, getters, and overrides, such as the toString method, to make the class cleaner. Also note that the class has the @NotNull and @NotBlank annotations in some of the fields; these annotations are used in the validation that we do later on. The default constructor has field initialization, so it is easy to create a ToDo instance

 《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第9张图片

 

 《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第10张图片

 

 

package com.apress.todo.repository;

import com.apress.todo.domain.ToDo;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

@Repository
public class ToDoRepository implements CommonRepository {

    private Map toDos = new HashMap<>();


    @Override
    public ToDo save(ToDo domain) {
        ToDo result = toDos.get(domain.getId());
        if(result != null) {
            result.setModified(LocalDateTime.now());
            result.setDescription(domain.getDescription());
            result.setCompleted(domain.isCompleted());
            domain = result;
        }
        toDos.put(domain.getId(), domain);
        return toDos.get(domain.getId());

    }

    @Override
    public Iterable save(Collection domains) {
        domains.forEach(this::save);
        return findAll();
    }

    @Override
    public void delete(ToDo domain) {
        toDos.remove(domain.getId());
    }

    @Override
    public ToDo findById(String id) {
        return toDos.get(id);
    }

    @Override
    public Iterable findAll() {
        return toDos.entrySet().stream().sorted(entryComparator).map(Map.Entry::getValue).collect(Collectors.toList());
    }

    private Comparator> entryComparator = (Map.Entry o1, Map.Entry o2) -> {
        return o1.getValue().getCreated().compareTo(o2.getValue().getCreated());
    };
}

《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第11张图片

 

 

package com.apress.todo.validation;

import com.fasterxml.jackson.annotation.JsonInclude;

import java.util.ArrayList;
import java.util.List;

public class ToDoValidationError {

    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private List errors = new ArrayList<>();

    private final String errorMessage;

    public ToDoValidationError(String errorMessage) {
        this.errorMessage = errorMessage;
    }

    public void addValidationError(String error) {
        errors.add(error);
    }

    public List getErrors() {
        return errors;
    }

    public String getErrorMessage() {
        return errorMessage;
    }
}

 

 

 

 

package com.apress.todo.validation;

import org.springframework.validation.Errors;
import org.springframework.validation.ObjectError;

public class ToDoValidationErrorBuilder {

    public static ToDoValidationError fromBindingErrors(Errors errors) {
        ToDoValidationError error = new ToDoValidationError("Validation failed. " + errors.getErrorCount() + " error(s)");
        for (ObjectError objectError : errors.getAllErrors()) {
            error.addValidationError(objectError.getDefaultMessage());
        }
        return error;
    }
}

《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第12张图片

 

 

package com.apress.todo.controller;

import com.apress.todo.domain.ToDo;
import com.apress.todo.domain.ToDoBuilder;
import com.apress.todo.repository.CommonRepository;
import com.apress.todo.validation.ToDoValidationError;
import com.apress.todo.validation.ToDoValidationErrorBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import javax.validation.Valid;
import java.net.URI;

@RestController
@RequestMapping("/api")
public class ToDoController {

    private CommonRepository repository;

    @Autowired
    public ToDoController(CommonRepository repository) {
        this.repository = repository;
    }

    @GetMapping("/todo") //@RequestMapping(value="/todo", method = {RequestMethod.GET})
    public ResponseEntity> getToDos(){
        return ResponseEntity.ok(repository.findAll());
    }

    @GetMapping("/todo/{id}")
    public ResponseEntity getToDoById(@PathVariable String id){
        return ResponseEntity.ok(repository.findById(id));
    }

    @PatchMapping("/todo/{id}")
    public ResponseEntity setCompleted(@PathVariable String id){
        ToDo result = repository.findById(id);
        result.setCompleted(true);
        repository.save(result);
        URI location = ServletUriComponentsBuilder.fromCurrentRequest()
                .buildAndExpand(result.getId()).toUri();

        return ResponseEntity.ok().header("Location",location.toString()).build();
    }

    @RequestMapping(value="/todo", method = {RequestMethod.POST,RequestMethod.PUT})
    public ResponseEntity createToDo(@Valid @RequestBody ToDo toDo, Errors errors){
        if (errors.hasErrors()) {
            return ResponseEntity.badRequest().body(ToDoValidationErrorBuilder.fromBindingErrors(errors));
        }

        ToDo result = repository.save(toDo);
        URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
                .buildAndExpand(result.getId()).toUri();
        return ResponseEntity.created(location).build();
    }

    @DeleteMapping("/todo/{id}")
    public ResponseEntity deleteToDo(@PathVariable String id){
        repository.delete(ToDoBuilder.create().withId(id).build());
        return ResponseEntity.noContent().build();
    }

    @DeleteMapping("/todo")
    public ResponseEntity deleteToDo(@RequestBody ToDo toDo){
        repository.delete(toDo);
        return ResponseEntity.noContent().build();
    }

    @ExceptionHandler
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public ToDoValidationError handleException(Exception exception) {
        return new ToDoValidationError(exception.getMessage());
    }

}

 《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第13张图片

 

 《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第14张图片

 

 《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第15张图片

 


 

 《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第16张图片

 

 《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第17张图片

 

 《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第18张图片

 

 《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第19张图片

 

 


《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第20张图片

 

 《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第21张图片

 

 

 

 

package com.apress.todo.client.domain;

import lombok.Data;

import java.time.LocalDateTime;
import java.util.UUID;

@Data
public class ToDo {

    private String id;
    private String description;
    private LocalDateTime created;
    private LocalDateTime modified;
    private boolean completed;


    public ToDo(){
        LocalDateTime date = LocalDateTime.now();
        this.id = UUID.randomUUID().toString();
        this.created = date;
        this.modified = date;
    }

    public ToDo(String description){
        this();
        this.description = description;
    }
}

 

 

package com.apress.todo.client.error;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.DefaultResponseErrorHandler;

import java.io.IOException;
import java.nio.charset.Charset;

public class ToDoErrorHandler extends DefaultResponseErrorHandler {

    private Logger log = LoggerFactory.getLogger(ToDoErrorHandler.class);

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        log.error(response.getStatusCode().toString());
        log.error(StreamUtils.copyToString(response.getBody(),Charset.defaultCharset()));
    }

}

 

 《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第22张图片

 

 

package com.apress.todo.client;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix="todo")
@Data
public class ToDoRestClientProperties {

    private String url;
    private String basePath;

}

《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第23张图片

 

 

package com.apress.todo.client;

import com.apress.todo.client.domain.ToDo;
import com.apress.todo.client.error.ToDoErrorHandler;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

@Service
public class ToDoRestClient {

    private RestTemplate restTemplate;
    private ToDoRestClientProperties properties;

    public ToDoRestClient(ToDoRestClientProperties properties){
        this.restTemplate = new RestTemplate();
        this.restTemplate.setErrorHandler(new ToDoErrorHandler());
        this.properties = properties;
    }

    public Iterable findAll() throws URISyntaxException {
        RequestEntity> requestEntity = new RequestEntity>(HttpMethod.GET,new URI(properties.getUrl() + properties.getBasePath()));
        ResponseEntity> response =
                restTemplate.exchange(requestEntity,new ParameterizedTypeReference>(){});

        if(response.getStatusCode() == HttpStatus.OK){
            return response.getBody();
        }

        return null;
    }

    public ToDo findById(String id){
        Map params = new HashMap();
        params.put("id", id);
        return restTemplate.getForObject(properties.getUrl() + properties.getBasePath() + "/{id}",ToDo.class,params);
    }

    public ToDo upsert(ToDo toDo) throws URISyntaxException {
       RequestEntity requestEntity = new RequestEntity<>(toDo,HttpMethod.POST,new URI(properties.getUrl() + properties.getBasePath()));
       ResponseEntity response = restTemplate.exchange(requestEntity, new ParameterizedTypeReference() {});

       if(response.getStatusCode() == HttpStatus.CREATED){
           return restTemplate.getForObject(response.getHeaders().getLocation(),ToDo.class);
       }
       return null;
    }

    public ToDo setCompleted(String id) throws URISyntaxException{
        Map params = new HashMap();
        params.put("id", id);
        restTemplate.postForObject(properties.getUrl() + properties.getBasePath() + "/{id}?_method=patch",null, ResponseEntity.class, params);

        return findById(id);
    }

    public void delete(String id){
        Map params = new HashMap();
        params.put("id", id);
        restTemplate.delete(properties.getUrl() + properties.getBasePath() + "/{id}",params);
    }

}

 《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第24张图片

 

 

 

 

package com.apress.todo;

import com.apress.todo.client.ToDoRestClient;
import com.apress.todo.client.domain.ToDo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class TodoClientApplication {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(TodoClientApplication.class);
        app.setWebApplicationType(WebApplicationType.NONE);
        app.run(args);
    }

    private Logger log = LoggerFactory.getLogger(TodoClientApplication.class);

    @Bean
    public CommandLineRunner process(ToDoRestClient client){
        return args -> {

            Iterable toDos = client.findAll();
            assert toDos != null;
            toDos.forEach( toDo -> log.info(toDo.toString()));

            ToDo newToDo = client.upsert(new ToDo("Drink plenty of Water daily!"));
            assert newToDo != null;
            log.info(newToDo.toString());

            ToDo toDo = client.findById(newToDo.getId());
            assert toDos != null;
            log.info(toDo.toString());

            ToDo completed = client.setCompleted(newToDo.getId());
            assert completed.isCompleted();
            log.info(completed.toString());

            client.delete(newToDo.getId());
            assert client.findById(newToDo.getId()) == null;
        };
    }
}

《Pro Spring Boot 2》第四章:Web Applications with Spring Boot_第25张图片

 

你可能感兴趣的:(《Pro Spring Boot 2》第四章:Web Applications with Spring Boot)