详解Spring Boot实战之Restful API的构建
上一篇文章讲解了通过spring boot与jdbctemplate、jpa和mybatis的集成,实现对数据库的访问。今天主要给大家分享一下如何通过spring boot向前端返回数据。
在现在的开发流程中,为了最大程度实现前后端的分离,通常后端接口只提供数据接口,由前端通过ajax请求从后端获取数据并进行渲染再展示给用户。我们用的最多的方式就是后端会返回给前端一个json字符串,前端解析json字符串生成javascript的对象,然后再做处理。本文就来演示一下spring boot如何实现这种模式,本文重点会讲解如何设计一个restful的api,并通过spring boot来实现相关的api。不过,为了大家更好的了解restful风格的api,我们先设计一个传统的数据返回接口,这样大家可以对比着来理解。
一、非restful接口的支持
我们这里以文章列表为例,实现一个返回文章列表的接口,代码如下:
@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); } }
这个articleservice的实现很简单,就是简单的封装了articlemapper的操作,articlemapper的内容大家可以参考上一篇的文章,articleservice的实现类如下:
@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); } }
运行application.java这个类,然后访问:http://locahost:8080/article/list.json,就可以看到如下的结果:
articleserviceimpl这个类是一个很普通的类,只有一个spring的注解@service,标识为一个bean以便于通过spring ioc容器来管理。我们再来看看articlecontroller这个类,其实用过spring mvc的人应该都熟悉这几个注解,这里简单解释一下:
@controller 标识一个类为控制器。
@requestmapping url的映射。
@responsebody 返回结果转换为json字符串。
@requestbody 表示接收json格式字符串参数。
通过这个三个注解,我们就能轻松的实现通过url给前端返回json格式数据的功能。不过大家肯定有点疑惑,这不都是spring mvc的东西吗?跟spring boot有什么关系?其实spring boot的作用就是为我们省去了配置的过程,其他功能确实都是spring与spring mvc来为我们提供的,大家应该记得spring boot通过各种starter来为我们提供自动配置的服务,我们的工程里面之前引入过这个依赖:
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency>
这个是所有spring boot的web工程都需要引入的jar包,也就是说只要是spring boot的web的工程,都默认支持上述的功能。这里我们进一步发现,通过spring boot来开发web工程,确实为我们省了许多配置的工作。
二、restful api设计
好了,我们现在再来看看如何实现restful api。实际上restful本身不是一项什么高深的技术,而只是一种编程风格,或者说是一种设计风格。在传统的http接口设计中,我们一般只使用了get和post两个方法,然后用我们自己定义的词汇来表示不同的操作,比如上面查询文章的接口,我们定义了article/list.json来表示查询文章列表,可以通过get或者post方法来访问。而restful api的设计则通过http的方法来表示crud相关的操作。因此,除了get和post方法外,还会用到其他的http方法,如put、delete、head等,通过不同的http方法来表示不同含义的操作。下面是我设计的一组对文章的增删改查的restful api:
接口url | http方法 | 接口说明 |
/article | post | 保存文章 |
/article/{id} | get | 查询文章列表 |
/article/{id} | delete | 删除文章 |
/article/{id} | put | 更新文章信息 |
这里可以看出,url仅仅是标识资源的路劲,而具体的行为由http方法来指定。
三、restful api实现
现在我们再来看看如何实现上面的接口,其他就不多说,直接看代码:
@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; } }
我们再来分析一下这段代码,这段代码和之前代码的区别在于:
(1)我们使用的是@restcontroller这个注解,而不是@controller,不过这个注解同样不是spring boot提供的,而是spring mvc4中的提供的注解,表示一个支持restful的控制器。
(2)这个类中有三个url映射是相同的,即都是/article/{id},这在@controller标识的类中是不允许出现的。这里的可以通过method来进行区分,produces的作用是表示返回结果的类型是json。
(3)@pathvariable这个注解,也是spring mvc提供的,其作用是表示该变量的值是从访问路径中获取。
所以看来看去,这个代码还是跟spring boot没太多的关系,spring boot也仅仅是提供自动配置的功能,这也是spring boot用起来很舒服的一个很重要的原因,因为它的侵入性非常非常小,你基本感觉不到它的存在。
四、测试
代码写完了,怎么测试?除了get的方法外,都不能直接通过浏览器来访问,当然,我们可以直接通过postman来发送各种http请求。不过我还是比较支持通过单元测试类来测试各个方法。这里我们就通过junit来测试各个方法:
@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("测试文章000000"); article.settype(1); article.setstatus(2); article.setsummary("这是一篇测试文章"); 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("更新测试文章"); article.settype(1); article.setstatus(2); article.setsummary("这是一篇更新测试文章"); 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(); } }
执行结果这里就不给大家贴了,大家有兴趣的话可以自己实验一下。整个类要说明的点还是很少,主要这些东西都与spring boot没关系,支持这些操作的原因还是上一篇文章中提到的引入对应的starter:
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-test</artifactid> <scope>test</scope> </dependency>
因为要执行http请求,所以这里使用了mockmvc,articlerestcontroller通过注入的方式实例化,不能直接new,否则articlerestcontroller就不能通过spring ioc容器来管理,因而其依赖的其他类也无法正常注入。通过mockmvc我们就可以轻松的实现http的delete/put/post等方法了。
五、总结
本文讲解了如果通过spring boot来实现restful的api,其实大部分东西都是spring和spring mvc提供的,spring boot只是提供自动配置的功能。但是,正是这种自动配置,为我们减少了很多的开发和维护工作,使我们能更加简单、高效的实现一个web工程,从而让我们能够更加专注于业务本身的开发,而不需要去关心框架的东西。这篇文章中我们提到了可以通过postman和junit的方式来访问restful 接口,下篇文章我们会介绍另外一种方式来访问,有兴趣的可以继续关注一下。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
详解Spring Boot实战之Restful API的构建
-
详解spring cloud构建微服务架构的网关(API GateWay)
-
详解spring cloud整合Swagger2构建RESTful服务的APIs
-
使用dubbo+zookeeper+spring boot构建服务的方法详解
-
详解Spring Boot配置使用Logback进行日志记录的实战
-
详解Spring Boot实战之Rest接口开发及数据库基本操作
-
详解spring cloud构建微服务架构的网关(API GateWay)
-
详解Spring Boot实战之Filter实现使用JWT进行接口认证
-
详解Spring Boot实战之单元测试
-
Spring实战之XML与JavaConfig的混合配置详解