前言
2022过去了,迎来崭新的一年,在过去的半年里,我不仅在课内,还在课外通过各种方式了解和学习到了不少新的语言、框架、技术等等。平时急于产出和学习,没能好好的整理所学,遂在这个寒假,从这篇文章开始,以一系列文章作为对这段时间所学所作进行一个系统性回顾整理的过程,希望给自己查漏补缺,并能给一些正在或将要学习其中某些内容的朋友们一点启发。
JavaEE这个系列,将分别主要介绍 Java EE 8各方面的技术规范,并实战演练。写这些文章前,笔者已经掌握JSP和springboot,因此介绍、阐述javaee的方式和理念时,是以JSP和springboot(主要)使用者的眼光来看待,借助jsp和springboot的概念和架构来理解javaee。
这篇文章,介绍 Restful API 在Java EE 中的基础实现
导航
发文顺序从上到下,顺序大致按我接触它们的时间点排的,可能个别文章会提早或延后
- Springboot
- Java EE【√】
- Java EE 概览
- Java servlet
- JSF(Java Server Faces)
- REST(Restful Web Services API)【√】
- WebSocket
- Web Security
- Global Components and asynchronous
- EJB(Enterprise Java Bean)
- JPA(Java Persistence API)
- 网络安全框架 (Java)
- Spring Security
- Sa-Token
- Vue.js
- Web UI
- Mybatis
- Auto.js
- 浏览器3D引擎(轻量级): Three.js
- Uniapp / Unicloud 开发
- 微信小程序开发
- 前端深度学习引擎: Tensorflow.js
- 其他
- 利用Git管理源代码
- 利用maven管理java项目
- 利用npm管理项目
- Node.js配置和管理
- Sqlite: 内存中的轻数据库
- 软件过程和项目管理浅谈
- 打造自己的前端组件库
- 软件测试工具
- 文档撰写利器
正文
REST 是什么
Restful Web Services把应用和数据视作一个个资源,资源可以按指定路径通过http请求被访问,不同的资源有着不同的对应路径,对某一数据的不同操作可通过请求方式(GET, POST…)和参数分开实现,而不是将路径显示的写成相应的操作,如:(POST): …/deleteUserById?id=100 到 (DELETE) …/user/100
在javaee中,jax-rs是对REST的规范定义,相应的包为javax.ws.rs
手写 REST API
接下来,我们使用 JAVA EE 基本的规范技术 EJB 和 REST 来亲手实现几个 REST API 接口
-
创建基础 JAVA Web 项目
mvn archetype:generate -DarchetypeGroupId=io.openliberty.tools -DarchetypeArtifactId=liberty-archetype-webapp -DarchetypeVersion=3.6.1 -DgroupId=com.example -DartifactId=rest_demo -Dversion=1.0-SNAPSHOT -DinteractiveMode=false
-
支持 Java EE 8
- 修改 pom.xml 中 servlet-api 依赖为 javaee-api 依赖
<dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0</version> <scope>provided</scope> </dependency>
- 修改 liberty 下 server.xml 中 jsp 为 javaee-8.0
<featureManager> <feature>javaee-8.0</feature> </featureManager>
-
创建实体类User 和 UserList
User:
package com.example.model; import java.io.Serializable; import java.util.Objects; public class User implements Serializable { private static final long serialVersionUID = 1L; private String name; public User() { } public User(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof User)) return false; User user = (User) o; return Objects.equals(getName(), user.getName()); } @Override public int hashCode() { return Objects.hash(getName()); } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } }
UserList:
这个就有点像 springmvc 中我们用的 Service Bean,提供了对资源获取和修改的具体方法
俩注解是 JAVA EE 的 EJB 用来管理并发的,@Startup 是自启动Bean并发管理,@Singleton是单例模式,此外常用注解还有,@DependsOn指定Bean启动顺序,@AccessTimeout超时设置等等。
我这里userlist里面存了一个user列表,我们把它当作我们存放的全部user资源,然后提供了几个查询,添加的方法。
package com.example.model; import javax.ejb.Singleton; import javax.ejb.Startup; import java.util.ArrayList; import java.util.List; @Startup @Singleton public class UserList { private List<User> userlist = new ArrayList<>(); public UserList() { User user = new User("admin"); this.userlist.add(user); } public UserList(List<User> userlist) { this.userlist = userlist; } public List<User> getUserlist() { return userlist; } public void setUserlist(List<User> userlist) { this.userlist = userlist; } @Override public String toString() { return "UserList{" + "userlist=" + userlist + '}'; } public void addUser(String new_user_name){ User user = new User(new_user_name); this.userlist.add(user); } public User getUser(String user_name) { User user = new User(user_name); if(this.userlist.contains(user)){ return user; } return null; } }
-
定义请求资源的路径
这个其实就好比springboot里面我们的Controller啊,我就用controller命名类了,有助于理解,实际按照javaee rest本身的思想,应该命名成Resource之类的。
@Path(String):指定URL请求路径
@Inject:好比springboot中的@Autowired,让容器主动地帮我们将类和相应的Java Bean组装到一起。
@GET:指定请求方式为GET,其他的以此类推
@Produces(String):指定响应请求的返回形式,可以通过MediaType.XXX快速设置值,也可以像我一样直接写
@Consumes(String):指定发起请求MIME的类型,也就是Body传输数据的文本类型,同上
@PathParam({variable}):指定URL变量,和@Path配合食用@GET @Path("/user/{name}") public Response getUserName(@PathParam("name") String user_name) { return Response.ok(user_name).build(); }
其他常用的注解:
@QueryParam:接收URL参数,/url?name=rust&nick=bestlanguage,这种,和@PathParam一样写在方法传参的参前面
@FormParam:接收以表单中的某一个参数,写在方法传参的参前面
@HeaderParam:接收HTTP请求头中的参数,写在方法传参的参前面
@CookieParam:接收Cookie中的参数,写在方法传参的参前面
@MatrixParam:接收URL参数,但是形式和@QueryParam不同:/url;name=rust;nick=bestlanguage
@DefaultValue:接收参数时配置默认值
@BeanParam:接收多个参数的组合体,写在方法传参的参前面public class userBean { @FormParam("username") private String username; @HeaderParam("myHeader") private String header; @PathParam("id") private String id; @QueryParam("nick"); private String nick; ... } @Path("/user") public class MyResources { @POST @Path("{id}") public void post(@BeanParam userBean userbean) { ... } ... }
@Context:解析上下文参数
UserController :
package com.example.controller; import com.example.model.UserList; import javax.inject.Inject; import javax.ws.rs.*; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; @Path("/users") public class UserController { @Context private UriInfo context; public UserController() { } @Inject UserList userList; @GET @Produces("application/json") public Response getUsers() { return Response.ok(userList.getUserlist()).build(); } @POST @Consumes("application/json") @Produces("application/json") public Response addUser(String new_user_name) { userList.addUser(new_user_name); return Response.ok(userList.getUser(new_user_name)).build(); } @GET @Path("{name}") @Produces("application/json") public Response getUser(@PathParam("name") String user_name) { return Response.ok(userList.getUser(user_name)).build(); } }
-
配置发布资源的根路径:
package com.example.config; import java.util.Set; import javax.ws.rs.core.Application; @javax.ws.rs.ApplicationPath("api") public class ApplicationConfig extends Application { @Override public Set<Class<?>> getClasses() { Set<Class<?>> resources = new java.util.HashSet<>(); resources.add(com.example.controller.UserController.class); return resources; } }
-
Open Liberty 跨域配置
<cors domain="/configurations/simple" allowedOrigins="openliberty.io" allowedMethods="GET, POST" allowCredentials="true" />
-
测试
没啥问题,很OK
REST 返回
我们再来具体讲讲rest的响应返回,JAX-RS生成的内容称为 response entity,就是用@Peoduces标记的,至于返回什么,我们有两种形式。
- Response 响应体
就是我上面代码里面用的,Response可以设置http响应码等等,大家可以自行研究 - 直接返回Java对象
比如 String,byte[],File等等
后记
过几天,我会专门写一篇文章好好讲讲 restful api 接口的优质实践规范