LinkRest指南

2025/04/08

1. 概述

LinkRest是一个用于构建数据驱动的REST Web服务的开源框架,它建立在JAX-RS和Apache Cayenne ORM之上,并使用基于HTTP/JSON的消息协议。

基本上,这个框架旨在提供一种在Web上展示我们的数据存储的简单方法。

在下面的部分中,我们将了解如何使用LinkRest构建REST Web服务来访问数据模型。

2. Maven依赖

要开始使用该库,首先我们需要添加link-rest依赖:

<dependency>
    <groupId>com.nhl.link.rest</groupId>
    <artifactId>link-rest</artifactId>
    <version>2.9</version>
</dependency>

这也带来了cayenne-server工件。

此外,我们将使用Jersey作为JAX-RS实现,因此我们需要添加jersey-container-servlet依赖,以及jersey-media-moxy用于序列化JSON响应:

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>2.25.1</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-moxy</artifactId>
    <version>2.25.1</version>
</dependency>

对于我们的示例,我们将使用内存中的H2数据库,因为它更容易设置;因此,我们还将添加h2

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.196</version>
</dependency>

3. Cayenne数据模型

我们将要使用的数据模型包含一个Department和一个Employee实体,它们代表一对多的关系:

如上所述,LinkRest与使用Apache Cayenne ORM生成的数据对象配合使用。使用Cayenne不是本文的主题,因此有关更多信息,请查看Apache Cayenne文档

我们将把Cayenne项目保存在cayenne-linkrest-project.xml文件中。

运行cayenne-maven-plugin后,将生成两个_Department和_Employee抽象类-它们将扩展CayenneDataObject类,以及从它们派生的两个具体类,Department和Employee。

后者是我们可以定制并与LinkRest一起使用的类。

4. LinkRest应用程序启动

在下一部分中,我们将编写和测试REST端点,因此为了能够运行它们,我们需要设置我们的运行时。

由于我们使用Jersey作为JAX-RS实现,因此让我们添加一个扩展ResourceConfig的类并指定包含定义REST端点的类的包:

@ApplicationPath("/linkrest")
public class LinkRestApplication extends ResourceConfig {

    public LinkRestApplication() {
        packages("cn.tuyucheng.taketoday.linkrest.apis");

        // load linkrest runtime
    }
}

在同一个构造函数中,我们需要构建LinkRestRuntime并将其注册到Jersey容器,此类基于首先加载CayenneRuntime:

ServerRuntime cayenneRuntime = ServerRuntime.builder()
    .addConfig("cayenne-linkrest-project.xml")
    .build();
LinkRestRuntime lrRuntime = LinkRestBuilder.build(cayenneRuntime);
super.register(lrRuntime);

最后,我们需要将该类添加到web.xml中:

<servlet>
    <servlet-name>linkrest</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>cn.tuyucheng.taketoday.LinkRestApplication</param-value>
        </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>linkrest</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

5. REST资源

现在我们已经有了模型类,我们可以开始编写REST资源。

REST端点是使用标准JAX-RS注解创建的,而响应是使用LinkRest类构建的

我们的示例将包括编写一系列使用不同HTTP方法访问/department URL的CRUD端点。

首先,让我们创建DepartmentResource类,它映射到/department:

@Path("department")
@Produces(MediaType.APPLICATION_JSON)
public class DepartmentResource {

    @Context
    private Configuration config;

    // ...
}

LinkRest类需要JAX-RS Configuration类的实例,该实例使用Context注解注入,也是由JAX-RS提供的。

接下来,让我们继续编写访问Department对象的每个端点。

5.1 使用POST创建实体

为了创建实体,LinkRest类提供了create()方法,该方法返回一个UpdateBuilder对象:

@POST
public SimpleResponse create(String data) {
    return LinkRest.create(Department.class, config).sync(data);
}

data参数可以是表示Department的单个JSON对象,也可以是对象数组。此参数使用sync()方法发送到UpdateBuilder,以创建一个或多个对象并将记录插入数据库,之后该方法返回一个SimpleResponse。

该库为响应定义了另外3种格式:

  • DataResponse<T>:表示T集合的响应
  • MetadataResponse<T>:包含有关类型的元数据信息
  • SimpleResponse:包含两个success和message属性的对象

接下来,让我们使用curl向数据库添加Department记录:

curl -i -X POST -H "Content-Type:application/json" 
  -d "{"name":"IT"}" http://localhost:8080/linkrest/department

因此,该命令返回状态201 Created和success属性:

{"success":true}

我们还可以通过发送JSON数组来创建多个对象:

curl -i -X POST -H "Content-Type:application/json" 
  -d "[{"name":"HR"},{"name":"Marketing"}]" 
  http://localhost:8080/linkrest/department

5.2 使用GET读取实体

查询对象的主要方法是LinkRest类中的select()方法,该方法返回一个SelectBuilder对象,我们可以使用该对象链接其他查询或过滤方法。

让我们在DepartmentResource类中创建一个端点,返回数据库中的所有Department对象:

@GET
public DataResponse<Department> getAll(@Context UriInfo uriInfo) {
    return LinkRest.select(Department.class, config).uri(uriInfo).get();
}

uri()调用设置了SelectBuilder的请求信息,而get()返回了包装为DataResponse<Department>对象的Departments集合。

让我们使用此端点获取之前添加的部门:

curl -i -X GET http://localhost:8080/linkrest/department

响应采用带有data数组和total属性的JSON对象的形式:

