在現(xiàn)在的開發(fā)流程中夫偶,為了最大程度實(shí)現(xiàn)前后端的分離,通常后端接口只提供數(shù)據(jù)接口岛请,由前端通過(guò)Ajax請(qǐng)求從后端獲取數(shù)據(jù)并進(jìn)行渲染再展示給用戶沫浆。我們用的最多的方式就是后端會(huì)返回給前端一個(gè)JSON字符串,前端解析JSON字符串生成JavaScript的對(duì)象斩萌,然后再做處理缝裤。
本文就來(lái)演示一下Spring boot如何實(shí)現(xiàn)這種模式,本文重點(diǎn)會(huì)講解如何設(shè)計(jì)一個(gè)Restful的API颊郎,并通過(guò)Spring boot來(lái)實(shí)現(xiàn)相關(guān)的API。不過(guò)霎苗,為了大家更好的了解Restful風(fēng)格的API姆吭,我們先設(shè)計(jì)一個(gè)傳統(tǒng)的數(shù)據(jù)返回接口,這樣大家可以對(duì)比著來(lái)理解唁盏。
一内狸、非Restful接口的支持
我們這里以文章列表為例,實(shí)現(xiàn)一個(gè)返回文章列表的接口厘擂,代碼如下:
@Controller
@RequestMapping("/article")
public class ArticleController {
? ?@Autowired
? ?private ArticleService articleService;
? ?@RequestMapping("/list.json")
? ?@ResponseBody
? ?public List<Article> listArticles(String title, Integer pageSize, Integer pageNum) {
? ? ? ?if (pageSize == null) {
? ? ? ? ? ?pageSize = 10;
? ? ? ?}
? ? ? ?if (pageNum == null) {
? ? ? ? ? ?pageNum = 1;
? ? ? ?}
? ? ? ?int offset = (pageNum - 1) * pageSize;
? ? ? ?return articleService.getArticles(title, 1L, offset, pageSize);
? ?}
}
這個(gè)ArticleService的實(shí)現(xiàn)很簡(jiǎn)單昆淡,就是簡(jiǎn)單的封裝了ArticleMapper的操作,ArticleMapper的內(nèi)容大家可以參考上一篇的文章刽严,ArticleService的實(shí)現(xiàn)類如下:
@Service
public class ArticleServiceImpl implements ArticleService {
? ?@Autowired
? ?private ArticleMapper articleMapper;
? ?@Override
? ?public Long saveArticle(@RequestBody Article article) {
? ? ? ?return articleMapper.insertArticle(article);
? ?}
? ?@Override
? ?public List<Article> getArticles(String title,Long userId,int offset,int pageSize) {
? ? ? ?Article article = new Article();
? ? ? ?article.setTitle(title);
? ? ? ?article.setUserId(userId);
? ? ? ?return articleMapper.queryArticlesByPage(article,offset,pageSize);
? ?}
? ?@Override
? ?public Article getById(Long id) {
? ? ? ?return articleMapper.queryById(id);
? ?}
? ?@Override
? ?public void updateArticle(Article article) {
? ? ? ?article.setUpdateTime(new Date());
? ? ? ?articleMapper.updateArticleById(article);
? ?}
}
運(yùn)行Application.java這個(gè)類昂灵,然后訪問(wèn):http://locahost:8080/article/list.json,就可以看到如下的結(jié)果:
ArticleServiceImpl這個(gè)類是一個(gè)很普通的類,只有一個(gè)Spring的注解@Service眨补,標(biāo)識(shí)為一個(gè)bean以便于通過(guò)Spring IoC容器來(lái)管理管削。我們?cè)賮?lái)看看ArticleController這個(gè)類,其實(shí)用過(guò)Spring MVC的人應(yīng)該都熟悉這幾個(gè)注解撑螺,這里簡(jiǎn)單解釋一下:
@Controller 標(biāo)識(shí)一個(gè)類為控制器含思。
@RequestMapping URL的映射。
@ResponseBody 返回結(jié)果轉(zhuǎn)換為JSON字符串甘晤。
@RequestBody 表示接收J(rèn)SON格式字符串參數(shù)含潘。
通過(guò)這個(gè)三個(gè)注解,我們就能輕松的實(shí)現(xiàn)通過(guò)URL給前端返回JSON格式數(shù)據(jù)的功能线婚。不過(guò)大家肯定有點(diǎn)疑惑调鬓,這不都是Spring MVC的東西嗎?跟Spring boot有什么關(guān)系酌伊?其實(shí)Spring boot的作用就是為我們省去了配置的過(guò)程腾窝,其他功能確實(shí)都是Spring與Spring MVC來(lái)為我們提供的,大家應(yīng)該記得Spring boot通過(guò)各種starter來(lái)為我們提供自動(dòng)配置的服務(wù)居砖,我們的工程里面之前引入過(guò)這個(gè)依賴:
<dependency>
? ? ?<groupId>org.springframework.boot</groupId>
? ? ?<artifactId>spring-boot-starter-web</artifactId>
</dependency>
這個(gè)是所有Spring boot的web工程都需要引入的jar包虹脯,也就是說(shuō)只要是Spring boot的web的工程,都默認(rèn)支持上述的功能奏候。這里我們進(jìn)一步發(fā)現(xiàn)循集,通過(guò)Spring boot來(lái)開發(fā)web工程,確實(shí)為我們省了許多配置的工作蔗草。
二咒彤、Restful API設(shè)計(jì)
好了,我們現(xiàn)在再來(lái)看看如何實(shí)現(xiàn)Restful API咒精。實(shí)際上Restful本身不是一項(xiàng)什么高深的技術(shù)镶柱,而只是一種編程風(fēng)格,或者說(shuō)是一種設(shè)計(jì)風(fēng)格模叙。在傳統(tǒng)的http接口設(shè)計(jì)中歇拆,我們一般只使用了get和post兩個(gè)方法,然后用我們自己定義的詞匯來(lái)表示不同的操作范咨,比如上面查詢文章的接口故觅,我們定義了article/list.json來(lái)表示查詢文章列表,可以通過(guò)get或者post方法來(lái)訪問(wèn)渠啊。
而Restful API的設(shè)計(jì)則通過(guò)HTTP的方法來(lái)表示CRUD相關(guān)的操作输吏。因此,除了get和post方法外替蛉,還會(huì)用到其他的HTTP方法贯溅,如PUT拄氯、DELETE、HEAD等盗迟,通過(guò)不同的HTTP方法來(lái)表示不同含義的操作坤邪。下面是我設(shè)計(jì)的一組對(duì)文章的增刪改查的Restful API:
這里可以看出,URL僅僅是標(biāo)識(shí)資源的路勁罚缕,而具體的行為由HTTP方法來(lái)指定艇纺。
三、Restful API實(shí)現(xiàn)
現(xiàn)在我們?cè)賮?lái)看看如何實(shí)現(xiàn)上面的接口邮弹,其他就不多說(shuō)黔衡,直接看代碼:
@RestController
@RequestMapping("/rest")
public class ArticleRestController {
? ?@Autowired
? ?private ArticleService articleService;
? ?@RequestMapping(value = "/article", method = POST, produces = "application/json")
? ?public WebResponse<Map<String, Object>> saveArticle(@RequestBody Article article) {
? ? ? ?article.setUserId(1L);
? ? ? ?articleService.saveArticle(article);
? ? ? ?Map<String, Object> ret = new HashMap<>();
? ? ? ?ret.put("id", article.getId());
? ? ? ?WebResponse<Map<String, Object>> response = WebResponse.getSuccessResponse(ret);
? ? ? ?return response;
? ?}
? ?@RequestMapping(value = "/article/{id}", method = DELETE, produces = "application/json")
? ?public WebResponse<?> deleteArticle(@PathVariable Long id) {
? ? ? ?Article article = articleService.getById(id);
? ? ? ?article.setStatus(-1);
? ? ? ?articleService.updateArticle(article);
? ? ? ?WebResponse<Object> response = WebResponse.getSuccessResponse(null);
? ? ? ?return response;
? ?}
? ?@RequestMapping(value = "/article/{id}", method = PUT, produces = "application/json")
? ?public WebResponse<Object> updateArticle(@PathVariable Long id, @RequestBody Article article) {
? ? ? ?article.setId(id);
? ? ? ?articleService.updateArticle(article);
? ? ? ?WebResponse<Object> response = WebResponse.getSuccessResponse(null);
? ? ? ?return response;
? ?}
? ?@RequestMapping(value = "/article/{id}", method = GET, produces = "application/json")
? ?public WebResponse<Article> getArticle(@PathVariable Long id) {
? ? ? ?Article article = articleService.getById(id);
? ? ? ?WebResponse<Article> response = WebResponse.getSuccessResponse(article);
? ? ? ?return response;
? ?}
}
我們?cè)賮?lái)分析一下這段代碼,這段代碼和之前代碼的區(qū)別在于:
我們使用的是@RestController這個(gè)注解腌乡,而不是@Controller盟劫,不過(guò)這個(gè)注解同樣不是Spring boot提供的,而是Spring?MVC4中的提供的注解与纽,表示一個(gè)支持Restful的控制器侣签。
這個(gè)類中有三個(gè)URL映射是相同的,即都是/article/{id}急迂,這在@Controller標(biāo)識(shí)的類中是不允許出現(xiàn)的影所。這里的可以通過(guò)method來(lái)進(jìn)行區(qū)分,produces的作用是表示返回結(jié)果的類型是JSON僚碎。
@PathVariable這個(gè)注解猴娩,也是Spring MVC提供的,其作用是表示該變量的值是從訪問(wèn)路徑中獲取勺阐。
所以看來(lái)看去卷中,這個(gè)代碼還是跟Spring boot沒(méi)太多的關(guān)系,Spring boot也僅僅是提供自動(dòng)配置的功能渊抽,這也是Spring boot用起來(lái)很舒服的一個(gè)很重要的原因蟆豫,因?yàn)樗那秩胄苑浅7浅P。慊靖杏X(jué)不到它的存在腰吟。
四无埃、測(cè)試
代碼寫完了,怎么測(cè)試毛雇?除了GET的方法外,都不能直接通過(guò)瀏覽器來(lái)訪問(wèn)侦镇,當(dāng)然灵疮,我們可以直接通過(guò)postman來(lái)發(fā)送各種http請(qǐng)求。不過(guò)我還是比較支持通過(guò)單元測(cè)試類來(lái)測(cè)試各個(gè)方法壳繁。這里我們就通過(guò)Junit來(lái)測(cè)試各個(gè)方法:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class ArticleControllerTest {
? ?@Autowired
? ?private ArticleRestController restController;
? ?private MockMvc mvc;
? ?@Before
? ?public void setUp() throws Exception {
? ? ? ?mvc = MockMvcBuilders.standaloneSetup(restController).build();
? ?}
? ?@Test
? ?public void testAddArticle() throws Exception {
? ? ? ?Article article = new Article();
? ? ? ?article.setTitle("測(cè)試文章000000");
? ? ? ?article.setType(1);
? ? ? ?article.setStatus(2);
? ? ? ?article.setSummary("這是一篇測(cè)試文章");
? ? ? ?Gson gosn = new Gson();
? ? ? ?RequestBuilder builder = MockMvcRequestBuilders
? ? ? ? ? ? ? ?.post("/rest/article")
? ? ? ? ? ? ? ?.accept(MediaType.APPLICATION_JSON)
? ? ? ? ? ? ? ?.contentType(MediaType.APPLICATION_JSON_UTF8)
? ? ? ? ? ? ? ?.content(gosn.toJson(article));
? ? ? ?MvcResult result = mvc.perform(builder).andReturn();
? ? ? ?System.out.println(result.getResponse().getContentAsString());
? ?}
? ?@Test
? ?public void testUpdateArticle() throws Exception {
? ? ? ?Article article = new Article();
? ? ? ?article.setTitle("更新測(cè)試文章");
? ? ? ?article.setType(1);
? ? ? ?article.setStatus(2);
? ? ? ?article.setSummary("這是一篇更新測(cè)試文章");
? ? ? ?Gson gosn = new Gson();
? ? ? ?RequestBuilder builder = MockMvcRequestBuilders
? ? ? ? ? ? ? ?.put("/rest/article/1")
? ? ? ? ? ? ? ?.accept(MediaType.APPLICATION_JSON)
? ? ? ? ? ? ? ?.contentType(MediaType.APPLICATION_JSON_UTF8)
? ? ? ? ? ? ? ?.content(gosn.toJson(article));
? ? ? ?MvcResult result = mvc.perform(builder).andReturn();
? ?}
? ?@Test
? ?public void testQueryArticle() throws Exception {
? ? ? ?RequestBuilder builder = MockMvcRequestBuilders
? ? ? ? ? ? ? ?.get("/rest/article/1")
? ? ? ? ? ? ? ?.accept(MediaType.APPLICATION_JSON)
? ? ? ? ? ? ? ?.contentType(MediaType.APPLICATION_JSON_UTF8);
? ? ? ?MvcResult result = mvc.perform(builder).andReturn();
? ? ? ?System.out.println(result.getResponse().getContentAsString());
? ?}
? ?@Test
? ?public void testDeleteArticle() throws Exception {
? ? ? ?RequestBuilder builder = MockMvcRequestBuilders
? ? ? ? ? ? ? ?.delete("/rest/article/1")
? ? ? ? ? ? ? ?.accept(MediaType.APPLICATION_JSON)
? ? ? ? ? ? ? ?.contentType(MediaType.APPLICATION_JSON_UTF8);
? ? ? ?MvcResult result = mvc.perform(builder).andReturn();
? ?}
}
執(zhí)行結(jié)果這里就不給大家貼了震捣,大家有興趣的話可以自己實(shí)驗(yàn)一下荔棉。整個(gè)類要說(shuō)明的點(diǎn)還是很少,主要這些東西都與Spring boot沒(méi)關(guān)系蒿赢,支持這些操作的原因還是上一篇文章中提到的引入對(duì)應(yīng)的starter:
<dependency>
? ? ?<groupId>org.springframework.boot</groupId>
? ? ?<artifactId>spring-boot-starter-test</artifactId>
? ? ?<scope>test</scope>
</dependency>
因?yàn)橐獔?zhí)行HTTP請(qǐng)求润樱,所以這里使用了MockMvc,ArticleRestController通過(guò)注入的方式實(shí)例化羡棵,不能直接new壹若,否則ArticleRestController就不能通過(guò)Spring IoC容器來(lái)管理,因而其依賴的其他類也無(wú)法正常注入皂冰。通過(guò)MockMvc我們就可以輕松的實(shí)現(xiàn)HTTP的DELETE/PUT/POST等方法了店展。
五、總結(jié)
本文講解了如果通過(guò)Spring boot來(lái)實(shí)現(xiàn)Restful的API秃流,其實(shí)大部分東西都是Spring和Spring MVC提供的赂蕴,Spring boot只是提供自動(dòng)配置的功能。但是舶胀,正是這種自動(dòng)配置概说,為我們減少了很多的開發(fā)和維護(hù)工作,使我們能更加簡(jiǎn)單嚣伐、高效的實(shí)現(xiàn)一個(gè)web工程糖赔,從而讓我們能夠更加專注于業(yè)務(wù)本身的開發(fā),而不需要去關(guān)心框架的東西纤控。這篇文章中我們提到了可以通過(guò)postman和junit的方式來(lái)訪問(wèn)Restful 接口挂捻,下篇文章我們會(huì)介紹另外一種方式來(lái)訪問(wèn),有興趣的可以繼續(xù)關(guān)注一下船万。
擴(kuò)展閱讀
Spring Boot配置隨機(jī)數(shù)那些小技巧
給你一份詳細(xì)的Spring Boot知識(shí)清單
Spring MVC到Spring BOOT的簡(jiǎn)化之路
來(lái)源:https://www.cnblogs.com/paddix/p/8215245.html