有条件地调用 webApi
Conditional call to webApi
我在数据库中有一个 actors
的集合,我可以通过以下对网络的调用获得这些 Api:
HttpResponseMessage response = await client.GetAsync("api/actors");
这会在我的 webapi 控制器上触发以下方法:
// GET: api/Actors
public IQueryable<Actor> GetActors()
{
return db.Actors;
}
现在假设我对获取所有获得奥斯卡奖的演员感兴趣。在没有 webapi 的情况下直接调用数据库可能看起来像这样:
return db.actors.where(o=>o.OscarWinner==true);
但是如何使用网络进行类似的调用Api?
Api 是否仅限于让我获取所有演员:
HttpResponseMessage response = await client.GetAsync("api/actors");
或一位特定演员:
HttpResponseMessage response = await client.GetAsync("api/actors/1");
我认为这一定是一个常见的请求,能够根据某些条件获取对象列表?
编辑:
public IQueryable<Actor> GetUsers(int CountrId)
{
var list = db.Actors.Where(o => o.CountryId == CountryId);
return list;
}
引自专业人士ASP.NET MVC 4 书
To accept incoming values from the request, you can put parameters on you action and just like MVC, the Web API framework will automatically provide values for those action parameters. Unlike MVC, there is a strong line drawn between values from the HTTP body and values taken from other places (like from the URI).
By default, Web API will assume that parameters which are simple types (that is the intrinsic types, strings, dates, times, and anything with a type converter from strings) are taken from non-body values, and complex types (everything else) are taken from the body. There is an additional restriction as well: Only a single value can come from the body, and that value must represent the entirety of the body.
所以你有两个选择。如果您只想使用简单的过滤器,从查询字符串中获取它就足够了。更好的选择是编写一个 class,它将从 JSON 请求主体映射其属性。我将提供一个示例,我是如何从查询字符串映射过滤器数据的,因为我想让我的用户通过查询字符串查询数据。我写了一个与 ModelBinder 绑定的过滤器 class。
1。查询字符串
HttpResponseMessage response = await client.GetAsync("api/actors/?oscarWinner=true");
相应的动作方法
// GET: api/Actors
public IQueryable<Actor> GetActors(bool oscarWinner = false)
{
return db.Actors.Where(actor=>actor.OscarWinner == oscarWinner);
}
2。模型绑定器
条件过滤器Class
/// <summary>
/// Condition Filter
/// </summary>
public class ConditionFilter
{
/// <summary>
/// Oscar Winner
/// </summary>
public bool? OscarWinner { get; set; }
/// <summary>
/// Page you want to get. Counting from 0.
/// </summary>
public int? Page { get; set; }
/// <summary>
/// Number of jobs on page. Default page size 1000.
/// </summary>
public int? PageSize { get; set; }
}
条件过滤器绑定器
public class ConditionFilterBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType != typeof (ConditionFilter))
{
return false;
}
var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (val == null)
{
return false;
}
var queryString = HttpUtility.ParseQueryString(val.RawValue.ToString());
var conditionFilter = new ConditionFilter
{
Page = queryString.GetValueFromQueryString<int>("page"),
PageSize = queryString.GetValueFromQueryString<int>("pageSize"),
OscarWinner = queryString.GetValueFromQueryString<bool>("oscarWinner")
};
bindingContext.Model = conditionFilter;
return true;
}
}
}
并将其与您的操作方法一起使用
public List<Actor> GetActors([ModelBinder(BinderType = typeof(ConditionFilterBinder))] ConditionFilter conditionFilter = null)
{
//just to be sure, that we will not take some huge amount of data from database
const int MAX_ACTOR_COUNT = 1000;
var page = 0;
var pageSize = MAX_ACTOR_COUNT;
var actors = db.Actors;
if (conditionFilter != null)
{
if(conditionFilter.OscarWinner.HasValue && conditionFilter.OscarWinner.Value)
{
actors = actors.Where(actor => actor.OscarWinner)
}
if (conditionFilter.Page.HasValue)
{
page = conditionFilter.Page.Value;
}
if (conditionFilter.PageSize.HasValue)
{
pageSize = conditionFilter.PageSize.Value > MAX_ACTOR_COUNT ? MAX_ACTOR_COUNT : conditionFilter.PageSize.Value;
}
}
return actors.Skip(page * pageSize).Take(pageSize);
}
使用此实现,您可以像这样调用您的操作方法
HttpResponseMessage response = await client.GetAsync("api/actors?oscarWinner=true&page=3&pageSize=100");
补充一下,这是我的 GetValueFromQueryString
帮手 class
/// <summary>
/// Extension method for name value collection
/// </summary>
public static class NameValueCollectionExtensions
{
/// <summary>
/// Get typed value from query string
/// </summary>
/// <param name="queryString"></param>
/// <param name="value"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T? GetValueFromQueryString<T>(this NameValueCollection queryString, string value) where T : struct, IConvertible
{
if (queryString[value] != null)
{
var thisType = default(T);
var typeCode = thisType.GetTypeCode();
switch (typeCode)
{
case TypeCode.Boolean:
{
bool queryStringValue;
if (bool.TryParse(queryString[value], out queryStringValue))
{
return (T)Convert.ChangeType(queryStringValue, typeCode);
}
}
break;
case TypeCode.Int32:
case TypeCode.Int64:
{
if (typeof(T).IsEnum)
{
var numberValue = queryString.GetValueFromQueryString<int>(value);
if (numberValue.HasValue && Enum.IsDefined(typeof(T), numberValue.Value))
{
return (T)(object)numberValue;
}
else
{
T queryStringValueForEnum;
if (Enum.TryParse(queryString[value], true, out queryStringValueForEnum))
{
return queryStringValueForEnum;
}
}
}
int queryStringValue;
if (int.TryParse(queryString[value], out queryStringValue))
{
return (T)Convert.ChangeType(queryStringValue, typeCode);
}
}
break;
case TypeCode.DateTime:
{
DateTime queryStringValue;
if (DateTime.TryParse(queryString[value], out queryStringValue))
{
return (T)Convert.ChangeType(queryStringValue, typeCode);
}
break;
}
}
}
return null;
}
/// <summary>
/// Get string value from query string
/// </summary>
/// <param name="queryString"></param>
/// <param name="value"></param>
/// <returns></returns>
public static string GetValueFromQueryString(this NameValueCollection queryString, string value)
{
return queryString[value];
}
}
我在数据库中有一个 actors
的集合,我可以通过以下对网络的调用获得这些 Api:
HttpResponseMessage response = await client.GetAsync("api/actors");
这会在我的 webapi 控制器上触发以下方法:
// GET: api/Actors
public IQueryable<Actor> GetActors()
{
return db.Actors;
}
现在假设我对获取所有获得奥斯卡奖的演员感兴趣。在没有 webapi 的情况下直接调用数据库可能看起来像这样:
return db.actors.where(o=>o.OscarWinner==true);
但是如何使用网络进行类似的调用Api? Api 是否仅限于让我获取所有演员:
HttpResponseMessage response = await client.GetAsync("api/actors");
或一位特定演员:
HttpResponseMessage response = await client.GetAsync("api/actors/1");
我认为这一定是一个常见的请求,能够根据某些条件获取对象列表?
编辑:
public IQueryable<Actor> GetUsers(int CountrId)
{
var list = db.Actors.Where(o => o.CountryId == CountryId);
return list;
}
引自专业人士ASP.NET MVC 4 书
To accept incoming values from the request, you can put parameters on you action and just like MVC, the Web API framework will automatically provide values for those action parameters. Unlike MVC, there is a strong line drawn between values from the HTTP body and values taken from other places (like from the URI).
By default, Web API will assume that parameters which are simple types (that is the intrinsic types, strings, dates, times, and anything with a type converter from strings) are taken from non-body values, and complex types (everything else) are taken from the body. There is an additional restriction as well: Only a single value can come from the body, and that value must represent the entirety of the body.
所以你有两个选择。如果您只想使用简单的过滤器,从查询字符串中获取它就足够了。更好的选择是编写一个 class,它将从 JSON 请求主体映射其属性。我将提供一个示例,我是如何从查询字符串映射过滤器数据的,因为我想让我的用户通过查询字符串查询数据。我写了一个与 ModelBinder 绑定的过滤器 class。
1。查询字符串
HttpResponseMessage response = await client.GetAsync("api/actors/?oscarWinner=true");
相应的动作方法
// GET: api/Actors
public IQueryable<Actor> GetActors(bool oscarWinner = false)
{
return db.Actors.Where(actor=>actor.OscarWinner == oscarWinner);
}
2。模型绑定器
条件过滤器Class
/// <summary>
/// Condition Filter
/// </summary>
public class ConditionFilter
{
/// <summary>
/// Oscar Winner
/// </summary>
public bool? OscarWinner { get; set; }
/// <summary>
/// Page you want to get. Counting from 0.
/// </summary>
public int? Page { get; set; }
/// <summary>
/// Number of jobs on page. Default page size 1000.
/// </summary>
public int? PageSize { get; set; }
}
条件过滤器绑定器
public class ConditionFilterBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType != typeof (ConditionFilter))
{
return false;
}
var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (val == null)
{
return false;
}
var queryString = HttpUtility.ParseQueryString(val.RawValue.ToString());
var conditionFilter = new ConditionFilter
{
Page = queryString.GetValueFromQueryString<int>("page"),
PageSize = queryString.GetValueFromQueryString<int>("pageSize"),
OscarWinner = queryString.GetValueFromQueryString<bool>("oscarWinner")
};
bindingContext.Model = conditionFilter;
return true;
}
}
}
并将其与您的操作方法一起使用
public List<Actor> GetActors([ModelBinder(BinderType = typeof(ConditionFilterBinder))] ConditionFilter conditionFilter = null)
{
//just to be sure, that we will not take some huge amount of data from database
const int MAX_ACTOR_COUNT = 1000;
var page = 0;
var pageSize = MAX_ACTOR_COUNT;
var actors = db.Actors;
if (conditionFilter != null)
{
if(conditionFilter.OscarWinner.HasValue && conditionFilter.OscarWinner.Value)
{
actors = actors.Where(actor => actor.OscarWinner)
}
if (conditionFilter.Page.HasValue)
{
page = conditionFilter.Page.Value;
}
if (conditionFilter.PageSize.HasValue)
{
pageSize = conditionFilter.PageSize.Value > MAX_ACTOR_COUNT ? MAX_ACTOR_COUNT : conditionFilter.PageSize.Value;
}
}
return actors.Skip(page * pageSize).Take(pageSize);
}
使用此实现,您可以像这样调用您的操作方法
HttpResponseMessage response = await client.GetAsync("api/actors?oscarWinner=true&page=3&pageSize=100");
补充一下,这是我的 GetValueFromQueryString
帮手 class
/// <summary>
/// Extension method for name value collection
/// </summary>
public static class NameValueCollectionExtensions
{
/// <summary>
/// Get typed value from query string
/// </summary>
/// <param name="queryString"></param>
/// <param name="value"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T? GetValueFromQueryString<T>(this NameValueCollection queryString, string value) where T : struct, IConvertible
{
if (queryString[value] != null)
{
var thisType = default(T);
var typeCode = thisType.GetTypeCode();
switch (typeCode)
{
case TypeCode.Boolean:
{
bool queryStringValue;
if (bool.TryParse(queryString[value], out queryStringValue))
{
return (T)Convert.ChangeType(queryStringValue, typeCode);
}
}
break;
case TypeCode.Int32:
case TypeCode.Int64:
{
if (typeof(T).IsEnum)
{
var numberValue = queryString.GetValueFromQueryString<int>(value);
if (numberValue.HasValue && Enum.IsDefined(typeof(T), numberValue.Value))
{
return (T)(object)numberValue;
}
else
{
T queryStringValueForEnum;
if (Enum.TryParse(queryString[value], true, out queryStringValueForEnum))
{
return queryStringValueForEnum;
}
}
}
int queryStringValue;
if (int.TryParse(queryString[value], out queryStringValue))
{
return (T)Convert.ChangeType(queryStringValue, typeCode);
}
}
break;
case TypeCode.DateTime:
{
DateTime queryStringValue;
if (DateTime.TryParse(queryString[value], out queryStringValue))
{
return (T)Convert.ChangeType(queryStringValue, typeCode);
}
break;
}
}
}
return null;
}
/// <summary>
/// Get string value from query string
/// </summary>
/// <param name="queryString"></param>
/// <param name="value"></param>
/// <returns></returns>
public static string GetValueFromQueryString(this NameValueCollection queryString, string value)
{
return queryString[value];
}
}