在今天的帖子中,我们将超越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 } )@POSTpublic 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