Skip to content

Commit 6dde5fc

Browse files
authored
Merge pull request eugenp#6245 from rozagerardo/geroza/BAEL-11404_Update-and-move-Build-REST-API-article
[BAEL-11404] update and move 'Build Rest API' article code
2 parents 191039f + ca90f35 commit 6dde5fc

File tree

16 files changed

+251
-55
lines changed

16 files changed

+251
-55
lines changed

spring-boot-rest/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ Module for the articles that are part of the Spring REST E-book:
22

33
1. [Bootstrap a Web Application with Spring 5](https://www.baeldung.com/bootstraping-a-web-application-with-spring-and-java-based-configuration)
44
2. [Error Handling for REST with Spring](http://www.baeldung.com/exception-handling-for-rest-with-spring)
5-
3. [REST Pagination in Spring](http://www.baeldung.com/rest-api-pagination-in-spring)
5+
3. [REST Pagination in Spring](http://www.baeldung.com/rest-api-pagination-in-spring)
6+
4. [Build a REST API with Spring and Java Config](http://www.baeldung.com/building-a-restful-web-service-with-spring-and-java-based-configuration)

spring-boot-rest/pom.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@
5151
<dependency>
5252
<groupId>net.sourceforge.htmlunit</groupId>
5353
<artifactId>htmlunit</artifactId>
54-
<version>${htmlunit.version}</version>
5554
<scope>test</scope>
5655
</dependency>
5756
</dependencies>
@@ -67,7 +66,6 @@
6766

6867
<properties>
6968
<start-class>com.baeldung.SpringBootRestApplication</start-class>
70-
<htmlunit.version>2.32</htmlunit.version>
7169
<guava.version>27.0.1-jre</guava.version>
7270
</properties>
7371
</project>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,29 @@
11
package com.baeldung.persistence;
22

33
import java.io.Serializable;
4+
import java.util.List;
45

56
import org.springframework.data.domain.Page;
67

78
public interface IOperations<T extends Serializable> {
89

10+
// read - one
11+
12+
T findOne(final long id);
13+
914
// read - all
1015

16+
List<T> findAll();
17+
1118
Page<T> findPaginated(int page, int size);
1219

1320
// write
1421

1522
T create(final T entity);
23+
24+
T update(final T entity);
25+
26+
void delete(final T entity);
27+
28+
void deleteById(final long entityId);
1629
}

spring-boot-rest/src/main/java/com/baeldung/persistence/service/common/AbstractService.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,62 @@
11
package com.baeldung.persistence.service.common;
22

33
import java.io.Serializable;
4+
import java.util.List;
45

56
import org.springframework.data.domain.Page;
67
import org.springframework.data.domain.PageRequest;
78
import org.springframework.data.repository.PagingAndSortingRepository;
89
import org.springframework.transaction.annotation.Transactional;
910

1011
import com.baeldung.persistence.IOperations;
12+
import com.google.common.collect.Lists;
1113

1214
@Transactional
1315
public abstract class AbstractService<T extends Serializable> implements IOperations<T> {
1416

17+
// read - one
18+
19+
@Override
20+
@Transactional(readOnly = true)
21+
public T findOne(final long id) {
22+
return getDao().findById(id)
23+
.get();
24+
}
25+
1526
// read - all
1627

28+
@Override
29+
@Transactional(readOnly = true)
30+
public List<T> findAll() {
31+
return Lists.newArrayList(getDao().findAll());
32+
}
33+
1734
@Override
1835
public Page<T> findPaginated(final int page, final int size) {
1936
return getDao().findAll(PageRequest.of(page, size));
2037
}
2138

2239
// write
23-
40+
2441
@Override
2542
public T create(final T entity) {
2643
return getDao().save(entity);
2744
}
45+
46+
@Override
47+
public T update(final T entity) {
48+
return getDao().save(entity);
49+
}
50+
51+
@Override
52+
public void delete(final T entity) {
53+
getDao().delete(entity);
54+
}
55+
56+
@Override
57+
public void deleteById(final long entityId) {
58+
getDao().deleteById(entityId);
59+
}
2860

2961
protected abstract PagingAndSortingRepository<T, Long> getDao();
3062

spring-boot-rest/src/main/java/com/baeldung/persistence/service/impl/FooService.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.baeldung.persistence.service.impl;
22

3+
import java.util.List;
4+
35
import org.springframework.beans.factory.annotation.Autowired;
46
import org.springframework.data.domain.Page;
57
import org.springframework.data.domain.Pageable;
@@ -11,6 +13,7 @@
1113
import com.baeldung.persistence.model.Foo;
1214
import com.baeldung.persistence.service.IFooService;
1315
import com.baeldung.persistence.service.common.AbstractService;
16+
import com.google.common.collect.Lists;
1417

1518
@Service
1619
@Transactional
@@ -36,5 +39,13 @@ protected PagingAndSortingRepository<Foo, Long> getDao() {
3639
public Page<Foo> findPaginated(Pageable pageable) {
3740
return dao.findAll(pageable);
3841
}
42+
43+
// overridden to be secured
44+
45+
@Override
46+
@Transactional(readOnly = true)
47+
public List<Foo> findAll() {
48+
return Lists.newArrayList(getDao().findAll());
49+
}
3950

4051
}

spring-boot-rest/src/main/java/com/baeldung/web/controller/FooController.java

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,28 @@
99
import org.springframework.data.domain.Page;
1010
import org.springframework.data.domain.Pageable;
1111
import org.springframework.http.HttpStatus;
12-
import org.springframework.stereotype.Controller;
12+
import org.springframework.web.bind.annotation.DeleteMapping;
1313
import org.springframework.web.bind.annotation.GetMapping;
14+
import org.springframework.web.bind.annotation.PathVariable;
15+
import org.springframework.web.bind.annotation.PostMapping;
16+
import org.springframework.web.bind.annotation.PutMapping;
1417
import org.springframework.web.bind.annotation.RequestBody;
1518
import org.springframework.web.bind.annotation.RequestMapping;
16-
import org.springframework.web.bind.annotation.RequestMethod;
1719
import org.springframework.web.bind.annotation.RequestParam;
18-
import org.springframework.web.bind.annotation.ResponseBody;
1920
import org.springframework.web.bind.annotation.ResponseStatus;
21+
import org.springframework.web.bind.annotation.RestController;
2022
import org.springframework.web.util.UriComponentsBuilder;
2123

2224
import com.baeldung.persistence.model.Foo;
2325
import com.baeldung.persistence.service.IFooService;
2426
import com.baeldung.web.exception.MyResourceNotFoundException;
2527
import com.baeldung.web.hateoas.event.PaginatedResultsRetrievedEvent;
2628
import com.baeldung.web.hateoas.event.ResourceCreatedEvent;
29+
import com.baeldung.web.hateoas.event.SingleResourceRetrievedEvent;
30+
import com.baeldung.web.util.RestPreconditions;
2731
import com.google.common.base.Preconditions;
2832

29-
@Controller
33+
@RestController
3034
@RequestMapping(value = "/auth/foos")
3135
public class FooController {
3236

@@ -42,10 +46,24 @@ public FooController() {
4246

4347
// API
4448

49+
// read - one
50+
51+
@GetMapping(value = "/{id}")
52+
public Foo findById(@PathVariable("id") final Long id, final HttpServletResponse response) {
53+
final Foo resourceById = RestPreconditions.checkFound(service.findOne(id));
54+
55+
eventPublisher.publishEvent(new SingleResourceRetrievedEvent(this, response));
56+
return resourceById;
57+
}
58+
4559
// read - all
4660

47-
@RequestMapping(params = { "page", "size" }, method = RequestMethod.GET)
48-
@ResponseBody
61+
@GetMapping
62+
public List<Foo> findAll() {
63+
return service.findAll();
64+
}
65+
66+
@GetMapping(params = { "page", "size" })
4967
public List<Foo> findPaginated(@RequestParam("page") final int page, @RequestParam("size") final int size,
5068
final UriComponentsBuilder uriBuilder, final HttpServletResponse response) {
5169
final Page<Foo> resultPage = service.findPaginated(page, size);
@@ -59,7 +77,6 @@ public List<Foo> findPaginated(@RequestParam("page") final int page, @RequestPar
5977
}
6078

6179
@GetMapping("/pageable")
62-
@ResponseBody
6380
public List<Foo> findPaginatedWithPageable(Pageable pageable, final UriComponentsBuilder uriBuilder,
6481
final HttpServletResponse response) {
6582
final Page<Foo> resultPage = service.findPaginated(pageable);
@@ -74,9 +91,8 @@ public List<Foo> findPaginatedWithPageable(Pageable pageable, final UriComponent
7491

7592
// write
7693

77-
@RequestMapping(method = RequestMethod.POST)
94+
@PostMapping
7895
@ResponseStatus(HttpStatus.CREATED)
79-
@ResponseBody
8096
public Foo create(@RequestBody final Foo resource, final HttpServletResponse response) {
8197
Preconditions.checkNotNull(resource);
8298
final Foo foo = service.create(resource);
@@ -86,4 +102,18 @@ public Foo create(@RequestBody final Foo resource, final HttpServletResponse res
86102

87103
return foo;
88104
}
105+
106+
@PutMapping(value = "/{id}")
107+
@ResponseStatus(HttpStatus.OK)
108+
public void update(@PathVariable("id") final Long id, @RequestBody final Foo resource) {
109+
Preconditions.checkNotNull(resource);
110+
RestPreconditions.checkFound(service.findOne(resource.getId()));
111+
service.update(resource);
112+
}
113+
114+
@DeleteMapping(value = "/{id}")
115+
@ResponseStatus(HttpStatus.OK)
116+
public void delete(@PathVariable("id") final Long id) {
117+
service.deleteById(id);
118+
}
89119
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.baeldung.web.hateoas.event;
2+
3+
import javax.servlet.http.HttpServletResponse;
4+
5+
import org.springframework.context.ApplicationEvent;
6+
7+
public class SingleResourceRetrievedEvent extends ApplicationEvent {
8+
private final HttpServletResponse response;
9+
10+
public SingleResourceRetrievedEvent(final Object source, final HttpServletResponse response) {
11+
super(source);
12+
13+
this.response = response;
14+
}
15+
16+
// API
17+
18+
public HttpServletResponse getResponse() {
19+
return response;
20+
}
21+
22+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.baeldung.web.hateoas.listener;
2+
3+
import javax.servlet.http.HttpServletResponse;
4+
5+
import com.baeldung.web.hateoas.event.SingleResourceRetrievedEvent;
6+
import com.baeldung.web.util.LinkUtil;
7+
import org.springframework.context.ApplicationListener;
8+
import org.springframework.stereotype.Component;
9+
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
10+
11+
import com.google.common.base.Preconditions;
12+
import com.google.common.net.HttpHeaders;
13+
14+
@Component
15+
class SingleResourceRetrievedDiscoverabilityListener implements ApplicationListener<SingleResourceRetrievedEvent> {
16+
17+
@Override
18+
public void onApplicationEvent(final SingleResourceRetrievedEvent resourceRetrievedEvent) {
19+
Preconditions.checkNotNull(resourceRetrievedEvent);
20+
21+
final HttpServletResponse response = resourceRetrievedEvent.getResponse();
22+
addLinkHeaderOnSingleResourceRetrieval(response);
23+
}
24+
25+
void addLinkHeaderOnSingleResourceRetrieval(final HttpServletResponse response) {
26+
final String requestURL = ServletUriComponentsBuilder.fromCurrentRequestUri().build().toUri().toASCIIString();
27+
final int positionOfLastSlash = requestURL.lastIndexOf("/");
28+
final String uriForResourceCreation = requestURL.substring(0, positionOfLastSlash);
29+
30+
final String linkHeaderValue = LinkUtil.createLinkHeader(uriForResourceCreation, "collection");
31+
response.addHeader(HttpHeaders.LINK, linkHeaderValue);
32+
}
33+
34+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.baeldung.web;
2+
3+
import org.junit.Test;
4+
import org.junit.runner.RunWith;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
7+
import org.springframework.boot.test.context.SpringBootTest;
8+
import org.springframework.test.context.junit4.SpringRunner;
9+
import org.springframework.test.web.servlet.MockMvc;
10+
11+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
12+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
13+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
14+
15+
/**
16+
*
17+
* We'll start the whole context, but not the server. We'll mock the REST calls instead.
18+
*
19+
*/
20+
@RunWith(SpringRunner.class)
21+
@SpringBootTest
22+
@AutoConfigureMockMvc
23+
public class FooControllerAppIntegrationTest {
24+
25+
@Autowired
26+
private MockMvc mockMvc;
27+
28+
@Test
29+
public void whenFindPaginatedRequest_thenEmptyResponse() throws Exception {
30+
this.mockMvc.perform(get("/auth/foos").param("page", "0")
31+
.param("size", "2"))
32+
.andExpect(status().isOk())
33+
.andExpect(content().json("[]"));
34+
}
35+
36+
}

0 commit comments

Comments
 (0)