Spark入门:也可以用Java创建轻量级的RESTful应用程序
最近,我一直在使用Spark (一種Java的Web框架,與Apache Spark 不相關)編寫RESTful服務。 當我們計劃寫這篇文章時,我已經做好了不可避免的接口,樣板代碼和深層層次結構的Java風格的準備。 我很驚訝地發現,對于局限于Java的開發人員來說,還存在一個替代世界。
在本文中,我們將了解如何使用JSON傳輸數據來為博客構建RESTful應用程序。 我們會看到:
- 如何在Spark中創建一個簡單的Hello World
- 如何指定請求中期望的JSON對象的布局
- 如何發送帖子請求以創建新帖子
- 如何發送獲取請求以檢索帖子列表
我們不會看到如何在數據庫中插入該數據。 我們只將列表保留在內存中(在我的實際服務中,我一直在使用sql2o )。
一些依賴
我們將使用Maven,因此我將首先創建一個新的pom.xml并添加一些內容。 基本上:
- 火花
- 杰克遜
- Lombok
- 番石榴
- Easymock(僅在測試中使用,本文中未介紹)
- 格森
火花你好世界
你有這一切嗎? 太酷了,然后編寫一些代碼。
package me.tomassetti;import static spark.Spark.get; import static spark.Spark.post; import spark.Request; import spark.Response; import spark.Route;public class BlogService {public static void main( String[] args) {get("/posts", (req, res) -> {return "Hello Sparkingly World!";});} }現在,我們可以使用以下命令運行它:
mvn compile && mvn exec:java讓我們打開瀏覽器并訪問localhost http:// localhost:4567 / posts 。 在這里我們要做一個簡單的獲取。 對于執行帖子,您可能想要在瀏覽器中使用Postman插件,或者只運行curl 。 一切為您服務。
使用Jackson和Lombok進行很棒的描述性交換對象
在典型的RESTful應用程序中,我們希望接收帶有json對象的POST請求作為有效負載的一部分。 我們的工作將是檢查代碼是否為格式正確的JSON,是否與預期的結構相對應,值是否在有效范圍內,等等。無聊且重復。 我們可以用不同的方式做到這一點。 最基本的一種是使用gson :
JsonParser parser = new JsonParser(); JsonElement responseData = parser.parse(response); if (!responseData.isJsonObject()){// send an error like: "Hey, you did not pass an Object! } JsonObject obj = responseData.getAsJsonObject(); if (!obj.hasField("title")){// send an error like: "Hey, we were expecting a field name title! } JsonElement titleAsElem = obj.get("title"); if (!titleAsElem.isString()){// send an error like: "Hey, title is not an string! } // etc, etc, etc我們可能不想這樣做。
指定我們期望的結構的更具聲明性的方法是創建特定的類。
class NewPostPayload {private String title;private List<String> categories;private String content;public String getTitle() { ... }public void setTitle(String title) { ... }public List<String> getCategories() { ... }public void setCategories(List<String> categories){ ... }public String getContent() { ... }public void setContent(String content) { ... } }然后我們可以使用Jackson:
try {ObjectMapper mapper = new ObjectMapper();NewPostPayload newPost = mapper.readValue(request.body(), NewPostPayload.class); } catch (JsonParseException e){// Hey, you did not send a valid request! }這樣,杰克遜會自動為我們檢查有效載荷是否具有預期的結構。 我們可能想驗證是否遵守其他約束。 例如,我們可能要檢查標題是否為空,并且至少指定了一個類別。 我們可以創建一個僅用于驗證的接口:
interface Validable {boolean isValid(); }class NewPostPayload implements Validable {private String title;private List<String> categories;private String content;public String getTitle() { ... }public void setTitle(String title) { ... }public List<String> getCategories() { ... }public void setCategories(List<String> categories){ ... }public String getContent() { ... }public void setContent(String content) { ... }public boolean isValid() {return title != null && !title.isEmpty() && !categories.isEmpty();} }仍然有很多無聊的getter和setter方法。 它們的信息量不是很大,只會污染代碼。 我們可以使用Lombok擺脫它們。 Lombok是一個注釋處理器,可以為您添加重復方法(getter,setter,equals,hashCode等)。 您可以將其視為編譯器的插件,該插件可查找注釋(例如@Data )并基于注釋生成方法。 如果將其添加到依賴項中,maven會很好,但是您的IDE無法自動完成Lombok添加的方法。 您可能要安裝插件。 對于Intellij Idea,我使用的是Lombok插件版本0.9.1,它的效果很好。
現在,您可以將類NewPostPayload修改為:
@Data class NewPostPayload {private String title;private List<String> categories;private String content;public boolean isValid() {return title != null && !title.isEmpty() && !categories.isEmpty();} }好多了,是嗎?
一個完整的例子
我們基本上需要做兩件事:
第一個操作應實現為POST(具有副作用),而第二個操作應實現為GET。 它們都對posts集合進行操作,因此我們將使用端點/ posts 。
讓我們從插入帖子開始。 首先我們要解析
// insert a post (using HTTP post method)post("/posts", (request, response) -> {try {ObjectMapper mapper = new ObjectMapper();NewPostPayload creation = mapper.readValue(request.body(), NewPostPayload.class);if (!creation.isValid()) {response.status(HTTP_BAD_REQUEST);return "";}int id = model.createPost(creation.getTitle(), creation.getContent(), creation.getCategories());response.status(200);response.type("application/json");return id;} catch (JsonParseException jpe) {response.status(HTTP_BAD_REQUEST);return "";}});然后查看如何檢索所有帖子:
// get all post (using HTTP get method)get("/posts", (request, response) -> {response.status(200);response.type("application/json");return dataToJson(model.getAllPosts());});最后的代碼是:
package me.tomassetti;import static spark.Spark.get; import static spark.Spark.post;import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import lombok.Data; import spark.Request; import spark.Response; import spark.Route;import java.io.IOException; import java.io.StringWriter; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.stream.Collector; import java.util.stream.Collectors;public class BlogService {private static final int HTTP_BAD_REQUEST = 400;interface Validable {boolean isValid();}@Datastatic class NewPostPayload {private String title;private List<String> categories = new LinkedList<>();private String content;public boolean isValid() {return title != null && !title.isEmpty() && !categories.isEmpty();}}// In a real application you may want to use a DB, for this example we just store the posts in memorypublic static class Model {private int nextId = 1;private Map<Integer, Post> posts = new HashMap<>();@Dataclass Post {private int id;private String title;private List<String> categories;private String content;}public int createPost(String title, String content, List<String> categories){int id = nextId++;Post post = new Post();post.setId(id);post.setTitle(title);post.setContent(content);post.setCategories(categories);posts.put(id, post);return id;}public List<Post> getAllPosts(){return posts.keySet().stream().sorted().map((id) -> posts.get(id)).collect(Collectors.toList());}}public static String dataToJson(Object data) {try {ObjectMapper mapper = new ObjectMapper();mapper.enable(SerializationFeature.INDENT_OUTPUT);StringWriter sw = new StringWriter();mapper.writeValue(sw, data);return sw.toString();} catch (IOException e){throw new RuntimeException("IOException from a StringWriter?");}}public static void main( String[] args) {Model model = new Model();// insert a post (using HTTP post method)post("/posts", (request, response) -> {try {ObjectMapper mapper = new ObjectMapper();NewPostPayload creation = mapper.readValue(request.body(), NewPostPayload.class);if (!creation.isValid()) {response.status(HTTP_BAD_REQUEST);return "";}int id = model.createPost(creation.getTitle(), creation.getContent(), creation.getCategories());response.status(200);response.type("application/json");return id;} catch (JsonParseException jpe) {response.status(HTTP_BAD_REQUEST);return "";}});// get all post (using HTTP get method)get("/posts", (request, response) -> {response.status(200);response.type("application/json");return dataToJson(model.getAllPosts());});} }使用PostMan嘗試應用程序
如果您更喜歡命令行,則可能要改用curl。 我喜歡不必轉義JSON并擁有基本的編輯器,因此可以使用PostMan(Chrome插件)。
讓我們插入一個帖子。 我們將所有字段指定為插入到請求正文中的Json對象的一部分。 我們獲取創建的帖子的ID。
然后,我們可以獲得帖子列表。 在這種情況下,我們使用GET(請求中沒有正文),并獲取所有帖子的數據(僅是我們在上面插入的帖子)。
結論
我不得不說,我對該項目感到非常驚訝。 我已經準備好了變得更糟:這是一種需要基本邏輯和大量管道的應用程序。 我發現Python,Clojure和Ruby在解決此類問題方面都做得很好,而當我用Java編寫簡單的Web應用程序時,邏輯就被樣板代碼淹沒了。 好吧,事情可能會有所不同。 Spark,Lombok,Jackson和Java 8的結合確實很誘人。 我非常感謝這些軟件的作者,他們確實在改善Java開發人員的生活。 我認為這也是一個教訓:出色的框架可以經常改進很多事情,而這超出了我們的想象。
編輯:我從reddit的好人那里收到了一個改進示例的建議。 謝謝! 請保持良好的建議來!
翻譯自: https://www.javacodegeeks.com/2015/08/getting-started-with-spark-it-is-possible-to-create-lightweight-restful-application-also-in-java.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Spark入门:也可以用Java创建轻量级的RESTful应用程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在Mac上创建桌面快捷方式电脑如何创
- 下一篇: 如何使用Java 8函数式编程生成字母序