Créer une API avec Spark Java Framework

1. Introduction

Dans cet article, nous aurons une introduction rapide au framework Spark. Le framework Spark est un framework Web de développement rapide inspiré du framework Sinatra pour Ruby et est construit autour de la philosophie Java 8 Lambda Expression, ce qui le rend moins verbeux que la plupart des applications écrites dans d'autres frameworks Java.

C'est un bon choix si vous souhaitez avoir une expérience similaire à Node.js lors du développement d'une API Web ou de microservices en Java. Avec Spark, vous pouvez avoir une API REST prête à servir JSON en moins de dix lignes de code.

Nous allons commencer rapidement avec un exemple «Hello World», suivi d'une simple API REST.

2. Dépendances de Maven

2.1. Cadre Spark

Incluez la dépendance Maven suivante dans votre pom.xml :

 com.sparkjava spark-core 2.5.4 

Vous pouvez trouver la dernière version de Spark sur Maven Central.

2.2. Bibliothèque Gson

À divers endroits de l'exemple, nous utiliserons la bibliothèque Gson pour les opérations JSON. Pour inclure Gson dans votre projet, incluez cette dépendance dans votre pom.xml :

 com.google.code.gson gson 2.8.0 

Vous pouvez trouver la dernière version de Gson sur Maven Central.

3. Premiers pas avec Spark Framework

Jetons un coup d'œil aux éléments de base d'une application Spark et démontrons un service Web rapide.

3.1. Itinéraires

Les services Web de Spark Java reposent sur les routes et leurs gestionnaires. Les itinéraires sont des éléments essentiels dans Spark. Selon la documentation, chaque route est composée de trois éléments simples - un verbe , un chemin et un rappel .

  1. Le verbe est une méthode correspondant à une méthode HTTP. Les méthodes verbales incluent: obtenir, publier, mettre, supprimer, head, tracer, connecter et options
  2. Le chemin (également appelé modèle de route) détermine le ou les URI que la route doit écouter et fournir une réponse pour
  3. Le rappel est une fonction de gestionnaire qui est invoquée pour un verbe et un chemin donnés afin de générer et de renvoyer une réponse à la requête HTTP correspondante. Un rappel prend un objet de requête et un objet de réponse comme arguments

Ici, nous montrons la structure de base d'une route qui utilise le verbe get :