{"data":[
    {"id":200,"name":"IT"},
    {"id":201,"name":"Marketing"},
    {"id":202,"name":"HR"}
],
    "total":3}

或者,要检索对象集合,我们也可以使用getOne()而不是get()返回单个对象。

让我们添加一个映射到/department/{departmentId}的端点,该端点返回具有给定id的对象。为此,我们将使用byId()方法过滤记录:

@GET
@Path("{id}")
public DataResponse<Department> getOne(@PathParam("id") int id, @Context UriInfo uriInfo) {
    return LinkRest.select(Department.class, config)
        .byId(id).uri(uriInfo).getOne();
}

然后,我们可以向此URL发送GET请求:

curl -i -X GET http://localhost:8080/linkrest/department/200

结果是一个包含一个元素的data数组:

{"data":[{"id":200,"name":"IT"}],"total":1}

5.3 使用PUT更新实体

要更新记录,我们可以使用update()或createOrUpdate()方法。后者将更新记录(如果记录存在),如果记录不存在则创建记录:

@PUT
public SimpleResponse createOrUpdate(String data) {
    return LinkRest.createOrUpdate(Department.class, config).sync(data);
}

与前面的部分类似,data参数可以是单个对象或对象数组。

让我们更新之前添加的一个部门:

curl -i -X PUT -H "Content-Type:application/json" 
  -d "{"id":202,"name":"Human Resources"}" 
  http://localhost:8080/linkrest/department

这将返回一个包含成功或错误消息的JSON对象;之后,我们可以验证ID为202的部门名称是否已更改:

curl -i -X GET http://localhost:8080/linkrest/department/202

因此,这个命令返回了具有新名称的对象:

{"data":[
    {"id":202,"name":"Human Resources"}
],
    "total":1}

5.4 使用DELETE删除实体

并且,要删除一个对象,我们可以调用delete()方法创建一个DeleteBuilder,然后使用id()方法指定我们要删除的对象的主键:

@DELETE
@Path("{id}")
public SimpleResponse delete(@PathParam("id") int id) {
    return LinkRest.delete(Department.class, config).id(id).delete();
}

然后我们可以使用curl调用该端点:

curl -i -X DELETE http://localhost:8080/linkrest/department/202

5.5 处理实体之间的关系

LinkRest还包含使处理对象之间关系更容易的方法。

由于Department与Employee具有一对多关系,因此让我们添加一个访问EmployeeSubResource类的/department/{departmentId}/employees端点:

@Path("{id}/employees")
public EmployeeSubResource getEmployees(@PathParam("id") int id, @Context UriInfo uriInfo) {
    return new EmployeeSubResource(id);
}

EmployeeSubResource类对应于一个部门,因此它有一个设置部门ID的构造函数,以及Configuration实例:

@Produces(MediaType.APPLICATION_JSON)
public class EmployeeSubResource {
    private Configuration config;

    private int departmentId;

    public EmployeeSubResource(int departmentId, Configuration configuration) {
        this.departmentId = departmentId;
        this.config = config;
    }

    public EmployeeSubResource() {
    }
}

请注意,要将对象序列化为JSON对象,必须有一个默认构造函数。

接下来,让我们定义一个检索某个部门的所有员工的端点:

@GET
public DataResponse<Employee> getAll(@Context UriInfo uriInfo) {
    return LinkRest.select(Employee.class, config)
        .toManyParent(Department.class, departmentId, Department.EMPLOYEES)
        .uri(uriInfo).get();
}

在这个例子中,我们使用了SelectBuilder的toManyParent()方法来仅查询具有给定父级的对象。

可以以类似的方式创建POST、PUT、DELETE方法的端点。

要将员工添加到部门,我们可以使用POST方法调用entities/{departmentId}/employees端点:

curl -i -X POST -H "Content-Type:application/json" 
  -d "{"name":"John"}" http://localhost:8080/linkrest/department/200/employees

然后我们发送一个GET请求来查看该部门的员工:

curl -i -X GET "http://localhost:8080/linkrest/department/200/employees

这将返回一个带有data数组的JSON对象:

{"data":[{"id":200,"name":"John"}],"total":1}

6. 使用请求参数自定义响应

LinkRest提供了一种通过向请求添加特定参数来自定义响应的简单方法,这些参数可用于过滤、排序、分页或限制结果集的属性集。

6.1 过滤

我们可以使用cayenneExp参数根据属性值过滤结果;顾名思义,这遵循Cayenne表达式的格式。

让我们发送一个仅返回名称为“IT”的部门的请求:

curl -i -X GET http://localhost:8080/linkrest/department?cayenneExp=name='IT'

6.2 排序

对一组结果进行排序需要添加的参数是sort和dir,其中第一个指定排序的属性,第二个指定排序的方向。

让我们看看按名称排序的所有部门:

curl -i -X GET "http://localhost:8080/linkrest/department?sort=name&dir=ASC"

6.3 分页

该库通过添加start和limit参数来支持分页:

curl -i -X GET "http://localhost:8080/linkrest/department?start=0&limit=2

6.4 选择属性

使用include和exclude参数,我们可以控制在结果中返回哪些属性或关系。

例如,我们发送一个仅显示部门名称的请求:

curl -i -X GET "http://localhost:8080/linkrest/department?include=name

为了仅显示部门名称以及员工姓名,我们可以使用include属性两次:

curl -i -X GET "http://localhost:8080/linkrest/department?include=name&include=employees.name

7. 总结

在本文中,我们展示了如何使用LinkRest框架通过REST端点快速公开数据模型。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章