Skapa ett API med Spark Java Framework

1. Introduktion

I den här artikeln kommer vi att få en snabb introduktion till Spark-ramverket. Spark framework är ett webbutvecklingssystem för snabb utveckling inspirerat av Sinatra-ramverket för Ruby och är uppbyggt kring Java 8 Lambda Expression-filosofi, vilket gör det mindre ordentligt än de flesta applikationer skrivna i andra Java-ramar.

Det är ett bra val om du vill ha en Node.js- liknande upplevelse när du utvecklar ett webb-API eller mikrotjänster i Java. Med Spark kan du ha ett REST API redo att servera JSON i mindre än tio rader kod.

Vi börjar snabbt med ett “Hello World” -exempel, följt av ett enkelt REST API.

2. Maven Beroenden

2.1. Spark Framework

Inkludera följande Maven-beroende i din pom.xml :

 com.sparkjava spark-core 2.5.4 

Du hittar den senaste versionen av Spark på Maven Central.

2.2. Gson-biblioteket

På olika ställen i exemplet kommer vi att använda Gson-biblioteket för JSON-operationer. För att inkludera Gson i ditt projekt, inkludera detta beroende i din pom.xml :

 com.google.code.gson gson 2.8.0 

Du hittar den senaste versionen av Gson på Maven Central.

3. Komma igång med Spark Framework

Låt oss ta en titt på de grundläggande byggstenarna i en Spark-applikation och visa en snabb webbtjänst.

3.1. Rutter

Webbtjänster i Spark Java bygger på rutter och deras hanterare. Rutter är viktiga element i Spark. Enligt dokumentationen består varje rutt av tre enkla bitar - ett verb , en väg och en återuppringning .

  1. Den verb är en metod som motsvarar till en HTTP-metod. Verbmetoderna inkluderar: få, posta, sätta, ta bort, head, spåra, ansluta och alternativ
  2. Den bana (även kallad en rutt mönster) bestämmer vilken URI (s) rutten bör lyssna till och ge en respons för
  3. Den återuppringning är en hanterarfunktion som anropas för en given verb och sökväg för att generera och returnera ett svar på HTTP-begäran motsvarande. En återuppringning tar ett begäranobjekt och svarobjekt som argument

Här visar vi grundstrukturen för en rutt som använder get verb:

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

3.2. Hello World API

Låt oss skapa en enkel webbtjänst som har två rutter för GET-förfrågningar och returnerar "Hej" -meddelanden som svar. Dessa rutter använder get -metoden, som är en statisk import från klassen spark.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"); }); } }

Det första argumentet för get- metoden är vägen för rutten. Den första rutten innehåller en statisk sökväg som endast representerar en enda URI ( “/ hej” ).

Den andra ruttens sökväg ( “/ hej /: namn” ) innehåller en platshållare för parametern “namn” , som anges genom att parametern föregås av ett kolon (“:”). Denna rutt kommer att åberopas som svar på GET-förfrågningar till URI som "/ hej / Joe" och "/ hej / Mary" .

Det andra argumentet för get- metoden är ett lambdauttryck som ger en funktionell programmeringssmak till detta ramverk.

Lambdauttrycket har begäran och svaret som argument och hjälper till att returnera svaret. Vi lägger vår kontrollerlogik i lambdauttrycket för REST API-rutterna, som vi kommer att se senare i den här handledningen.

3.3. Testar Hello World API

Efter att ha kört klassen HelloWorldService som en vanlig Java-klass kommer du att kunna komma åt tjänsten på dess standardport 4567 med de rutter som definierats med get- metoden ovan.

Låt oss titta på begäran och svaret för den första rutten:

Begäran:

GET //localhost:4567/hello

Svar:

Hello, world

Låt oss testa den andra vägen, passerar namn parameter i sin väg:

Begäran:

GET //localhost:4567/hello/baeldung

Svar:

Hello, baeldung

Se hur placeringen av texten "baeldung" i URI användes för att matcha ruttmönstret "/ hej /: namn" - vilket gör att den andra ruttens återuppringningshanterarfunktion åberopas.

4. Designa en RESTful Service

I detta avsnitt kommer vi att utforma en enkel REST webbtjänst för följande Användare enhet:

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

4.1. Rutter

Låt oss lista de rutter som utgör vårt API:

  • GET / användare - få lista över alla användare
  • GET / användare /: id - få användare med angivet id
  • POST / användare /: id - lägg till en användare
  • PUT / användare /: id - redigera en viss användare
  • ALTERNATIV / användare /: id - kontrollera om en användare existerar med angivet id
  • RADERA / användare /: id - radera en viss användare

4.2. Användartjänsten

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

I den här artikeln hade vi en snabb introduktion till Spark-ramverket för snabb webbutveckling.

Detta ramverk främjas främst för att generera mikrotjänster i Java. Node.js- utvecklare med Java-kunskap som vill utnyttja bibliotek byggda på JVM-bibliotek ska känna sig hemma med hjälp av detta ramverk.

Och som alltid kan du hitta alla källor för den här guiden i Github-projektet.