Cyril Lacôte
Ninja Squad
@clacote
REpresentational State Transfer
REST c'est quoi?
- C’est un style d’architecture
- Ce n’est pas un standard
- C’est une alternative à SOAP
REST utilise les standards existants
- HTTP
- URIs/URLs
- XML, HTML, PNG, JSON...
- application/xml, text/html, image/png, ... (types MIME)
Stateless
horizontal scalability
JSON or XML
But JSON is obviously THE FUTURE!
{
"name": "Cyril Lacôte",
"company": "Ninja Squad",
"email": "cyril@ninja-squad.com",
"twitter": "@clacote",
"associations" : ["LyonJUG", "Mix-IT"]
}
Everybody familiar
with Json?
URI
http://server.com/users/clacote
HTTP verb
GETPOSTPUTDELETEgetUserById(id)GET /users/{id}deleteUser(id)DELETE /users/{id}updateUserPUT /users/{id}searchUser(criteria)GET /users/search?name=Lac*&age=34Code http
3 cas courants
OK, erreur client, erreur serveur
HTTP status 403
{
"code": "14",
"developerMessage": "this user has not enough credentials to access admin repository",
"more": "http://mywebsite.com/api/errors/14",
"userMessage": "You can't access this repository",
}
Content type negociation
application/JSON
application/xml
HATEOAS
Hypermedia as the Engine
of Application State
Pragmatist
vs
RESTafarian
Api sub domain
http://api.github.com
Verbs are bad
Nouns are good
concrete nouns
lower case
plural (as in collections)
Attributs names
"created_at": Twitter, Github
"createdAt": Foursquare
Pagination
/users?limit=120
/users?page=2&count=30
/users?fields=name,email
A controller
@Controller
@RequestMapping("/spectacle")
public class SpectacleController {
@Autowired
private SpectacleService spectacleService;
@RequestMapping(value="/{id}", method=RequestMethod.GET)
public ModelAndView view(@PathVariable("id") Long id) {
Spectacle spectacle = spectacleService.findById(id);
ModelAndView result = new ModelAndView("spectacle");
result.addObject("spectacle", spectacle);
return result;
}
}
Been there,
Done that.
So what about
JSON handling?
JSON needs
- Check ACCEPT_TYPE header (if we're nice)
- Read JSON input (if we need to)
- Write JSON ouput.
You're lucky to use Spring MVC :
Add Jackson to your CLASSPATH
and... that's it!
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.12</version>
</dependency>
Jackson?
A JSON marshaller. THE JSON marshaller.(other famous one: Gson)
Write Json?
Jackson API
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(writer, javaObject);
Through Spring MVC:
@ResponseBody on return type
If request accept "application/json"
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public @ResponseBody Spectacle view(@PathVariable("id") Long id) {
Spectacle spectacle = spectacleService.findById(id);
return spectacle;
}
One gotcha
Default Json mapping:
all fields serialized.
Avoid cyclic references!
How to customize Json serialization?
Lot of ways (check documentation).
Easiest: annotate your model classes.
-
@JsonIgnore to ignore attribute
-
@JsonProperty("otherName") to customize name
- ... and many more.
Read Json?
Jackson API
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(json, User.class);
Through Spring MVC:
@RequestBody on action method parameter
If request content-type "application/json"
@RequestMapping(value = "/", method = RequestMethod.POST)
public void Spectacle create(@RequestBody Spectacle spectacle) {
spectacleService.createSpectacle(spectacle);
}
Handling errors?
and return dedicated HTTP error status?
- Define your own exception class
-
@ExceptionHandler (in same controller class)
@ExceptionHandler(InvalidRequestException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public @ResponseBody String whenInvalidRequest() {
return "invalid data";
}
And now, for your coding pleasure
GET /api/spectacles/ : liste des spectacles
GET /api/spectacles/{id} : détail du spectacle d'identifiant {id}
POST /api/spectacles/ : créer un nouveau spectacle
avec gestion d'erreurs (paramètre manquant -> erreur HTTP 400)