Top Banner
Craig Walls [email protected] Twitter: @habuma @springsocial http://github.com/habuma Giving Spring some REST
53

Spring Rest

Dec 14, 2015

Download

Documents

Rubesh

Restful API's in Spring Framework by Craig Walls
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Spring Rest

Craig [email protected]: @habuma @springsocialhttp://github.com/habuma

Giving Spring some REST

Page 2: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

REST in One Slide

Resources (aka, the “things”)Representations

HTTP Methods (aka, the “verbs”)URIs and URLs

Page 3: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Why REST?

Key piece of the Modern Application puzzleMore APIs / Fewer “pages”

Humans and browsers consume pagesEverything can consume APIs

(incl. browsers, JS, mobile apps, other apps,etc)

Page 4: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Spring’s REST Story

Spring MVC 3.0+Spring HATEOAS

Spring Security for OAuth (S2OAuth)Spring RestTemplate

Spring SocialSpring Data RESTSpring REST Shell

WebSocket/STOMP Support (Spring 4)Spring Boot

Page 5: Spring Rest

Creating REST APIs

Page 6: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Let’s write a simple REST controller...

@Controller@RequestMapping("/books")public class BooksController {!! private BookRepository bookRepository;

! @Inject! public BooksController(BookRepository bookRepository) {! ! this.bookRepository = bookRepository;! !! }

! @RequestMapping(method=RequestMethod.GET)! public @ResponseBody List<Book> allBooks() {! ! return bookRepository.findAll();! }!}

GET http://host/app/books

Page 7: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

How Does @ResponseBody Work?

How does it know how to write it to the response?

Page 8: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Message ConvertersStringHttpMessageConverter

Reads “text/*” into String; writes String into “text/plain”

FormHttpMessageConverterReads/writes “application/x-www-form-urlencoded” from/to MultiValueMap<String,String>

ByteArrayMessageConverterReads “*/*” into byte[]; writes Object as “application/octet-stream”

Jaxb2RootElementHttpMessageConverterReads/writes “text/xml” or “application/xml” from/to JAXB-annotated objects

MappingJacksonHttpMessageConverter / MappingJackson2HttpMessageConverter

Reads/writes “application/json” from/to Objects

SourceHttpMessageConverterReads/writes “text/xml”/”application/xml” from/to javax.xml.transform.Source

ResourceHttpMessageConverterReads/writes org.springframework.core.io.Resource objects

[AtomFeed|RssChannel]HttpMessageConverterReads/writes Rome Feed and RssChannels (application/atom+xml | rss+xml)

Page 9: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Let’s Add Another Endpoint

@Controller@RequestMapping("/books")public class BooksController {!...

! @RequestMapping(value="/{id}", method=RequestMethod.GET)! public @ResponseBody Book bookById(@PathVariable("id") long id) {! ! return bookRepository.findOne(id);! }!}

GET http://host/app/books/{id}

Page 10: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

What About POSTing Resources?

@Controller@RequestMapping("/books")public class BooksController {!...

! @RequestMapping(method=RequestMethod.POST)! public @ResponseBody Book postBook(@RequestBody Book book) {! ! return bookRepository.save(book);! }!

}

POST http://host/app/books

Page 11: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Can We PUT a Resource?

@Controller@RequestMapping("/books")public class BooksController {!...

! @RequestMapping(value="/{id}" method=RequestMethod.PUT)! public void updateBook(@PathVariable("id") long id, @RequestBody Book book) {! ! bookRepository.save(book);! }

}

PUT http://host/app/books/{id}

Page 12: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Deleting a Resource

@Controller@RequestMapping("/books")public class BooksController {!...

! @RequestMapping(value="{id}", method=RequestMethod.DELETE)! public void deleteBook(@PathVariable("id") long id) {! ! bookRepository.delete(id);! }

}

DELETE http://host/app/books/{id}

Page 13: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

There’s More Than the Resource...

@Controller@RequestMapping("/books")public class BooksController {!...

! @RequestMapping(value="/{id}", method=RequestMethod.GET)! public @ResponseBody Book bookById(@PathVariable("id") long id) {! ! return bookRepository.findOne(id);! }!}

What will happen if findById() returns null?

What should happen?

Page 14: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

There’s More Than the Resource...

@Controller@RequestMapping("/books")public class BooksController {!...

! @RequestMapping(method=RequestMethod.POST)! public @ResponseBody Book postBook(@RequestBody Book book) {! ! return bookRepository.save(book);! }!

}

What will the HTTP status code be?

What should it be?

Page 15: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Returning a ResponseEntity

@Controller@RequestMapping("/books")public class BooksController {!...

! @RequestMapping(value="/{id}", method=RequestMethod.GET)! public ResponseEntity<?> bookById(@PathVariable("id") long id) {

Book book = bookRepository.findOne(id);if (book != null) {

return new ResponseEntity<Book>(book, HttpStatus.OK);} else {

Error error = new Error("Book with ID " + id + " not found");return new ResponseEntity<Error>(error, HttpStatus.NOT_FOUND);

}! }!}

Page 16: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Returning a ResponseEntity

@Controller@RequestMapping("/books")public class BooksController {!...

! @RequestMapping(value="/{id}", method=RequestMethod.GET)! public ResponseEntity<Book> bookById(@PathVariable("id") long id) {

Book book = bookRepository.findOne(id);if (book != null) {

return new ResponseEntity<Book>(book, HttpStatus.OK);}throw new BookNotFoundException(id);

! }!

@ExceptionHandler(BookNotFoundException.class)public ResponseEntity<Error> bookNotFound(BookNotFoundException e) {

Error error = new Error("Book with ID " + id + " not found");return new ResponseEntity<Error>(error, HttpStatus.NOT_FOUND);

}}

Page 17: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Returning a ResponseEntity

@Controller@RequestMapping("/books")public class BooksController {!...

! @RequestMapping(method=RequestMethod.POST)! public ResponseEntity<Book> postBook(@RequestBody Book book) {! ! Book newBook = bookRepository.save(book);! ! ResponseEntity<Book> bookEntity =

new ResponseEntity<Book>(newBook, HttpStatus.CREATED);! ! String locationUrl =

ServletUriComponentsBuilder.fromCurrentContextPath().! ! ! ! path("/books/" + newBook.getId()).build().toUriString();! ! bookEntity.getHeaders().setLocation(URI.create(locationUrl));! ! return bookEntity;! }

}

Page 18: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Linking Resources

HATEOAS

Hypermedia As The Engine Of Application State

Responses carry links to related endpoints

API is self-descriptive

Client can “learn” about the API

Page 19: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Self-Describing API

{ "links" : [ { "rel" : "self", "href" : "http://localhost:8080/BookApp/books/5" }, { "rel" : "all", "href" : "http://localhost:8080/BookApp/books/" },

{"rel" : "author","href" : "http://localhost:8080/BookApp/authors/2"

} ],

"id" : 5, "title" : "Spring in Action", ...}

Page 20: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Defining a Resource

public class BookResource extends ResourceSupport { ...}

Page 21: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Adding Links to a Resource

