spark restful_Spark入门:也可以用Java创建轻量级的RESTful应用程序
spark restful
最近,我一直在使用Spark (一種Java的Web框架,與Apache Spark 不相關(guān))編寫RESTful服務(wù)。 當(dāng)我們計(jì)劃寫這篇文章時(shí),我已經(jīng)做好了不可避免的接口,樣板代碼和深層層次結(jié)構(gòu)的Java風(fēng)格的準(zhǔn)備。 令我驚訝的是,對于局限于Java的開發(fā)人員來說,還有一個(gè)替代世界。
在本文中,我們將了解如何使用JSON傳輸數(shù)據(jù)來為博客構(gòu)建RESTful應(yīng)用程序。 我們會(huì)看到:
- 如何在Spark中創(chuàng)建一個(gè)簡單的Hello World
- 如何指定請求中期望的JSON對象的布局
- 如何發(fā)送帖子請求以創(chuàng)建新帖子
- 如何發(fā)送獲取請求以檢索帖子列表
我們不會(huì)看到如何在數(shù)據(jù)庫中插入此數(shù)據(jù)。 我們只將列表保留在內(nèi)存中(在我的實(shí)際服務(wù)中,我一直在使用sql2o )。
一些依賴
我們將使用Maven,因此我將首先創(chuàng)建一個(gè)新的pom.xml并添加一些內(nèi)容。 基本上:
- 火花
- 杰克遜
- 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!";});} }現(xiàn)在,我們可以使用以下命令運(yùn)行它:
mvn compile && mvn exec:java讓我們打開瀏覽器并訪問localhost http:// localhost:4567 / posts 。 在這里我們要做一個(gè)簡單的獲取。 對于執(zhí)行帖子,您可能需要在瀏覽器中使用Postman插件或只運(yùn)行curl 。 一切為您服務(wù)。
使用Jackson和Lombok進(jìn)行很棒的描述性交換對象
在一個(gè)典型的RESTful應(yīng)用程序中,我們希望接收帶有json對象的POST請求作為有效負(fù)載的一部分。 我們的工作將是檢查代碼是否為格式正確的JSON,是否與預(yù)期的結(jié)構(gòu)相對應(yīng),值是否在有效范圍內(nèi),等等。無聊且重復(fù)。 我們可以用不同的方式做到這一點(diǎn)。 最基本的一種是使用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我們可能不想這樣做。
指定我們期望的結(jié)構(gòu)的更具聲明性的方法是創(chuàng)建特定的類。
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! }這樣,杰克遜會(huì)自動(dòng)為我們檢查有效載荷是否具有預(yù)期的結(jié)構(gòu)。 我們可能想驗(yàn)證是否遵守其他約束。 例如,我們可能要檢查標(biāo)題是否為空,并且至少指定了一個(gè)類別。 我們可以創(chuàng)建一個(gè)僅用于驗(yàn)證的接口:
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方法。 它們的信息量不是很大,只會(huì)污染代碼。 我們可以使用Lombok擺脫它們。 Lombok是一個(gè)注釋處理器,可以為您添加重復(fù)方法(getter,setter,equals,hashCode等)。 您可以將其視為編譯器的插件,該插件可查找注釋(例如@Data )并基于注釋生成方法。 如果將其添加到依賴項(xiàng)中,maven會(huì)很好,但是您的IDE無法為Lombok添加的方法自動(dòng)完成。 您可能要安裝插件。 對于Intellij Idea,我使用的是Lombok插件版本0.9.1,它的效果很好。
現(xiàn)在,您可以將類NewPostPayload修改為:
@Data class NewPostPayload {private String title;private List<String> categories;private String content;public boolean isValid() {return title != null && !title.isEmpty() && !categories.isEmpty();} }好多了,是嗎?
一個(gè)完整的例子
我們基本上需要做兩件事:
第一個(gè)操作應(yīng)實(shí)現(xiàn)為POST(具有副作用),而第二個(gè)操作應(yīng)實(shí)現(xiàn)為GET。 它們都對posts集合進(jìn)行操作,因此我們將使用端點(diǎn)/ 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嘗試應(yīng)用程序
如果您更喜歡命令行,則可能要改用curl。 我喜歡不必轉(zhuǎn)義JSON和使用基本的編輯器,因此可以使用PostMan(Chrome插件)。
讓我們插入一個(gè)帖子。 我們將所有字段指定為插入請求主體中的Json對象的一部分。 我們獲取創(chuàng)建的帖子的ID。
然后,我們可以獲得帖子列表。 在這種情況下,我們使用GET(請求中沒有正文),并獲取所有帖子的數(shù)據(jù)(僅是我們在上面插入的帖子)。
結(jié)論
我不得不說,我對該項(xiàng)目感到非常驚訝。 我已經(jīng)準(zhǔn)備好了變得更糟:這是一種需要基本邏輯和大量管道的應(yīng)用程序。 我發(fā)現(xiàn)Python,Clojure和Ruby在解決這類問題上都做得很好,而當(dāng)我用Java編寫簡單的Web應(yīng)用程序時(shí),邏輯就被樣板代碼淹沒了。 好吧,事情可能會(huì)有所不同。 Spark,Lombok,Jackson和Java 8的結(jié)合確實(shí)很誘人。 我非常感謝這些軟件的作者,他們確實(shí)在改善Java開發(fā)人員的生活。 我認(rèn)為這也是一個(gè)教訓(xùn):出色的框架通常可以使事情比我們想象的要好得多。
編輯:我從reddit的好人那里收到了一個(gè)改進(jìn)示例的建議。 謝謝! 請保持良好的建議來!
翻譯自: https://www.javacodegeeks.com/2015/08/getting-started-with-spark-it-is-possible-to-create-lightweight-restful-application-also-in-java.html
spark restful
總結(jié)
以上是生活随笔為你收集整理的spark restful_Spark入门:也可以用Java创建轻量级的RESTful应用程序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 七彩虹推出 MEOW 系列 DDR5-6
- 下一篇: 吉利发布汽车座舱云原生技术:应用部署云端