get("/your-route-path/", (request, response) -> { // your callback code });

3.2. API Hello World

Créons un service Web simple qui a deux routes pour les requêtes GET et renvoie des messages «Hello» en réponse. Ces routes utilisent la méthode get , qui est une importation statique à partir de la classe spark .

import static spark.Spark.*; public class HelloWorldService { public static void main(String[] args) { get("/hello", (req, res)->"Hello, world"); get("/hello/:name", (req,res)->{ return "Hello, "+ req.params(":name"); }); } }

Le premier argument de la méthode get est le chemin de la route. La première route contient un chemin statique représentant un seul URI ( «/ hello» ).

Le chemin de la deuxième route ( «/ bonjour /: nom» ) contient un espace réservé pour le paramètre «nom» , comme indiqué en faisant précéder le paramètre par deux points («:»). Cette route sera invoquée en réponse aux requêtes GET aux URI tels que «/ hello / Joe» et «/ hello / Mary» .

Le deuxième argument de la méthode get est une expression lambda donnant une saveur de programmation fonctionnelle à ce framework.

L'expression lambda a une requête et une réponse comme arguments et aide à renvoyer la réponse. Nous allons mettre notre logique de contrôleur dans l'expression lambda pour les routes de l'API REST, comme nous le verrons plus loin dans ce tutoriel.

3.3. Test de l'API Hello World

Après avoir exécuté la classe HelloWorldService en tant que classe Java normale, vous pourrez accéder au service sur son port par défaut 4567 en utilisant les routes définies avec la méthode get ci-dessus.

Regardons la demande et la réponse pour la première route:

Demande:

GET //localhost:4567/hello

Réponse:

Hello, world

Testons la deuxième route, en passant le paramètre name dans son chemin:

Demande:

GET //localhost:4567/hello/baeldung

Réponse:

Hello, baeldung

Voyez comment le placement du texte «baeldung» dans l'URI a été utilisé pour correspondre au modèle de route «/ hello /: name» - provoquant l'appel de la fonction de gestionnaire de rappel de la deuxième route.

4. Conception d'un service RESTful

Dans cette section, nous allons concevoir un service Web REST simple pour l' entité utilisateur suivante :

public class User { private String id; private String firstName; private String lastName; private String email; // constructors, getters and setters }

4.1. Itinéraires

Listons les routes qui composent notre API:

  • GET / users - obtenir la liste de tous les utilisateurs
  • GET / users /: id - obtient un utilisateur avec un identifiant donné
  • POST / users /: id - ajouter un utilisateur
  • PUT / users /: id - éditer un utilisateur particulier
  • OPTIONS / users /: id - vérifier si un utilisateur existe avec un identifiant donné
  • DELETE / users /: id - supprimer un utilisateur particulier

4.2. Le service utilisateur

Below is the UserService interface declaring the CRUD operations for the User entity:

public interface UserService { public void addUser (User user); public Collection getUsers (); public User getUser (String id); public User editUser (User user) throws UserException; public void deleteUser (String id); public boolean userExist (String id); }

For demonstration purposes, we provide a Map implementation of this UserService interface in the GitHub code to simulate persistence. You can supply your own implementation with the database and persistence layer of your choice.

4.3. The JSON Response Structure

Below is the JSON structure of the responses used in our REST service:

{ status:  message:  data:  }

The status field value can be either SUCCESS or ERROR. The data field will contain the JSON representation of the return data, such as a User or collection of Users.

When there is no data being returned, or if the status is ERROR, we will populate the message field to convey a reason for the error or lack of return data.

Let's represent the above JSON structure using a Java class:

public class StandardResponse { private StatusResponse status; private String message; private JsonElement data; public StandardResponse(StatusResponse status) { // ... } public StandardResponse(StatusResponse status, String message) { // ... } public StandardResponse(StatusResponse status, JsonElement data) { // ... } // getters and setters }

where StatusResponse is an enum defined as below:

public enum StatusResponse { SUCCESS ("Success"), ERROR ("Error"); private String status; // constructors, getters }

5. Implementing RESTful Services

Now let's implement the routes and handlers for our REST API.

5.1. Creating Controllers

The following Java class contains the routes for our API, including the verbs and paths and an outline of the handlers for each route:

public class SparkRestExample { public static void main(String[] args) { post("/users", (request, response) -> { //... }); get("/users", (request, response) -> { //... }); get("/users/:id", (request, response) -> { //... }); put("/users/:id", (request, response) -> { //... }); delete("/users/:id", (request, response) -> { //... }); options("/users/:id", (request, response) -> { //... }); } }

We will show the full implementation of each route handler in the following subsections.

5.2. Add User

Below is the post method response handler which will add a User:

post("/users", (request, response) -> { response.type("application/json"); User user = new Gson().fromJson(request.body(), User.class); userService.addUser(user); return new Gson() .toJson(new StandardResponse(StatusResponse.SUCCESS)); });

Note: In this example, the JSON representation of the User object is passed as the raw body of a POST request.

Let's test the route:

Request:

POST //localhost:4567/users { "id": "1012", "email": "[email protected]", "firstName": "Mac", "lastName": "Mason1" }

Response:

{ "status":"SUCCESS" }

5.3. Get All Users

Below is the get method response handler which returns all users from the UserService:

get("/users", (request, response) -> { response.type("application/json"); return new Gson().toJson( new StandardResponse(StatusResponse.SUCCESS,new Gson() .toJsonTree(userService.getUsers()))); });

Now let's test the route:

Request:

GET //localhost:4567/users

Response:

{ "status":"SUCCESS", "data":[ { "id":"1014", "firstName":"John", "lastName":"Miller", "email":"[email protected]" }, { "id":"1012", "firstName":"Mac", "lastName":"Mason1", "email":"[email protected]" } ] }

5.4. Get User by Id

Below is the get method response handler which returns a User with the given id:

get("/users/:id", (request, response) -> { response.type("application/json"); return new Gson().toJson( new StandardResponse(StatusResponse.SUCCESS,new Gson() .toJsonTree(userService.getUser(request.params(":id"))))); });

Now let's test the route:

Request:

GET //localhost:4567/users/1012

Response:

{ "status":"SUCCESS", "data":{ "id":"1012", "firstName":"Mac", "lastName":"Mason1", "email":"[email protected]" } }

5.5. Edit a User

Below is the put method response handler, which edits the user having the id supplied in the route pattern:

put("/users/:id", (request, response) -> { response.type("application/json"); User toEdit = new Gson().fromJson(request.body(), User.class); User editedUser = userService.editUser(toEdit); if (editedUser != null) { return new Gson().toJson( new StandardResponse(StatusResponse.SUCCESS,new Gson() .toJsonTree(editedUser))); } else { return new Gson().toJson( new StandardResponse(StatusResponse.ERROR,new Gson() .toJson("User not found or error in edit"))); } });

Note: In this example, the data are passed in the raw body of a POST request as a JSON object whose property names match the fields of the User object to be edited.

Let's test the route:

Request:

PUT //localhost:4567/users/1012 { "lastName": "Mason" }

Response:

{ "status":"SUCCESS", "data":{ "id":"1012", "firstName":"Mac", "lastName":"Mason", "email":"[email protected]" } }

5.6. Delete a User

Below is the delete method response handler, which will delete the User with the given id:

delete("/users/:id", (request, response) -> { response.type("application/json"); userService.deleteUser(request.params(":id")); return new Gson().toJson( new StandardResponse(StatusResponse.SUCCESS, "user deleted")); });

Now, let's test the route:

Request:

DELETE //localhost:4567/users/1012

Response:

{ "status":"SUCCESS", "message":"user deleted" }

5.7. Check if User Exists

The options method is a good choice for conditional checking. Below is the options method response handler which will check whether a User with the given id exists:

options("/users/:id", (request, response) -> { response.type("application/json"); return new Gson().toJson( new StandardResponse(StatusResponse.SUCCESS, (userService.userExist( request.params(":id"))) ? "User exists" : "User does not exists" )); });

Now let's test the route:

Request:

OPTIONS //localhost:4567/users/1012

Response:

{ "status":"SUCCESS", "message":"User exists" }

6. Conclusion

Dans cet article, nous avons eu une introduction rapide au framework Spark pour un développement Web rapide.

Ce framework est principalement promu pour générer des microservices en Java. Les développeurs Node .

Et comme toujours, vous pouvez trouver toutes les sources de ce tutoriel dans le projet Github.