我有一个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标头中)..那么,如何保持我的控制器简单并减少代码重复这个任务?
答案 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
}
如果您需要先验证过滤器输入,请在尝试处理请求之前将输入发送到验证层。 特别是在您申请的边界处分离关注点会在长期内获得巨大回报。