Rest with Spring

27
REST with Spring BJUG 6 @IBM Eugen Paraschiv

Transcript of Rest with Spring

REST with Spring

BJUG 6 @IBM

Eugen Paraschiv

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 ...

Conclusion

Questions: - ?- ?- ?- ?

THANKS