@RequestMapping(value="/{id}", method=RequestMethod.GET)public ResponseEntity<BookResource> bookById(@PathVariable("id") long id) { Book book = bookRepository.findOne(id); if (book != null) {

BookResource resource = bookResourceAssembler.toResource(book); resource.add(ControllerLinkerBuilder.linkTo(BooksController.class) .withRel("all"));

resource.add(ControllerLinkerBuilder.linkTo(AuthorsController.class) .slash(book.getAuthor().getId());

.withRel("author");

return new ResponseEntity<BookResource>(resource, HttpStatus.OK); }

throw new BookNotFoundException(id);}

Page 22: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Assembling a Resource

public class BookResourceAssembler extends ResourceAssemblerSupport<Book, BookResource> {

public BookResourceAssembler() { super(BooksController.class, BookResource.class); }

public BookResource toResource(Book book) { return createResource(book); }

public BookResource instantiateResource(Book book) { return new BookResource(book); }

}

Page 23: Spring Rest

Asynchronous Controllers

Page 24: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Spring + WebSocket + STOMP

Spring 4.0.0.M1: Low-level WebSocket supportSpring 4.0.0.M2: Higher-level, STOMP support

Messaging, not request-handlingSTOMP: Simple Text Oriented Messaging Protocol

Page 25: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Handling messages

@Controllerpublic class PortfolioController {

...

! @MessageMapping(value="/app/trade")! public void executeTrade(Trade trade, Principal principal) {! ! trade.setUsername(principal.getName());! ! this.tradeService.executeTrade(trade);! }

...}

Page 26: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Handling subscriptions

@Controllerpublic class PortfolioController {

...

! @SubscribeEvent("/app/positions")! public List<PortfolioPosition> getPortfolios(Principal principal) throws Exception {

! ! Portfolio portfolio = this.portfolioService.findPortfolio(principal.getName());

! ! return portfolio.getPositions();! }

...}

Page 27: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Handling message exceptions

@Controllerpublic class PortfolioController {

...

! @MessageExceptionHandler! @ReplyToUser(value="/queue/errors")! public String handleException(Throwable exception) {! ! return exception.getMessage();! }

...

}

Page 28: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

On the client side: Receiving messages

var socket = new SockJS('/spring-websocket-portfolio/portfolio');var stompClient = Stomp.over(socket);

...

stompClient.subscribe("/app/positions", function(message) { self.portfolio().loadPositions(JSON.parse(message.body));});

stompClient.subscribe("/topic/price.stock.*", function(message) { self.portfolio().processQuote(JSON.parse(message.body));});

stompClient.subscribe("/queue/errors" + queueSuffix, function(message) { self.pushNotification("Error " + message.body);});

Page 29: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

On the client side: Sending messages

var socket = new SockJS('/spring-websocket-portfolio/portfolio');var stompClient = Stomp.over(socket);

...

var trade = { "action" : self.action(), "ticker" : self.currentRow().ticker, "shares" : self.sharesToTrade()};

stompClient.send("/app/trade", {}, JSON.stringify(trade));

Page 30: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Configuring STOMP in Spring

Well...there are a lot of beans...

It gets better in 4.0.0.RC1 (see SPR-10835)...

But for now...

Page 31: Spring Rest

Securing REST APIs

Page 32: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

OAuth

• An open standard for authorization

• Supported by Facebook, Twitter, LinkedIn, TripIt, Salesforce, and dozens more

• Puts the user in control of what resources are shared

http://oauth.net

Page 33: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

The Many Versions of OAuth

OAuth 1.0

TripIt, NetFlix, DropBox, Gliffy, MySpace, ...

OAuth 1.0a

Twitter, LinkedIn, Evernote, Flickr, Yammer, Yelp!, ...

OAuth 2

Facebook, Foursquare, Google, GitHub, Instagram, ...

Page 34: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

OAuth 2

• Much simpler than OAuth 1.0(a)

• No more request token

• Leverages HTTPS instead of encrypting the token

• No signature or canonicalization of the request

• Much simpler Authorization header

• Scoped authorization

• Short-lived tokens, long-lived authorization

• Separate roles of authorization server/resource server

• Multiple grant types

Page 35: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Authorization Code Grant

• Like OAuth 1.0 flow

• Starts with redirect to provider for authorization

• After authorization, redirects back to client with code query parameter

• Code is exchanged for access token

• Client must be able to keep tokens confidential

• Commonly used for web apps

Page 36: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Implicit Grant

• Simplified authorization flow

• After authorization, redirects back to client with access token in fragment parameter

• Reduced round-trips

• No refresh token support

• Commonly used by in-browser JavaScript apps or widgets

Page 37: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Resource Owner Credentials Grant

• Directly exchanges user’s credentials for an access token

• Useful where the client is well-trusted by the user and where a browser redirect would be awkward

• Commonly used with mobile apps

Page 38: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Client Credentials Grant

• Directly exchanges the client’s credentials for an access token

• For accessing client-owned resources (with no user involvement)

Page 39: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

The OAuth 2 Authorization Header

Authorization: Bearer e139a950-2fc5-4822-9266-8a2b572108c5

Much simpler than OAuth 1.0(a)Differs across OAuth 2 drafts...

Authorization: BEARER e139a950-2fc5-4822-9266-8a2b572108c5

Authorization: OAuth e139a950-2fc5-4822-9266-8a2b572108c5

Authorization: Token token=”e139a950-2fc5-4822-9266-8a2b572108c5”

Drafts 14-current

Drafts 12-13

Draft 10

Drafts 1-9

Page 40: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

OAuth Provider Responsibilities

• Authorization server

• If supporting authorization code and/or implicit grant, must serve an authorization page

• Support an authorization endpoint for all supported grant types

• Not obligated to support all grant types

• Produce and manage tokens

• Resource server

• Validate access tokens on requests to resource endpoints

Page 41: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Spring Security OAuth (S2OAuth)

• Based on Spring Security

• Declarative model for OAuth

• Provider-side support for authorization endpoints, token management, and resource-level security

• Also offers client-side OAuth

• Implemented for both OAuth 1 and OAuth 2

• http://www.springsource.org/spring-security-oauth

Page 42: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Key Pieces of S2OAuth

• Authorization Server

• Implemented as Spring MVC controller

• Handles /oauth/authorize and /oauth/token endpoints

• Resource Server

• Implemented as servlet filters

Page 43: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Configuring the Authorization Server<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices"> <oauth:authorization-code /> <oauth:implicit /> <oauth:refresh-token /> <oauth:client-credentials /> <oauth:password /></oauth:authorization-server>

<oauth:client-details-service id="clientDetails"> <oauth:client client-id="tonr" secret="secret" resource-ids="sparklr" authorized-grant-types="authorization_code,implicit" authorities="ROLE_CLIENT" scope="read,write" /></oauth:client-details-service>

<bean id="tokenServices" class="o.sf.security.oauth2.provider.token.DefaultTokenServices"> <property name="tokenStore" ref="tokenStore" /> <property name="supportRefreshToken" value="true" /> <property name="clientDetailsService" ref="clientDetails"/></bean>

<bean id="tokenStore" class="o.sf.security.oauth2.provider.token.InMemoryTokenStore" />

Page 44: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Configuring the Authorization Server

<http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager" entry-point-ref="oauthAuthenticationEntryPoint" xmlns="http://www.springframework.org/schema/security"> <intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />! <anonymous enabled="false" />! <http-basic entry-point-ref="oauthAuthenticationEntryPoint" /></http>

<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">! <authentication-provider user-service-ref="clientDetailsUserService" /></authentication-manager>

<bean id="clientDetailsUserService" class="o.sf.security.oauth2.provider.client.ClientDetailsUserDetailsService"> <constructor-arg ref="clientDetails" /></bean>

<bean id="oauthAuthenticationEntryPoint" class="o.sf.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"> <property name="realmName" value="sparklr2" /></bean>

Page 45: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Configuring the Resource Server

<http pattern="/photos/**" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint"! access-decision-manager-ref="accessDecisionManager" ! xmlns="http://www.springframework.org/schema/security">! <anonymous enabled="false" />

! <intercept-url pattern="/photos" access="ROLE_USER,SCOPE_READ" />! <intercept-url pattern="/photos/trusted/**" access="ROLE_CLIENT,SCOPE_TRUST" />! <intercept-url pattern="/photos/user/**" access="ROLE_USER,SCOPE_TRUST" />! <intercept-url pattern="/photos/**" access="ROLE_USER,SCOPE_READ" />

! <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" /></http>

<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased" xmlns="http://www.springframework.org/schema/beans"> <constructor-arg> <list> <bean class="o.sf.security.oauth2.provider.vote.ScopeVoter" /> <bean class="o.sf.security.access.vote.RoleVoter" /> <bean class="o.sf.security.access.vote.AuthenticatedVoter" /> </list> </constructor-arg></bean>

Page 46: Spring Rest

Consuming REST APIs

Page 47: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

RestTemplate

Handles boilerplate HTTP connection codeKeeps your focus on the resources

Page 48: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Using RestTemplate

RestTemplate restTemplate = new RestTemplate();Book book = new Book("Spring in Action", "Gregg Walls");

Book newBook = restTemplate.postForObject( "http://host/app/books", book, Book.class);

book = restTemplate.getForObject( "http://host/app/books/{id}", Book.class, newBook.getId());

book.setAuthor("Craig Walls");restTemplate.put("http://host/app/books/{id}", Book.class, book.getId());

restTemplate.delete("http://host/app/books/{id}", Book.class, book.getId());

Page 49: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Tweeting with RestTemplate

RestTemplate rest = new RestTemplate();MultiValueMap<String, Object> params = new LinkedMultiValueMap<String, Object>();params.add("status", "Hello Twitter!");rest.postForObject("https://api.twitter.com/1/statuses/update.json", params, String.class);

WARNING: POST request for "https://api.twitter.com/1/statuses/update.json" resulted in 401 (Unauthorized); invoking error handlerorg.springframework.web.client.HttpClientErrorException: 401 Unauthorized! at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:75)! at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:486)! at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:443)! at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:401)! at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:279)

