用于高级搜索/过滤的.Net Web API URL约定

时间:2013-02-12 04:27:22

标签: api rest asp.net-web-api

我对微软的REST和WebAPI相对较新。我们正在实现一个中心REST服务,它将容纳几种类型的对象获取和设置。作为该项目的领导者,我的任务是提出我们正在进行的适当的Uri设计。我想知道关于战争的想法更好。是的,我没有使用“标准”这个词,而是专门分阶段。

以下是我的团队和我目前正在娱乐的选项:

Http://servername/API/REST/Ldap/AD/employees?username=jsmith 
Http://servername/API/REST/Ldap/AD/employee/UserName?searchTerm=jsmith (this seems RPC to me)
Http://servername/API/REST/Ldap/AD/employees/getusername?searchterm?jsmith

我们也在创建一个Soap版本,因此剩下的就是Uri。

感谢输入

2 个答案:

答案 0 :(得分:17)

我建议你看看Brian Mulloy的Web API Design。在搜索和过滤方面,他有几点建议。

简化关联 - 扫描'?'

下的复杂性
  

大多数API都具有超出资源基础级别的复杂性。   复杂性可以包括许多可以更新,更改,   查询,以及与资源关联的属性。做了   通过设置可选状态,开发人员可以轻松使用基本URL   和HTTP问号背后的属性。保持您的API直观   通过简化资源之间的关联和清扫   在HTTP问题的基础上的参数和其他复杂性   标记

搜索提示

  

虽然可以将简单搜索建模为资源丰富的API(for   例如,dogs /?q = red),跨多个资源的更复杂的搜索   需要不同的设计。如果你想进行全局搜索   资源,我们建议您遵循Google模式:

     

全球搜索

     

/搜索Q =蓬松+毛皮

     

这里,搜索是动词; ?q表示查询。

     

Scoped search

     

要为搜索添加范围,您可以添加范围   搜索。例如,在资源ID 5678

拥有的狗中搜索      

/所有者/ 5678 /狗Q =蓬松+毛皮

     

请注意,已从网址中删除了显式搜索   相反,它依赖参数'q'来表示范围查询。

分页和部分回复

  

通过在逗号中添加可选字段来支持部分响应   分隔列表

     

/狗?字段=名称,颜色,位置

     

使用 limit offset ,让开发人员可以轻松分页   对象。

     

/狗限制= 25&安培;偏移量= 50

答案 1 :(得分:3)

对于Oppositional的评论,这是我不久前提出的。

https://groups.google.com/d/msg/servicestack/uoMzASmvxho/CtqpZdju7NcJ

public class QueryBase
{
    public string Query { get; set; }
    public int Limit { get; set; }
    public int Offset { get; set; }
}

[Route("/v1/users")]
public class User : IReturn<List<User>>
{
    public string Id { get; set; }
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
}

public class RequestFilterAttribute : Attribute, IHasRequestFilter
{
    #region IHasRequestFilter Members

    public IHasRequestFilter Copy()
    {
        return this;
    }

    public int Priority
    {
        get { return -100; }
    }

public void RequestFilter(IHttpRequest req, IHttpResponse res, object requestDto)
{
    var query = req.QueryString["q"] ?? req.QueryString["query"];
    var limit = req.QueryString["limit"];
    var offset = req.QueryString["offset"];

    var user = requestDto as QueryBase;
    if (user == null) { return; }
    user.Query = query;
    user.Limit = limit.IsEmpty() ? int.MaxValue : int.Parse(limit);
    user.Offset = offset.IsEmpty() ? 0 : int.Parse(offset);
    }

#endregion
}

[Route("/v1/users/search", "GET")]
[RequestFilter]
public class SearchUser : QueryBase, IReturn<PagedResult<User>> { }

public class UsersService : Service
{
    public static List<User> UserRepository = new List<User>
    {
        new User{ Id="1", FirstName = "Michael", LastName = "A", Email = "michaelEmail" },
        new User{ Id="2", FirstName = "Robert", LastName = "B", Email = "RobertEmail" },
        new User{ Id="3", FirstName = "Khris", LastName = "C", Email = "KhrisEmail" },
        new User{ Id="4", FirstName = "John", LastName = "D", Email = "JohnEmail" },
        new User{ Id="4", FirstName = "Lisa", LastName = "E", Email = "LisaEmail" }
    };

    public PagedResult<User> Get(SearchUser request)
{
    var query = request.Query;
    var users = request.Query.IsNullOrEmpty()
                    ? UserRepository.ToList()
                    : UserRepository.Where(x => x.FirstName.Contains(query) || x.LastName.Contains(query) || x.Email.Contains(query)).ToList();

    var totalItems = users.Count;
    var totalPages = (int)Math.Ceiling((decimal)totalItems / (decimal)request.Limit);
    var currentPage = request.Offset;
    users = users.Skip(request.Offset * request.Limit).Take(request.Limit).ToList();
    var itemCount = users.Count;

    return new PagedResult<User>
        {
            TotalItems = totalItems,
            TotalPages = totalPages,
            ItemCount = itemCount,
            Items = users,
            CurrentPage = currentPage
        };
}
}