在今天的帖子中,我们将超越JAX-RS 2.0规范,并探索Apache CXF (流行的JAX-RS 2.0实现之一)为REST服务和API开发人员提供的有用扩展。 特别是,我们将讨论使用OData 2.0查询过滤器子集的搜索扩展。
简而言之,搜索扩展仅将某种过滤器表达式映射到一组匹配的类型化实体(Java类的实例)。 OData 2.0查询过滤器可能非常复杂,但是目前Apache CXF仅支持它们的子集:
操作员 | 描述 | 例 |
---|---|---|
当量 | 等于 | 雷德蒙德市 |
NE | 不相等 | 城市“伦敦” |
gt | 比...更棒 | 价格gt 20 |
ge | 大于或等于 | 价格GE 10 |
lt | 少于 | 价格lt 20 |
乐 | 小于或等于 | 价格100 |
和 | 逻辑与 | 价格le 200和价格gt 3.5 |
要么 | 逻辑或 | 价格le 3.5或价格gt 200 |
基本上,要配置和激活JAX-RS服务的搜索扩展,只需定义两个属性search.query.parameter.name和search.parser ,再加上一个附加提供程序SearchContextProvider :
@Configuration
public class AppConfig {
@Bean( destroyMethod = "shutdown" )
public SpringBus cxf() {
return new SpringBus();
}
@Bean @DependsOn( "cxf" )
public Server jaxRsServer() {
final Map< String, Object > properties = new HashMap< String, Object >();
properties.put( "search.query.parameter.name", "$filter" );
properties.put( "search.parser", new ODataParser< Person >( Person.class ) );
final JAXRSServerFactoryBean factory =
RuntimeDelegate.getInstance().createEndpoint(
jaxRsApiApplication(),
JAXRSServerFactoryBean.class
);
factory.setProvider( new SearchContextProvider() );
factory.setProvider( new JacksonJsonProvider() );
factory.setServiceBeans( Arrays.< Object >asList( peopleRestService() ) );
factory.setAddress( factory.getAddress() );
factory.setProperties( properties );
return factory.create();
}
@Bean
public JaxRsApiApplication jaxRsApiApplication() {
return new JaxRsApiApplication();
}
@Bean
public PeopleRestService peopleRestService() {
return new PeopleRestService();
}
}
search.query.parameter.name定义用作过滤器的查询字符串参数的名称(我们将其设置为$ filter ),而search.parser定义用于解析过滤器表达式的解析器(我们设置将其与Person类一起参数化为ODataParser )。 ODataParser建立在出色的Apache Olingo项目之上, 该项目当前实现了OData 2.0协议(正在支持OData 4.0 )。
配置完成后,任何JAX-RS 2.0服务都可以通过注入上下文参数SearchContext来受益于搜索功能。 让我们通过定义REST服务来管理下面的Person类所代表的人员,来了解实际情况 :
public class Person {
private String firstName;
private String lastName;
private int age;
// Setters and getters here
}
该PeopleRestService将只允许使用来创建新的个人HTTP POST和使用进行搜索HTTP GET,下/搜索端点所列:
package com.example.rs;
import java.util.ArrayList;
import java.util.Collection;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.apache.cxf.jaxrs.ext.search.SearchCondition;
import org.apache.cxf.jaxrs.ext.search.SearchContext;
import com.example.model.Person;
@Path( "/people" )
public class PeopleRestService {
private final Collection< Person > people = new ArrayList<>();
@Produces( { MediaType.APPLICATION_JSON } )
@POST
public Response addPerson( @Context final UriInfo uriInfo,
@FormParam( "firstName" ) final String firstName,
@FormParam( "lastName" ) final String lastName,
@FormParam( "age" ) final int age ) {
final Person person = new Person( firstName, lastName, age );
people.add( person );
return Response
.created( uriInfo.getRequestUriBuilder().path( "/search" )
.queryParam( "$filter=firstName eq '{firstName}' and lastName eq '{lastName}' and age eq {age}" )
.build( firstName, lastName, age ) )
.entity( person ).build();
}
@GET
@Path("/search")
@Produces( { MediaType.APPLICATION_JSON } )
public Collection< Person > findPeople( @Context SearchContext searchContext ) {
final SearchCondition< Person > filter = searchContext.getCondition( Person.class );
return filter.findAll( people );
}
}
findPeople方法是我们正在寻找的方法。 由于Apache CXF所做的所有努力,该方法看起来非常简单:注入了SearchContext ,并从$ filter查询字符串参数中自动提取了过滤器表达式。 最后一部分是将过滤器应用于数据,在我们的例子中,它只是一个名为people的集合。 非常干净和直接。
让我们构建项目并运行它:
mvn clean package
java -jar target/cxf-search-extension-0.0.1-SNAPSHOT.jar
使用很棒的curl工具,让我们发出几个HTTP POST请求,以生成一些数据以针对以下条件运行过滤器查询:
> curl http://localhost:8080/rest/api/people -X POST -d "firstName=Tom&lastName=Knocker&age=16"
{
"firstName": "Tom",
"lastName": "Knocker",
"age": 16
}
> curl http://localhost:8080/rest/api/people -X POST -d "firstName=Bob&lastName=Bobber&age=23"
{
"firstName": "Bob",
"lastName": "Bobber",
"age": 23
}
> curl http://localhost:8080/rest/api/people -X POST -d "firstName=Tim&lastName=Smith&age=50"
{
"firstName": "Tim",
"lastName": "Smith",
"age": 50
}
有了示例数据,让我们继续前进,提出几个不同的搜索条件,这些条件足够复杂,足以展示OData 2.0查询过滤器的强大功能:
- 查找所有名字为Bob的人 ($ filter = “ firsteq eq'Bob'” )
> curl -G -X GET http://localhost:8080/rest/api/people/search --data-urlencode
$filter="firstName eq 'Bob'"
[
{
"firstName": "Bob",
"lastName": "Bobber",
"age": 23
}
]
- 查找所有姓氏为Bobber或姓氏 Smith且名字不是Bob的人 ($ filter = “ lastName eq'Bobber'或(lastName eq'Smith'and firstName ne'Bob')” ))
> curl -G -X GET http://localhost:8080/rest/api/people/search --data-urlencode
$filter="lastName eq 'Bobber' or (lastName eq 'Smith' and firstName ne 'Bob')"
[
{
"firstName": "Bob",
"lastName": "Bobber",
"age": 23
},
{
"firstName": "Tim",
"lastName": "Smith",
"age": 50
}
]
- 查找其名字开始从字母T,谁所有的人在16岁以上($ =过滤器“名字EQ‘T *’和年龄GE 16”)
> curl -G -X GET http://localhost:8080/rest/api/people/search --data-urlencode
$filter="firstName eq 'T*' and age ge 16"
[
{
"firstName": "Tom",
"lastName": "Knocker",
"age": 16
},
{
"firstName": "Tim",
"lastName": "Smith",
"age": 50
}
]
注意 :如果在类似Linux的环境中运行此命令,则可能需要使用\ $转义$符号,例如:
curl -X GET -G http:// localhost:8080 / rest / api / people / search –data-urlencode \ $ filter =“ firstName eq'Bob'”
目前, Apache CXF仅提供OData 2.0查询过滤器的基本支持,并保留了许多强大的表达式。 但是,一旦社区对使用此功能表示出足够的兴趣,就有承诺将其推进。
值得一提的是, OData 2.0查询过滤器不是唯一可用的选项。 搜索扩展还支持FIQL (提要项查询语言),而来自Apache CXF核心开发人员之一的精彩文章也对其进行了很好的介绍。
我认为Apache CXF的这一非常有用的功能可以通过为JAX-RS 2.0服务提供简单(而不是那么简单)的搜索功能来节省大量时间和精力。 如果适合您的应用程序需求,请尝试一下。
- 完整的项目源代码可在Github上获得 。
翻译自: https://www.javacodegeeks.com/2014/12/beyond-the-jax-rs-spec-apache-cxf-search-extension.html