Oh no!

Page 50: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Configuring Spring Social

@Configuration@EnableJdbcConnectionRepository@EnableTwitter(appId="${twitter.consumerKey}", appSecret="${twitter.consumerSecret}")@EnableFacebook(appId="${facebook.clientId}", appSecret="${facebook.clientSecret}")public class SocialConfig {!! @Inject! private ConnectionFactoryLocator connectionFactoryLocator;!! @Inject ! private ConnectionRepository connectionRepository;

! @Bean! public ConnectController connectController() {! ! return new ConnectController(connectionFactoryLocator, connectionRepository);! }

}

Page 51: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Configuring Spring Social

<facebook:config app-id="${facebook.clientId}" app-secret="${facebook.clientSecret}" app-namespace="socialshowcase" /><twitter:config app-id="${twitter.consumerKey}" app-secret="${twitter.consumerSecret}"/>

<social:jdbc-connection-repository/>!

<bean id="connectController" class="org.springframework.social.connect.web.ConnectController" autowire="constructor" />

Page 52: Spring Rest

Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Tweeting with Spring Social

public class TwitterTimelineController {

! private final Twitter twitter;!! @Inject! public TwitterTimelineController(Twitter twitter) {! ! this.twitter = twitter;! }

! @RequestMapping(value="/twitter/tweet", method=RequestMethod.POST)! public String postTweet(String message) {! ! twitter.timelineOperations().updateStatus(message);! ! return "redirect:/twitter";! }

}

Page 53: Spring Rest

Thank you!REST Books Sample (a work in progress)

https://github.com/habuma/rest-books

Spring HATEOAShttps://github.com/SpringSource/spring-hateoas

Spring Security for OAuth (S2OAuth)http://www.springsource.org/spring-security-oauth

Spring Socialhttp://www.springsource.org/spring-security-oauth

Spring Data RESThttp://www.springsource.org/spring-data/rest

Spring REST Shellhttps://github.com/SpringSource/rest-shell