Rest with Spring
-
Upload
eugen-paraschiv -
Category
Technology
-
view
2.753 -
download
2
Transcript of Rest with Spring
Overview
● Why REST?
● RESTful Constraints and Guidelines
● REST via Spring MVC
● Persistence with Spring Data
● Testing of a RESTful Service
● Q&A
Why REST
● REST is a set of Constraints (it's not the only
one)
● Minimize Coupling between Client and
Server
● Update the Server frequently without
updating the Clients (no control over them)
● Support for many different types of Clients
● Scaling becomes easy(er)
The Four Levels of HTTP APIs - I, II
Level I. SOAP (Flickr SOAP API, Google AdSense API)
- WSDL describes the interface at design time- no Resources are identified by URI - only Service Endpoints- no difference between Resource and Representation- HTTP treated as transport - no use of HTTP semantics
Level II. RPC (Amazon SimpleDB, Flickr 'REST' API)
+ the API exposes Resources (often corresponding to the application models)- operations are done via actions in the URIs - the URI space is known at design time (ex. /post/make_new)
- operations, failure codes are application specific- HTTP treated as transport - no use of HTTP semantics
The Four Levels of HTTP APIs - III
Level III. HTTP (Twitter API)
+ Resources are exposed and identified by URIs+ Resources are manipulated via Representations+ HTTP semantics used correctly, use of generic media types (e.g. application/xml)- message semantics only known to Client and Server but not intermediaries - Client and Server are coupled by original design- application state machine is known at design time - assumptions about available representations and transitions are hard-coded
The Four Levels of HTTP APIs - IV
Level IV. REST (Atom Pub, OpenSearch)
+ service description comes in the form of media type (and link relations) specifications+ Client only knows entry bookmark (the Root URI) and media types and no specifics about the particular service+ Client proceeds through application by looking at one response at a time, each time evaluating how best to proceed given its overall goal and the available transitions+ Methods to use are known from media type (and link relations) specifications or selected at runtime based on forms (form semantics known from media type specifications)
REST SEC projectWHERE- @github - https://github.com/eugenp/REST
WHY- Reference Spring implementation of a REST Service- Identity Management Solution as a Service
HOW- REST/web: Spring 3.1.x- Marshalling: Jackson 2.x (for JSON) and XStream (for XML)- Persistence: Spring Data JPA and Hibernate 4.1.x- Testing: Junit, Hamcrest, Mockito, rest-assured, RestTemplate (Apache HTTP Client)
"Each request from client to server must contain all of the information necessary to understand the request, and cannot take advantage of any stored context on the server. Session state is therefore kept entirely on the client"
In Short- no sessions, no cookies- each request should contain it's authentication credentials
With Spring Security<http create-session="stateless" ... >
RESTful Constraints - I. Stateless
RESTful Constraints - II. Cache
● Caching is on the Client side
● Goal of Client side Caching - partially or
completely eliminate interactions with the
Server
● HTTP Caching options:
● ETag/If-None-Match
● Last-Modified/If-Modified-Since
III. Caching - ETag - example
- ex: first, retrieve a Privilege resource:
curl -H "Accept: application/json" -i http://localhost:8080/rest-sec/api/privileges/1
HTTP/1.1 200 OKServer: Apache-Coyote/1.1Link: <http://localhost:8080/rest-sec/api/privileges>; rel="collection"ETag: "f88dd058fe004909615a64f01be66a7"Last-Modified: Fri, 05 Oct 2012 11:36:33 GMTContent-Type: application/json;charset=UTF-8Content-Length: 52Date: Fri, 05 Oct 2012 11:36:33 GMT
III. Caching - ETags - example (cont)
- next, use the etag value from the previous response to retrieve the Privilege resource again: curl -H "Accept: application/json" -H 'If-None-Match: "f88dd058fe004909615a64f01be66a7"' -i http://localhost:8080/rest-sec/api/privileges/1
HTTP/1.1 304 Not ModifiedServer: Apache-Coyote/1.1Link: <http://localhost:8080/rest-sec/api/privileges>; rel="collection"ETag: "f88dd058fe004909615a64f01be66a7"Date: Fri, 05 Oct 2012 11:37:55 GMT
REST Constraint - III. Uniform Interface
Uniform Interface Constraints
1. Identification of Resources
2. Manipulation of Resources through Representations
3. Self-descriptive Messages
4. Hypermedia As The Engine Of Application State
(HATEOAS)
III.1. Identification of Resources - Spring MVC
For a sample foo resource: - the Controller@Controller
@RequestMapping(value = "foos")
public class FooController{ ... }
- retrieve by id: GET api/foos/id@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public Foo findOne(@PathVariable("id") Long id){...}
- search: GET api/foos?q=query@RequestMapping(params = {"q"}, method = RequestMethod.GET)
public List<Foo> search(@RequestParam("q") String query){...}
III.1. Identification of Resources - Spring MVC (cont)
- create single (update the collection): POST api/foos@RequestMapping(method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public void create(@RequestBody Foo resource) {...}
- update/override PUT api/foos/id@RequestMapping(method = RequestMethod.PUT)
@ResponseStatus(HttpStatus.OK)
public void update(@RequestBody Foo resource) { ... }
- delete: DELETE api/foos/id@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable("id") Long id) { ... }
REST Constraint - III. Uniform Interface
Supported Representations: - JSON - application/json- XML - application/xml- future: ATOM
All read operations will perform Content Negotiation when the Accept header is set on the requestAll write operations supports the Content-Type header to be set on the request
Spring MVC - the Controller/Web Layer
Simple Responsibilities: - mapping of URIs- Marshalling/Unmarshalling of Resource Representations (implicit)- Translation of Exceptions to HTTP Status Codes
REST Constraint - III. Uniform Interface - HATEOAS
- Discoverability at the root- the Create operation- making use of `rel`- Advanced Topics: custom Mime Types, HAL
Persistence Layer - Spring Data- DAO only with interfaces - no implementationspublic interface IUserJpaDAO extends JpaRepository<User, Long> { … }
- define a new, simple method: List<User> findByLastname(String lastname);List<User> findByEmailAddressAndLastname(String emailAddress, String lastname);
- flexibility to use @Query@Query("select u from User u where u.emailAddress = ?1")User findByEmailAddress(String emailAddress);
Persistence Layer - Spring Data
● Pagination Page<User> findByFirstname(String firstname, Pageable pageable);
● SortingList<User> findByLastname(String lastname, Sort sort);
Other out of the box features - support for: ● Audit: create date, created by, last update date, last
updated by
● Advanced Persistence APIs: QueryDSL,
JPA 2 Specifications
Persistence Layer - Spring Data
Transactional Semantics
- the API Layer Strategy● the Controller layer is the transactional
owner● the Service layer contains no transactional
semantics● there are no self-invocations or inter-
invocations in the Controller layer - each invocation is a client call
● Live Tests: testing the deployed RESTful
service○ each RESTful service has a corresponding
production API and a testing API
○ high level testing is done via the production API
○ lower level testing is done via the testing API
● Integration tests: business, persistence
● Unit tests
Testing of a REST Service
Testing - High Level Live Test (over REST)@Test
public void
givenResourceExists_whenResourceIsRetrievedByName_thenResourceIsFound() {
// Given
T existingResource = api.create(createNewEntity());
// When
T resourceByName = api.findByName(existingResource.getName());
// Then
assertNotNull(resourceByName);
}
Testing - Low Level Live Test (over REST)@Test
public void givenInvalidResource_whenResourceIsUpdated_then409ConflictIsReceived() {
// Given
User existingUser = RestAssured.given().auth().preemptive().basic(username, password).contentType("application/json").body(resourceAsJson).post(uri).as(User.class);
existingUser.setName(null);
// When
Response updateResponse = RestAssured.given().auth().preemptive().basic(username, password).contentType("application/json").body(existingUser).put(uri);
// Then
assertThat(updateResponse.getStatusCode(), is(409));
}
Security Concerns
- Basic and Digest Authentication with Spring Security ON THE SAME URI (similar to Content Negotiation): ● Authorization: Basic ...● Authorization: Digest ...