可查询的web api

时间:2015-01-18 20:54:08

标签: c# json rest asp.net-web-api iqueryable

我有一个Web API,我希望为分页,排序和预选结果(数据库方式:dto中包含的列)等内容提供一些(有限的)可查询端点。到目前为止,事情进展顺利,但是我有很多非常臃肿的控制器方法,有很多技术内容,我想知道是否更容易满足我的要求。

典型的控制器如下:

public HttpResponseMessage GetEntities(int page = MaximumPageIndex, 
    int pageSize = MinimumPageSize,
    string orderBy = null,
    [FromUri] EntitySearchCriteria criteria = null)
{

    // ensure that page/pageSize lies within possible boundaries
    page = page.ToMinimum(MinimumPageIndex);
    pageSize = pageSize.ToBounds(MinimumPageSize, MaximumPageSize);

    // create any criteria for entity if none is available
    criteria = criteria ?? new AnyEntitySearchCriteria();

    // create ordering expressions based on string
    var orderOptions = orderBy != null
        ? ExpressionBuilder.CreateSelector<Entity, dynamic>(orderBy)
        : ExpressionBuilder.CreateSelector<Entity, dynamic>("Id");

    // create paging options
    var pagingOptions = new PagingOptions<Entity, dynamic>(page, pageSize, orderOptions);

    try {

        // get total count from database
        var totalCount = _repository.Count();

        // get entities by criteria specification (IMemberSpecification<T, TMember>)
        var results = _repository.GetEntitiesByCriteria(criteria,
            x = new BlablaDto {}  // apply result selectors (not shown in example,
            // map to paged list
            pagingOptions).ToPagedList(page, pageSize, totalCount);

        // create "paged" response with paging http headers (rfc 5988)
        return Request.CreatePagedResponse(HttpStatusCode.OK, results);
        ...
}

对于许多通常看起来相同的实体来说,这当然是一遍又一遍。现在我知道那里的Microsoft OData,但我不能并且因为多种原因而不能使用它(仅适用于IQueryable,奇怪的响应 - 例如:分页数据包含在json结果中,不在HTTP标头中)..那么,如何保持我的控制器简单并减少代码重复这个任务?

1 个答案:

答案 0 :(得分:0)

您的控制器有很多逻辑,您应该考虑将该逻辑移动到另一层,例如API Facade / Engine层。虽然您可以设置内置一些继承的搜索过滤器来清理输入。我在最近的许多项目中都必须这样做。

using System.Collections.Generic;
using System.Runtime.Serialization;

public interface IPaginatedFilter {

    int DefaultLimit { get; }
    int MaximumLimit { get; }
    List<string> SupportedOrderBy { get; }
    bool IsAscendingByDefault { get; }
}

[DataContract]
public sealed class PaginationInput 
{
    [DataMember(Name = "limit", Order = 2)]
    public int Limit { get; set; }

    [DataMember(Name = "offset", Order = 3)]
    public int Offset { get; set; }

    [DataMember(Name = "orderBy", Order = 4)]
    public string OrderBy { get; set; }

    [DataMember(Name = "orderByDirection", Order = 5)]
    public string OrderByDirection { get; set; }

}

/// <summary>
///     Base search filter
/// </summary>
public class SearchBase {
    /// <summary>
    ///     Results page information
    /// </summary>
    [DataMember(Name = "page", Order = 1)]
    public PaginationInput Page { get; set; }

    /// <summary>
    ///     Search phrase applicable to any member of the inheriting search filter
    /// </summary>
    [DataMember(Name = "any", Order = 1)]
    public string Any { get; set; }

}

public class EntitySearchCriteria : SearchBase, IPaginatedFilter {

    // columns to provide search functions for


    #region Implementation of IPaginatedFilter

    public int DefaultLimit {
        get { return 20; }
    }

    public int MaximumLimit {
        get { return 50; }
    }

    public List<string> SupportedOrderBy {
        get { return new List<string>
        {
            // columns that support ordering
        }; }
    }

    public bool IsAscendingByDefault {
        get { return true; }
    }

    #endregion

}

这样您就可以为所有搜索过滤器使用相同的API。您的控制器应该看起来像

public HttpResponseMessage GetEntities(
        [FromUri] EntitySearchCriteria filter = null)
{

    // process input using your service layer

    // generate response

}

如果您需要先验证过滤器输入,请在尝试处理请求之前将输入发送到验证层。 特别是在您申请的边界处分离关注点会在长期内获得巨大回报