Soyons
RESTful
avec
RESTX !
by @xavierhanin
http://slid.es/xavierhanin/restx
Any information that can be named can be a resource - Roy Fielding
http://restx.io/modules
http://en.wikipedia.org/wiki/Main_Page
REST components perform actions on a resource by using a representation to capture the current or intended state of that resource and transferring that representation between components - Roy Fielding
{
"name":"Acme Inc",
"address": {
"body":"111, av du truc",
"zipCode":"33700",
"city":"Merignac"
},
"_id":"51eba5766bc8e48ffeaacc89"
}
XML
<company name="Acme Inc" _id="51eba5766bc8e48ffeaacc89">
<address>
<body>111, av du truc</body>
<zipcode>33700</zipcode>
<city>Merignac</city>
</address>
</company>
GET /companies/51ebab006bc8e48ffeaacc93
HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:39 GMT Content-Type:application/json; charset=UTF-8 {
"name":"ACME Inc", "address":{ "body":"1122, pooder st",
"zipCode":"12345 CA",
"city":"Palo Alto"}, "_id":"51ebab006bc8e48ffeaacc93" }
HEAD /companies/51ebab006bc8e48ffeaacc93
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Content-Type:application/json; charset=UTF-8
OPTIONS /companies/51ebab006bc8e48ffeaacc93
Origin: http://foo.example
Access-Control-Request-Method: PUT
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: PUT, POST, GET, OPTIONS
PUT /companies/51ebab006bc8e48ffeaacc93
{ "name":"ACME Inc",
"address":{
"body":"1122, pooder st",
"zipCode":"12345 CA",
"city":"Palo Alto"},
"_id":"51ebab006bc8e48ffeaacc93"}
HTTP/1.1 200 OK
{ "name":"ACME Inc",
"address":{
"body":"1122, pooder st",
"zipCode":"12345 CA",
"city":"Palo Alto"},
"_id":"51ebab006bc8e48ffeaacc93"}
DELETE /companies/51ebab006bc8e48ffeaacc93
HTTP/1.1 204 No Content
POST /companies
{ "name":"ACME Inc",
"address":{
"body":"1122, pooder st",
"zipCode":"12345 CA",
"city":"Palo Alto"} }
HTTP/1.1 201 Created
{ "name":"ACME Inc",
"address":{
"body":"1122, pooder st",
"zipCode":"12345 CA",
"city":"Palo Alto"},
"_id":"51ebab006bc8e48ffeaacc93"}
such that 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. - Roy Fielding
Java lightweight REST framework
Open Source
Apache License
$ curl -s http://restx.io/install.sh | sh $ restx ========================================================== == WELCOME TO RESTX SHELL - 0.31 - type `help` for help ========================================================== restx> app new Welcome to RESTX APP bootstrap! This command will ask you a few questions to generate your app. For any question you can get help by answering '??' App name? MyApp [...] Congratulations! - Your app is now ready in `~/projects/myapp`
<dependency> <groupId>io.restx</groupId> <artifactId>restx-core</artifactId> <version>0.31<version> </dependency>
<dependency org="io.restx" name="restx-core" rev="0.31" />
@Component @RestxResource
public class HelloResource {
@PermitAll @GET("/message")
public MyMessage sayHello(String who) {
return new MyMessage().setMessage(String.format(
"hello %s, it's %s",
who, DateTime.now().toString("HH:mm:ss")));
}
}
$ curl "http://localhost:8080/api/message?who=restx" {"message":"hello restx, it's 15:53:23"}
Parameters names, Parameterized types, ...
@GET("/cities")
public Iterable<City> findCities(Optional<String> name) {
if (name.isPresent()) {
return cities.get().find("{name: #}",
name.get()).as(City.class);
} else {
return cities.get().find().as(City.class);
}
}
@GET("/cities/{oid}")
public Optional<City> findCityById(String oid) {
return Optional.fromNullable(cities.get().findOne(new ObjectId(oid)).as(City.class));
}
@PUT("/cities/{oid}")
public City updateCity(String oid, City city) {
checkEquals("oid", oid, "city.key", city.getKey());
cities.get().save(city);
return city;
}
new StdEntityRoute("default#HelloResource#sayHello",
mapper, new StdRouteMatcher("GET", "/message")) {
@Override
protected Optional doRoute(
RestxRequest request,
RestxRouteMatch match) throws IOException {
securityManager.check(request, open());
return Optional.of(resource.sayHello(
/* [QUERY] who */ checkPresent(request.getQueryParam("who"),
"query param who is required")
));
}
}
@Size(min = 4)
private String name;
@Component
public class MyComponent {
public MyComponent(MyDependency dependency) {
...
}
}
@Component @RestxResource
public class HelloResource {
@PermitAll @GET("/message")
public MyMessage sayHello(String who) {
return new MyMessage().setMessage(String.format(
"hello %s, it's %s",
who, DateTime.now().toString("HH:mm:ss")));
}
}
@Component @RestxResource
public class HelloResource {
@RolesAllowed("admin") @GET("/message")
public MyMessage sayHello(String who) {
return new MyMessage().setMessage(String.format(
"hello %s, it's %s",
who, DateTime.now().toString("HH:mm:ss")));
}
}
$ restx app new [...] $ mvn package $ bees app:deploy target/*.war -a myaccount/myapp
@Component @RestxResource
public class CompanyResource {
private final JongoCollection companies;
public CompanyResource(
@Named("companies") JongoCollection companies) {
this.companies = companies;
}
@GET("/companies")
public Iterable<Company> findCompanies() {
return companies.get().find().as(Company.class);
}
}
title: should say hello given: - time: 2013-03-31T14:33:18.272+02:00 wts: - when: GET message?who=xavier then: |
200 OK
{"message":"hello xavier, it's 14:33:18"}