Construire une API avec le framework Java Spark

Construire une API avec le framework Java Spark

1. introduction

Dans cet article, nous aurons une brève introduction àSpark framework. Le framework Spark est un framework web de développement rapide inspiré du framework Sinatra pour Ruby et construit autour de la philosophie Java 8 Lambda Expression, le rendant ainsi moins bavard que la plupart des applications écrites dans d'autres frameworks Java.

C'est un bon choix si vous voulez avoir une expérience deNode.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 commencerons rapidement par un exemple de «Hello World», suivi d’une simple API REST.

2. Dépendances Maven

2.1. Cadre Spark

Incluez la dépendance Maven suivante dans vospom.xml:


    com.sparkjava
    spark-core
    2.5.4

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

2.2. Bibliothèque Gson

À différents 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 vospom.xml:


    com.google.code.gson
    gson
    2.8.0

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

3. Premiers pas avec Spark Framework

Examinons les éléments de base d'une application Spark et présentons un service Web rapide.

3.1. Itinéraires

Les services Web dans Spark Java sont construits sur les itinéraires et leurs gestionnaires. Les itinéraires sont des éléments essentiels de Spark. Selon lesdocumentation, chaque route est composée de trois morceaux simples - unverb, unpath et uncallback.

  1. Leverb est une méthode correspondant à une méthode HTTP. Les méthodes verbales incluent:get, post, put, delete, head, trace, connect, etoptions

  2. Lepath (également appelé un modèle de route) détermine quel (s) URI (s) la route doit écouter et fournir une réponse pour

  3. Lecallback est une fonction de gestionnaire qui est appelée pour un verbe et un chemin donnés afin de générer et de retourner une réponse à la requête HTTP correspondante. Un rappel prend un objet requête et un objet réponse en argument

Nous montrons ici la structure de base d'une route qui utilise le verbeget:

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éthodeget, qui est une importation statique de la classespark.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éthodeget 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 (“/hello/:name”) contient un espace réservé pour le paramètre“name”, comme indiqué en faisant précéder le paramètre d'un signe 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éthodeget est unlambda expression donnant une saveur de programmation fonctionnelle à ce framework.

L'expression lambda a une requête et une réponse comme arguments et permet de 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 classeHelloWorldService en tant que classe Java normale, vous pourrez accéder au service sur son port par défaut de4567 en utilisant les routes définies avec la méthodeget ci-dessus.

Examinons la demande et la réponse pour le premier itinéraire:

Demande:

GET http://localhost:4567/hello

Réponse:

Hello, world

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

Demande:

GET http://localhost:4567/hello/example

Réponse:

Hello, example

Découvrez comment le placement du texte“example” dans l'URI a été utilisé pour correspondre au modèle d'itinéraire“/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éUser suivante:

public class User {
    private String id;
    private String firstName;
    private String lastName;
    private String email;

    // constructors, getters and setters
}

4.1. Itinéraires

Énumérons les routes qui composent notre API:

  • GET / users - obtenir la liste de tous les utilisateurs

  • GET / users /: id - récupère l'utilisateur avec l'identifiant donné

  • POST / users /: id - ajoute un utilisateur

  • PUT / users /: id - édite un utilisateur particulier

  • OPTIONS / users /: id - vérifie si un utilisateur existe avec un identifiant donné

  • DELETE / users /: id - supprime un utilisateur particulier

4.2. Le service utilisateur

Ci-dessous se trouve l'interfaceUserService déclarant les opérations CRUD pour l'entitéUser:

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);
}

À des fins de démonstration, nous fournissons une implémentationMap de cette interfaceUserService dans le code GitHub pour simuler la persistance. You can supply your own implementation with the database and persistence layer of your choice.

4.3. La structure de réponse JSON

Vous trouverez ci-dessous la structure JSON des réponses utilisées dans notre service REST:

{
    status: 
    message: 
    data: 
}

La valeur du champstatus peut êtreSUCCESS ouERROR. Le champdata contiendra la représentation JSON des données de retour, comme unUser ou une collection deUsers.

Lorsqu'aucune donnée n'est retournée, ou sistatus estERROR, nous remplirons le champmessage pour indiquer la raison de l'erreur ou du manque de données de retour.

Représentons la structure JSON ci-dessus à l'aide d'une classe Java:

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
}

StatusResponse est unenum défini comme ci-dessous:

public enum StatusResponse {
    SUCCESS ("Success"),
    ERROR ("Error");

    private String status;
    // constructors, getters
}

5. Implémentation des services RESTful

Maintenant, implémentons les routes et les gestionnaires pour notre API REST.

5.1. Création de contrôleurs

La classe Java suivante contient les routes de notre API, y compris les verbes et les chemins, ainsi qu'un aperçu des gestionnaires pour chaque 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) -> {
            //...
        });
    }
}

Nous montrerons la mise en œuvre complète de chaque gestionnaire d’itinéraires dans les sous-sections suivantes.

5.2. Ajouter un utilisateur

Vous trouverez ci-dessous le gestionnaire de réponse de la méthodepost qui ajoutera unUser:

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: Dans cet exemple, la représentation JSON de l'objetUser est transmise en tant que corps brut d'une requête POST.

Testons l'itinéraire:

Demande:

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

Réponse:

{
    "status":"SUCCESS"
}

5.3. Obtenez tous les utilisateurs

Vous trouverez ci-dessous le gestionnaire de réponses de la méthodeget qui renvoie tous les utilisateurs desUserService:

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

Maintenant, testons l'itinéraire:

Demande:

GET http://localhost:4567/users

Réponse:

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

5.4. Obtenir l'utilisateur par identifiant

Vous trouverez ci-dessous le gestionnaire de réponse de la méthodeget qui renvoie unUser avec leid donné:

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")))));
});

Maintenant, testons l'itinéraire:

Demande:

GET http://localhost:4567/users/1012

Réponse:

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

5.5. Modifier un utilisateur

Vous trouverez ci-dessous le gestionnaire de réponse de la méthodeput, qui édite l'utilisateur ayant lesid fournis dans le modèle d'itinéraire:

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: Dans cet exemple, les données sont passées dans le corps brut d'une requête POST en tant qu'objet JSON dont les noms de propriété correspondent aux champs de l'objetUser à éditer.

Testons l'itinéraire:

Demande:

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

Réponse:

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

5.6. Supprimer un utilisateur

Vous trouverez ci-dessous le gestionnaire de réponse de la méthodedelete, qui supprimera lesUser avec lesid donnés:

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

Maintenant, testons l'itinéraire:

Demande:

DELETE http://localhost:4567/users/1012

Réponse:

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

5.7. Vérifier si l'utilisateur existe

La méthodeoptions est un bon choix pour la vérification conditionnelle. Vous trouverez ci-dessous le gestionnaire de réponse de la méthodeoptions qui vérifiera si unUser avec leid donné existe:

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" ));
});

Maintenant, testons l'itinéraire:

Demande:

OPTIONS http://localhost:4567/users/1012

Réponse:

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

6. Conclusion

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

Ce framework est principalement promu pour générer des microservices en Java. Les développeurs deNode.js ayant des connaissances Java qui souhaitent exploiter des bibliothèques construites sur des bibliothèques JVM devraient se sentir chez eux en utilisant ce framework.

Et comme toujours, vous pouvez trouver toutes les sources de ce tutoriel dans lesGithub project.