超越JAX-RS规范:Apache CXF搜索扩展

本文超越JAX - RS 2.0规范,探索Apache CXF为REST服务和API开发人员提供的搜索扩展。介绍了配置和激活搜索扩展的方法,通过示例展示如何使用OData 2.0查询过滤器进行搜索,还提到支持FIQL,该功能可节省开发时间和精力。

在今天的帖子中,我们将超越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.namesearch.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执行搜索,该搜索在/ search端点下列出:

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岁以上的人($ filter = “ firsteq 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服务提供简单(而不是那么简单)的搜索功能,从而可以节省大量时间和精力。 如果适合您的应用程序请尝试一下。

翻译自: https://www.javacodegeeks.com/2014/12/beyond-the-jax-rs-spec-apache-cxf-search-extension.html

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值