簡體   English   中英

帶有投影的JAVA中的OData REST API

[英]OData REST API in JAVA with projection

可以在JAVA中做類似的事情嗎? 如果是,怎么辦? (附樣品)

考慮使用http://olingo.apache.org/或odata4j

我在.NET中制作了一個示例,該示例公開了OData格式的REST API(使我的消費者可以使用標准化協議進行篩選,選擇,排序等)。

注意事項:我公開了不同於ORM的模型(避免公開數據庫,並提供控制/限制使用者查詢的可能性)。 兩個用戶類:Models.User和DAL.User

樣品:

public IHttpActionResult GetUsers(ODataQueryOptions<Models.User> queryOptions)
{
    //Get the IQueryable from DB/Repository
    DAL.UsersDAO usersDAO = new DAL.UsersDAO();
    IQueryable<DAL.User> usersQueryable = usersDAO.GetUsers();

    //Make the projection from the 'User of Project DAL' to the 'User of ODataProject'
    IQueryable<Models.User> usersProjected = usersQueryable.Select(user => new Models.User()
    {
        Id = user.Id,
        Name = user.Name,
        Gender = user.Gender
    });

    //At this point, the query was not executed yet. And with that, it is possible to add new Filters
    //like the ones send by the client or even some rules from Business Logic
    //(ex: user only see other users from his country, etc)

    //Appling the queryOptions requested by the consumer
    IQueryable usersWithOptionsApplied = queryOptions.ApplyTo(usersProjected);
    IQueryable<Models.User> usersToExecute = usersWithOptionsApplied as IQueryable<Models.User>;

    //Execute the Query against the Database/Repository/Whatever with all the filters
    IEnumerable<Models.User> usersExecuted = usersToExecute.ToList();

    return Ok(usersExecuted);
}

關鍵點是:

1-建立查詢的可能性(從數據庫/存儲庫/任何地方獲取構建器)

2-將查詢投影到公開的模型(不是來自ORM的模型)

3-將從用戶發送的過濾器應用於OData REST API(queryOptions)

我在此處上傳的示例(在.NET中): http : //multiupload.biz/2meagoxw2boa

我真的很感謝任何不願意這樣做的人。 嘗試使用OData作為跨平台技術的標准方法的概念證明。

我真的很高興閱讀您的問題,因為這是我幾個月來一直在研究的主題,我希望我可以作為一個開源項目來提供。 我會嘗試給出一個簡潔的答案,但是有很多事情要說;-)

我使用Olingo進行概念驗證,並使用ElasticSearch進行后端處理,但是我想到了針對任何后端(SQL和noSQL)的開放解決方案。

有兩個主要部分:

  • 元數據配置 Olingo提供了一個實體EdmProvider ,該實體負責將托管實體的元數據提供給庫。 在請求處理期間將調用此方法,以將請求路由到正確的元素處理。

    此級別有兩種情況。 您可以手動配置此元素,也可以嘗試通過自動檢測后端結構來自動配置。 對於第一個,我們需要擴展抽象類EdmProvider 我介紹了自定義EdmProvider將基於的中間元數據,因為需要一些提示來確定實現請求的正確方法(例如,使用ElasticSearch,父/子關系等)。 以下是手動配置中間元數據的示例:

     MetadataBuilder builder = new MetadataBuilder(); builder.setNamespaces(Arrays.asList(new String[] { "odata" })); builder.setValidator(validator); TargetEntityType personDetailsAddressType = builder.addTargetComplexType("odata", "personDetailsAddress"); personDetailsAddressType.addField("street", "Edm.String"); personDetailsAddressType.addField("city", "Edm.String"); personDetailsAddressType.addField("state", "Edm.String"); personDetailsAddressType.addField("zipCode", "Edm.String"); personDetailsAddressType.addField("country", "Edm.String"); TargetEntityType personDetailsType = builder.addTargetEntityType( "odata", "personDetails"); personDetailsType.addPkField("personId", "Edm.Int32"); personDetailsType.addField("age", "Edm.Int32"); personDetailsType.addField("gender", "Edm.Boolean"); personDetailsType.addField("phone", "Edm.String"); personDetailsType.addField( "address", "odata.personDetailsAddress"); 

    第二種方法並非總是可行的,因為后端不必提供所有必需的元數據。 對於ElasticSearch,我們需要在類型映射中添加元數據以支持它。

    現在我們有了這個,我們可以專注於請求處理。

  • 請求處理 Olingo允許基於處理器處理請求。 實際上,該庫會將請求路由到可以處理該請求的類型的處理器。 例如,如果要在實體上執行CountEntityCollectionProcessor ,將選擇並使用實現EntityCollectionProcessorCountEntityCollectionProcessor和/或EntityProcessor的處理器。 然后將調用接口的正確方法。 屬性是一樣的,...

    因此,我們需要實現能夠適應請求並與目標后端進行交互的處理器。 在這個級別上,有很多管道(使用Olingo序列化器/反序列化器,構建上下文URL,最終提取參數,...),並且一種好的方法似乎實現了一個通用層作為基礎。 后者負責在后端執行操作(讀取,寫入,查詢等),還負責處理Olingo的類型( EntityProperty )和后端驅動程序使用的元素之間的轉換(對於ElasticSearch,原始對象,點擊數-參見http://www.elastic.co/guide/en/elasticsearch/client/java-api/current/client.html )。

因此,如果我們需要在公開的OData模型和后端模式之間建立一個間接級別,則需要在上述元數據和請求級別兩者之間實現它們之間的映射。 這樣就可以使實體及其屬性的名稱不完全相同。

關於過濾器,我們可以使用類UriInfo (請參見方法getFilterOptiongetSelectOptiongetExpandOptiongetOrderByOptiongetSkipOptiongetTopOption )在Olingo中輕松訪問它們,如下所述在處理器中:

@Override
public void readEntityCollection(final ODataRequest request,
              ODataResponse response, final UriInfo uriInfo,
              final ContentType requestedContentType)
              throws ODataApplicationException, SerializerException {
    (...)
    EntitySet entitySet = dataProvider.readEntitySet(edmEntitySet,
                uriInfo.getFilterOption(), uriInfo.getSelectOption(),
                uriInfo.getExpandOption(), uriInfo.getOrderByOption(),
                uriInfo.getSkipOption(), uriInfo.getTopOption());

    (...)
}

然后可以將所有提示傳遞給負責在后端創建請求的元素。 這是使用ElasticSearch的示例(請注意,類QueryBuilder是用於構建查詢的ElasticSearch Java的工廠):

QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
if (topOption!=null && skipOption!=null) {
    requestBuilder.setFrom(skipOption.getValue())
                  .setSize(topOption.getValue());
}

查詢參數$filter的查詢有點乏味,因為我們需要將初始查詢轉換為目標后端的查詢。 在類FilterOption ,我們可以訪問允許訪問表達式的Expression實例。 以下代碼描述了基於此類構建ElasticSearch查詢的簡化方法:

Expression expression = filterOption.getExpression();
QueryBuilder queryBuilder
           = expression.accept(new ExpressionVisitor() {
    (...)

    @Override
    public Object visitBinaryOperator(
              BinaryOperatorKind operator, Object left,
              Object right) throws ExpressionVisitException,
                                  ODataApplicationException {
        String fieldName = (String)left;
        // Simplification but not really clean code ;-)
        String value = ((String)right).substring(
                          1, right.length() - 1);
        return QueryBuilders.termQuery((String) left, right);
    }

    @Override
    public Object visitLiteral(String literal)
                throws ExpressionVisitException,
                ODataApplicationException {
        return literal;
    }

    @Override
    public Object visitMember(UriInfoResource member)
                throws ExpressionVisitException,
                ODataApplicationException {
        UriResourcePrimitiveProperty property
                   = (UriResourcePrimitiveProperty)
                          member.getUriResourceParts().get(0);
        return property.getProperty().getName();
    }
}

如果我們在查詢參數$filter使用值description eq 'test' ,我們將得到類似的結果:

>> visitMember - member = [description]
>> visitLiteral - literal = 'test'
>> visitBinaryOperator - operator = eq, left = description, right = 'test'

另一個棘手的部分在於處理導航屬性的方法,以及最終在后端對數據進行非規范化的方法。 我認為這超出了您的問題范圍。

隨時問我是否不清楚或/,以及是否需要更多詳細信息。

希望對您有幫助,蒂埃里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM