Skip to content

Instantly share code, notes, and snippets.

@aveuiller
Last active January 20, 2021 22:31
Show Gist options
  • Save aveuiller/dbcc48be20afd4469b3d28616cdffccd to your computer and use it in GitHub Desktop.
Save aveuiller/dbcc48be20afd4469b3d28616cdffccd to your computer and use it in GitHub Desktop.
medium_Spring_Boot_Apprentice_Cookbook
curl -i "localhost:8080/hello/jack?amount_exclamation=4"
HTTP/1.1 200
{"message":"Hello jack!!!!"}
# -d automatically creates a POST request.
$ curl -i "localhost:8080/hello" -d '{"name": "Bob"}' -H "Content-Type: application/json"
HTTP/1.1 200
{"message":"Hello Bob"}
@RestController
@RequestMapping(value = "/hello",
consumes = MediaType.ALL_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public class HelloWorldController {
// The behavior is not representative of a typical POST request
// and only here as a matter of example.
@RequestMapping(value = "", method = RequestMethod.POST)
public ResponseEntity<Map<String, String>> greetFromBody(@RequestBody HelloBody helloBody) {
HashMap<String, String> output = new HashMap<>();
output.put("message", "Hello " + helloBody.getName());
return new ResponseEntity<>(output, HttpStatus.OK);
}
@RequestMapping(value = "/{name}", method = RequestMethod.GET)
public ResponseEntity<Map<String, String>> greet(@PathVariable String name,
@RequestParam(required = false,
defaultValue = "0") int amount_exclamation) {
HashMap<String, String> output = new HashMap<>();
StringBuilder b = new StringBuilder("Hello ");
b.append(name);
for (int i = 0; i < amount_exclamation; i++) {
b.append("!");
}
output.put("message", b.toString());
return new ResponseEntity<>(output, HttpStatus.OK);
}
}
class HelloBody {
String name;
public HelloBody() {
// Used by Jackson
}
public String getName() {
return this.name;
}
}
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
spring.datasource.username=user
spring.datasource.password=password
spring.datasource.generate-unique-name=true
# Automatically create & update the database schema from code.
spring.jpa.hibernate.ddl-auto=update
#spring.datasource.url=jdbc:h2:mem:database_name
spring.datasource.url=jdbc:postgresql://localhost:5432/database_name
#spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.driver-class-name=org.postgresql.Driver
#spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL10Dialect
// Only use lowercase on docker image name
tasks.named("bootBuildImage") {
imageName = "${rootProject.name.toLowerCase()}:${version}"
}
@SpringBootTest
@AutoConfigureMockMvc
class DeliveryControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
DeliveryRepository deliveryRepository;
@Test
void testPostDeliveryOk() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Map<String, String> delivery = getValidDelivery();
String body = mapper.writeValueAsString(delivery);
MockHttpServletRequestBuilder accept =
MockMvcRequestBuilders.post("/delivery")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(body);
mvc.perform(accept).andExpect(status().isOk());
}
@Test
void testPostPersistIssue() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Map<String, String> delivery = getValidDelivery();
String body = mapper.writeValueAsString(delivery);
Mockito.when(deliveryRepository.save(Mockito.any())).thenThrow(new RuntimeException());
MockHttpServletRequestBuilder accept =
MockMvcRequestBuilders.post("/delivery")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(body);
mvc.perform(accept).andExpect(status().is4xxClientError());
}
private Map<String, String> getValidDelivery() {
Map<String, String> delivery = new HashMap<>();
delivery.put("state", "PENDING");
delivery.put("location", "Rome");
return delivery;
}
}
@RestController
@RequestMapping(value = "/delivery",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public class DeliveryController {
private final DeliveryRepository deliveryRepository;
@Autowired
public DeliveryController(DeliveryRepository deliveryRepository) {
this.deliveryRepository = deliveryRepository;
}
@RequestMapping(value = "", method = RequestMethod.POST)
public ResponseEntity<Delivery> post(@Valid @RequestBody Delivery delivery) throws ApiException {
try {
delivery = deliveryRepository.save(delivery);
} catch (Exception e) {
throw new ApiException();
}
return new ResponseEntity<>(delivery, HttpStatus.OK);
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity<Delivery> get(@PathVariable long id) throws ApiException {
Optional<Delivery> delivery = deliveryRepository.findById(id);
if (delivery.isEmpty()) {
throw new NotFoundException();
}
return new ResponseEntity<>(delivery.get(), HttpStatus.OK);
}
}
@Entity
@Table(name = "delivery")
public class Delivery {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
long id;
@Column(nullable = false)
@NotNull
@Enumerated(EnumType.STRING)
DeliveryState state;
@Column(nullable = false)
@NotNull
String location;
public Delivery() {
// Used by Jackson2
}
public Delivery(@NotNull DeliveryState state, @NotNull String location) {
this.state = state;
this.location = location;
}
public long getId() {
return id;
}
public DeliveryState getState() {
return state;
}
public void setState(DeliveryState state) {
this.state = state;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
enum DeliveryState {
PENDING, DELIVERING, WAITING_AT_ARRIVAL, RETURNING, RETURNED, PICKED_UP;
}
@RestController
@RequestMapping(value = "/delivery",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public class DeliveryController {
private final DeliveryRepository deliveryRepository;
@Autowired
public DeliveryController(DeliveryRepository deliveryRepository) {
this.deliveryRepository = deliveryRepository;
}
@RequestMapping(value = "", method = RequestMethod.GET)
public ResponseEntity<Page<Delivery>> index(@RequestParam(required = false, defaultValue = "0") int page) {
Pageable pageable = PageRequest.of(page, 50);
return new ResponseEntity<>(deliveryRepository.findAll(pageable), HttpStatus.OK);
}
}
@Repository
public interface DeliveryRepository extends PagingAndSortingRepository<Delivery, Long> {
}
@Repository
public interface DeliveryRepository extends CrudRepository<Delivery, Long> {
}
$ curl -i "localhost:8080/exception/500"
HTTP/1.1 500
$ curl -i "localhost:8080/exception/404"
HTTP/1.1 404
$ curl -i "localhost:8080/exception/400"
HTTP/1.1 400
@ControllerAdvice
public class MyApplicationControllerAdvice extends ResponseEntityExceptionHandler {
@ExceptionHandler(ApiException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public void handleBadRequest() {
}
@ExceptionHandler(NotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public void handleNotFound() {
}
}
public class ApiException extends Exception {
}
public class NotFoundException extends ApiException {
}
$ curl "localhost:8080/delivery" -H 'Content-Type: application/json' | jq 4 ↵
{
"content": [
{
"id": 1,
"state": "PENDING",
"location": "Budapest"
}
],
"pageable": {
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"offset": 0,
"pageNumber": 0,
"pageSize": 50,
"paged": true,
"unpaged": false
},
"totalPages": 1,
"totalElements": 1,
"last": true,
"first": true,
"size": 50,
"number": 0,
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"numberOfElements": 1,
"empty": false
}
$ curl -i "localhost:8080/delivery" -H 'Content-Type: application/json' \
-X POST -d '{"state": "PENDING"}'
HTTP/1.1 400
$ curl -i "localhost:8080/delivery/1" -H 'Content-Type: application/json'
HTTP/1.1 404
$ curl -i "localhost:8080/delivery" -H 'Content-Type: application/json' \
-X POST -d '{"state": "PENDING", "location":"Budapest"}'
HTTP/1.1 200
{"id":1,"state":"PENDING","location":"Budapest"}
$ curl -i "localhost:8080/delivery/1" -H 'Content-Type: application/json' 130 ↵
HTTP/1.1 200
{"id":1,"state":"PENDING","location":"Budapest"}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// Dependencies to your used dbms
implementation 'org.postgresql:postgresql:42.2.1'
implementation 'com.h2database:h2:1.4.200'
}
@RestController
@RequestMapping(value = "/exception")
public class ExceptionController {
@RequestMapping(value = "/404", method = RequestMethod.GET)
public ResponseEntity<Map<String, String>> notFound() throws NotFoundException {
throw new NotFoundException();
}
@RequestMapping(value = "/400", method = RequestMethod.GET)
public ResponseEntity<Map<String, String>> badRequest() throws ApiException {
throw new ApiException();
}
@RequestMapping(value = "/500", method = RequestMethod.GET)
public ResponseEntity<Map<String, String>> ise() throws Exception {
throw new Exception();
}
}
plugins {
id 'org.springframework.boot' version '2.4.2'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}
@RestController
@RequestMapping(value = "/hello",
consumes = MediaType.ALL_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public class HelloWorldController {
@RequestMapping(value = "/world", method = RequestMethod.GET)
public ResponseEntity<Map<String, String>> index() {
HashMap<String, String> output = new HashMap<>();
output.put("message", "Hello World!");
return new ResponseEntity<>(output, HttpStatus.OK);
}
}
$ curl -i "localhost:8080/hello/world"
HTTP/1.1 200
{"Hello":"World"}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment