知識庫 / REST RSS 訂閱

Java中使用Play Framework構建REST API

REST
HongKong
5
03:55 AM · Dec 06 ,2025

1. 概述

本教程旨在探索 Play 框架並學習如何使用 Java 構建 RESTful 服務。

我們將構建一個 REST API,用於創建、檢索、更新和刪除學生記錄。

在諸如此類應用程序中,通常會有數據庫來存儲學生記錄。Play 框架內置了 H2 數據庫,並支持與 JPA 和 Hibernate 等持久化框架的集成。

為了保持簡單並專注於最重要的內容,我們將使用簡單的映射來存儲學生對象,並帶有唯一的 ID。

2. 創建新應用程序

安裝了 Play Framework,如介紹中所述,我們現在可以創建我們的應用程序。

使用 <em sbt</em> 命令,創建一個名為 <em student-api</em> 的新應用程序,並使用 <em play-java-seed</em>

sbt new playframework/play-java-seed.g8

3. 模型

有了我們的應用程序腳手架搭建完成,請導航到 student-api/app/models 並創建一個 Java Bean,用於處理學生信息:

public class Student {
    private String firstName;
    private String lastName;
    private int age;
    private int id;

    // standard constructors, getters and setters
}

我們現在將創建一個簡單的存儲數據 – 採用 HashMap – 用於學生數據,並提供輔助方法來執行 CRUD 操作:

public class StudentStore {
    private Map<Integer, Student> students = new HashMap<>();

    public Optional<Student> addStudent(Student student) {
        int id = students.size();
        student.setId(id);
        students.put(id, student);
        return Optional.ofNullable(student);
    }

    public Optional<Student> getStudent(int id) {
        return Optional.ofNullable(students.get(id));
    }

    public Set<Student> getAllStudents() {
        return new HashSet<>(students.values());
    }

    public Optional<Student> updateStudent(Student student) {
        int id = student.getId();
        if (students.containsKey(id)) {
            students.put(id, student);
            return Optional.ofNullable(student);
        }
        return null;
    }

    public boolean deleteStudent(int id) {
        return students.remove(id) != null;
    }
}

4. 控制器

讓我們前往 student-api/app/controllers 並創建一個名為 StudentController.java 的新控制器。 我們將逐步通過代碼進行分析。

首先,我們需要配置 HttpExecutionContext. 我們將使用異步、非阻塞的代碼實現我們的操作。 這意味着我們的操作方法將返回 CompletionStage<Result>  而不是僅僅返回 Result。 這有益於我們能夠編寫長時間運行的任務,而無需阻塞。

在處理 Play Framework 控制器中的異步編程時,有一個需要注意的限制:我們必須提供 HttpExecutionContext。 如果我們沒有提供 HTTP 執行上下文,在調用操作方法時,將會收到“此處沒有可用的 HTTP 上下文”的臭名昭著的錯誤。

讓我們注入它:

private HttpExecutionContext ec;
private StudentStore studentStore;

@Inject
public StudentController(HttpExecutionContext ec, StudentStore studentStore) {
    this.studentStore = studentStore;
    this.ec = ec;
}

請注意,我們還添加了 StudentStore 並使用 @Inject 註解在控制器構造函數中注入了這兩個字段。 經過此操作,我們現在可以繼續實現 action 方法的實現。

請注意, Play 附帶 Jackson 以允許進行數據處理 – 因此,我們可以導入我們需要的任何 Jackson 類,而無需外部依賴。

讓我們定義一個實用類來執行重複操作。 在這種情況下,構建 HTTP 響應。

因此,讓我們創建 student-api/app/utils 包並在此處添加 Util.java 

public class Util {
    public static ObjectNode createResponse(Object response, boolean ok) {
        ObjectNode result = Json.newObject();
        result.put("isSuccessful", ok);
        if (response instanceof String) {
            result.put("body", (String) response);
        } else {
            result.putPOJO("body", response);
        }
        return result;
    }
}

使用這種方法,我們將創建標準的 JSON 響應,其中包含一個布爾值 isSuccessful 鍵以及響應體。

現在我們可以逐步跟蹤控制器類的操作。

4.1. 創建操作

映射為 操作,此方法處理 對象的創建:

public CompletionStage<Result> create(Http.Request request) {
    JsonNode json = request.body().asJson();
    return supplyAsync(() -> {
        if (json == null) {
            return badRequest(Util.createResponse("Expecting Json data", false));
        }

        Optional<Student> studentOptional = studentStore.addStudent(Json.fromJson(json, Student.class));
        return studentOptional.map(student -> {
            JsonNode jsonObject = Json.toJson(student);
            return created(Util.createResponse(jsonObject, true));
        }).orElse(internalServerError(Util.createResponse("Could not create data.", false)));
    }, ec.current());
}

我們使用注入的 <em>Http.Request</em> 類獲取請求體,並將其轉換為 Jackson 的 <em>JsonNode</em> 類。請注意,我們使用該實用方法來創建響應,如果體為空值 <em>null</em> 時,會創建響應。

我們還返回一個 <em>CompletionStage&lt;Result&gt;</em> 反應,這使得我們能夠使用CompletedFuture.supplyAsync` 方法編寫非阻塞代碼。

我們可以將任何 <em>String</em><em>JsonNode</em> 傳遞給它,以及一個 <em>boolean</em> 標誌,用於指示狀態。

請注意,我們使用 <em>Json.fromJson()</em> 將傳入的 JSON 對象轉換為 <em>Student</em> 對象,並將結果轉換為 JSON 以供響應使用。

最後,我們不再使用 <em>ok()</em> 方法,而是使用 <em>play.mvc.results</em> 包中的 <em>created</em> 輔助方法。 這種方法的想法是使用一個方法,該方法為特定上下文中執行的動作提供正確的 HTTP 狀態。 例如,對於 HTTP OK 200 狀態,使用 <em>ok()</em> ;對於 HTTP CREATED 201 狀態(如上所示),使用created()。 這種概念將在後續操作中出現。

4.2. 更新操作

當對 HTTP 請求(PUT 請求)發送到 http://localhost:9000/</em/> 時,它會觸發 StudentController</em/> 中的 update</em/> 方法,該方法通過調用 updateStudent</em/> 方法來更新 StudentStore</em/> 中的學生信息。

public CompletionStage<Result> update(Http.Request request) {
    JsonNode json = request.body().asJson();
    return supplyAsync(() -> {
        if (json == null) {
            return badRequest(Util.createResponse("Expecting Json data", false));
        }
        Optional<Student> studentOptional = studentStore.updateStudent(Json.fromJson(json, Student.class));
        return studentOptional.map(student -> {
            if (student == null) {
                return notFound(Util.createResponse("Student not found", false));
            }
            JsonNode jsonObject = Json.toJson(student);
            return ok(Util.createResponse(jsonObject, true));
        }).orElse(internalServerError(Util.createResponse("Could not create data.", false)));
    }, ec.current());
}

4.3. 檢索操作

為了檢索一名學生,我們將學生的 ID 作為路徑參數傳遞到 GET 請求中,請求地址為 http://localhost:9000/:id。 這將觸發 檢索操作:

public CompletionStage<Result> retrieve(int id) {
    return supplyAsync(() -> {
        final Optional<Student> studentOptional = studentStore.getStudent(id);
        return studentOptional.map(student -> {
            JsonNode jsonObjects = Json.toJson(student);
            return ok(Util.createResponse(jsonObjects, true));
        }).orElse(notFound(Util.createResponse("Student with id:" + id + " not found", false)));
    }, ec.current());
}

4.4. 刪除操作

“刪除”操作映射到 http://localhost:9000/:id。 我們通過提供 id 來標識要刪除的記錄:

public CompletionStage<Result> delete(int id) {
    return supplyAsync(() -> {
        boolean status = studentStore.deleteStudent(id);
        if (!status) {
            return notFound(Util.createResponse("Student with id:" + id + " not found", false));
        }
        return ok(Util.createResponse("Student with id:" + id + " deleted", true));
    }, ec.current());
}

4.5. listStudents 操作

最後,listStudents 操作返回迄今為止存儲的所有學生的列表。它被映射到 http://localhost:9000/ 作為一個 GET 請求:

public CompletionStage<Result> listStudents() {
    return supplyAsync(() -> {
        Set<Student> result = studentStore.getAllStudents();
        ObjectMapper mapper = new ObjectMapper();
        JsonNode jsonData = mapper.convertValue(result, JsonNode.class);
        return ok(Util.createResponse(jsonData, true));
    }, ec.current());
}

5. 映射

在配置好我們的控制器動作之後,我們可以通過打開文件 student-api/conf/routes 並添加以下路由來映射它們:

GET     /                           controllers.StudentController.listStudents()
GET     /:id                        controllers.StudentController.retrieve(id:Int)
POST    /                           controllers.StudentController.create(request: Request)
PUT     /                           controllers.StudentController.update(request: Request)
DELETE  /:id                        controllers.StudentController.delete(id:Int)
GET     /assets/*file               controllers.Assets.versioned(path="/public", file: Asset)

必須始終存在/assets 端點才能下載靜態資源。

之後,我們完成了Student API 的構建。

要了解更多關於定義路由映射的信息,請訪問我們的“Play 應用程序中的路由”教程。

6. 測試

我們可以通過向 http://localhost:9000/ 發送請求來對我們的 API 進行測試,並添加適當的上下文。 從瀏覽器中運行基本路徑應會輸出:

{
     "isSuccessful":true,
     "body":[]
}

由於我們尚未添加任何記錄,我們可以看到身體為空。使用 curl,讓我們進行一些測試(或者我們可以使用像 Postman 這樣的 REST 客户端)。

讓我們打開一個終端窗口並執行 curl 命令以 添加一名學生

curl -X POST -H "Content-Type: application/json" \
 -d '{"firstName":"John","lastName":"Baeldung","age": 18}' \
 http://localhost:9000/

這將返回新創建的學生。

{ 
    "isSuccessful":true,
    "body":{ 
        "firstName":"John",
        "lastName":"Baeldung",
        "age":18,
        "id":0
    }
}

在運行上述測試後,從瀏覽器中加載 http://localhost:9000 應該會得到:

{ 
    "isSuccessful":true,
    "body":[ 
        { 
            "firstName":"John",
            "lastName":"Baeldung",
            "age":18,
            "id":0
        }
    ]
}

id 屬性將在每次添加新記錄時遞增。

刪除記錄,我們發送一個DELETE 請求:

curl -X DELETE http://localhost:9000/0
{ 
    "isSuccessful":true,
    "body":"Student with id:0 deleted"
}

在上述測試中,我們刪除了第一個測試中創建的記錄,現在我們來再次創建它,以便測試更新方法

curl -X POST -H "Content-Type: application/json" \
-d '{"firstName":"John","lastName":"Baeldung","age": 18}' \
http://localhost:9000/
{ 
    "isSuccessful":true,
    "body":{ 
        "firstName":"John",
        "lastName":"Baeldung",
        "age":18,
        "id":0
    }
}

現在,讓我們通過將名字(first name)設置為“Andrew”和年齡設置為30來更新記錄:

curl -X PUT -H "Content-Type: application/json" \
-d '{"firstName":"Andrew","lastName":"Baeldung","age": 30,"id":0}' \
http://localhost:9000/
{ 
    "isSuccessful":true,
    "body":{ 
        "firstName":"Andrew",
        "lastName":"Baeldung",
        "age":30,
        "id":0
    }
}

上述測試演示了在更新記錄後,firstName age 字段值的變化。

讓我們創建一些額外的佔位記錄,我們將添加兩個:John Doe 和 Sam Baeldung:

curl -X POST -H "Content-Type: application/json" \
-d '{"firstName":"John","lastName":"Doe","age": 18}' \
http://localhost:9000/
curl -X POST -H "Content-Type: application/json" \
-d '{"firstName":"Sam","lastName":"Baeldung","age": 25}' \
http://localhost:9000/

現在,我們來獲取所有記錄:

curl -X GET http://localhost:9000/
{ 
    "isSuccessful":true,
    "body":[ 
        { 
            "firstName":"Andrew",
            "lastName":"Baeldung",
            "age":30,
            "id":0
        },
        { 
            "firstName":"John",
            "lastName":"Doe",
            "age":18,
            "id":1
        },
        { 
            "firstName":"Sam",
            "lastName":"Baeldung",
            "age":25,
            "id":2
        }
    ]
}

通過以上測試,我們正在確認 listStudents 控制器方法的正確運行。

7. 結論

在本文中,我們展示瞭如何使用 Play 框架構建一個完整的 REST API。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.