具有默认参数的空模型需要这样实例化
Null Models with default arguments need to be instantiated as such
我有以下使用 .NET 4.6 的 asp.net WebApi2 路由,它说明了我遇到的问题:
[Route("books/{id}")]
[HttpGet]
public JsonResponse GetBooks(string id, [FromUri]DescriptorModel model)
使用以下模型:
public class DescriptorModel
{
public bool Fiction { get; set; } = false;
// other properties with default arguments here
}
我正在尝试将 Fiction 属性 设置为默认值(如果在获取请求期间未指定)。
当我明确指定小说 属性 时,它可以正常工作:
curl -X GET --header 'Accept: application/json' 'http://127.0.0.1:11000/api/v1/books/516.375/?Fiction=false'
但是,在进行以下测试时(省略带有默认参数的 属性):
curl -X GET --header 'Accept: application/json' 'http://127.0.0.1:11000/api/v1/books/516.375'
“model”的值被绑定为 null,这不是我要找的。我的问题是如何简单地允许使用默认值定义的模型实例化 during/after 模型绑定过程,但在调用控制器的“GetBooks”操作方法之前。
注意。我将模型与 GET 请求一起使用的原因是,用 swagger 进行记录要容易得多,因为在许多情况下,我的 GET/POST 操作可以通过继承重用相同的模型。
由于您将 id 用作 FromUri,因此可以将模型与 get 一起使用的唯一方法是将 url 与查询字符串一起使用
[Route("~/GetBooks/{id?}")]
[HttpGet]
public IActionResult GetBooks(string id, [FromQuery] DescriptorModel model)
在这种情况下,你 url 应该是
'http://127.0.0.1:11000/api/v1/books/?Name=name&&fiction=true'
//or if fiction==false just
'http://127.0.0.1:11000/api/v1/books/?Name=name'
//or if want to use id
'http://127.0.0.1:11000/api/v1/books/123/?Name=name&&fiction=true'
按照您的方式使用模型将仅适用于 [FromForm] 或 [FromBody]。
要将其用作 MVC 推荐试试这个
[Route("books/{id}/{param1}/{param2}/{fiction?}")]
[HttpGet]
public JsonResponse GetBooks(string id, string param1, string param2, bool fiction)
顺便说一句,您不需要将 bool 设置为默认值,因为默认情况下它是假的
如果你想使用 uri 中的 ID 和 DescriptorModel,只有你也将 Id 添加到 DescriptorModel 才能做到这一点
[Route("books/{id}/{param1}/{param2}/{fiction?}")]
[HttpGet]
public JsonResponse GetBooks(DescriptorModel model)
更新
如果你的 mvc 不支持 [FromQuery],你可以像这样在 action 中使用 RequestQuery
var value= context.Request.Query["value"];
但最好更新到 MVC 6。
我无法弄清楚如何通过模型绑定来做到这一点,但我能够使用 Action Filters 来完成同样的事情。
这是我使用的代码(请注意,每个操作仅支持一个空模型,但如果需要,可以轻松修复):
public class NullModelActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext context)
{
object value = null;
string modelName = string.Empty;
// are there any null models?
if (context.ActionArguments.ContainsValue(null))
{
// Yes => iterate over all arguments to find them.
foreach (var arg in context.ActionArguments)
{
// Is the argument null?
if (arg.Value == null)
{
// Search the parameter bindings to find the matching argument....
foreach (var parameter in context.ActionDescriptor.ActionBinding.ParameterBindings)
{
// Did we find a match?
if (parameter.Descriptor.ParameterName == arg.Key)
{
// Yes => Does the type have the 'Default' attribute?
var type = parameter.Descriptor.ParameterType;
if (type.GetCustomAttributes(typeof(DefaultAttribute), false).Length > 0)
{
// Yes => need to instantiate it
modelName = arg.Key;
var constructor = parameter.Descriptor.ParameterType.GetConstructor(new Type[0]);
value = constructor.Invoke(null);
// update the model state
context.ModelState.Add(arg.Key, new ModelState { Value = new ValueProviderResult(value, value.ToString(), CultureInfo.InvariantCulture) });
}
}
}
}
}
// update the action arguments
context.ActionArguments[modelName] = value;
}
}
}
我像这样创建了一个 DefaultAttribute class:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class DefaultAttribute : Attribute
{
}
然后我将该属性添加到我的描述符中 class:
[Default]
public class DescriptorModel
{
public bool Fiction { get; set; } = false;
// other properties with default arguments here
}
并最终在
中注册了动作过滤器
public void Configure(IAppBuilder appBuilder)
{
var config = new HttpConfiguration();
// lots of configuration here omitted
config.Filters.Add(new NullModelActionFilter());
appBuilder.UseWebApi(config);
}
我绝对认为这是一个 hack(我认为我真的应该通过模型绑定来做到这一点)但它完成了我需要做的事情,我得到了 ASP.NET(不是核心)/ WebApi2 / .NET Framework 所以希望其他人能从中受益。
我有以下使用 .NET 4.6 的 asp.net WebApi2 路由,它说明了我遇到的问题:
[Route("books/{id}")]
[HttpGet]
public JsonResponse GetBooks(string id, [FromUri]DescriptorModel model)
使用以下模型:
public class DescriptorModel
{
public bool Fiction { get; set; } = false;
// other properties with default arguments here
}
我正在尝试将 Fiction 属性 设置为默认值(如果在获取请求期间未指定)。
当我明确指定小说 属性 时,它可以正常工作:
curl -X GET --header 'Accept: application/json' 'http://127.0.0.1:11000/api/v1/books/516.375/?Fiction=false'
但是,在进行以下测试时(省略带有默认参数的 属性):
curl -X GET --header 'Accept: application/json' 'http://127.0.0.1:11000/api/v1/books/516.375'
“model”的值被绑定为 null,这不是我要找的。我的问题是如何简单地允许使用默认值定义的模型实例化 during/after 模型绑定过程,但在调用控制器的“GetBooks”操作方法之前。
注意。我将模型与 GET 请求一起使用的原因是,用 swagger 进行记录要容易得多,因为在许多情况下,我的 GET/POST 操作可以通过继承重用相同的模型。
由于您将 id 用作 FromUri,因此可以将模型与 get 一起使用的唯一方法是将 url 与查询字符串一起使用
[Route("~/GetBooks/{id?}")]
[HttpGet]
public IActionResult GetBooks(string id, [FromQuery] DescriptorModel model)
在这种情况下,你 url 应该是
'http://127.0.0.1:11000/api/v1/books/?Name=name&&fiction=true'
//or if fiction==false just
'http://127.0.0.1:11000/api/v1/books/?Name=name'
//or if want to use id
'http://127.0.0.1:11000/api/v1/books/123/?Name=name&&fiction=true'
按照您的方式使用模型将仅适用于 [FromForm] 或 [FromBody]。 要将其用作 MVC 推荐试试这个
[Route("books/{id}/{param1}/{param2}/{fiction?}")]
[HttpGet]
public JsonResponse GetBooks(string id, string param1, string param2, bool fiction)
顺便说一句,您不需要将 bool 设置为默认值,因为默认情况下它是假的
如果你想使用 uri 中的 ID 和 DescriptorModel,只有你也将 Id 添加到 DescriptorModel 才能做到这一点
[Route("books/{id}/{param1}/{param2}/{fiction?}")]
[HttpGet]
public JsonResponse GetBooks(DescriptorModel model)
更新
如果你的 mvc 不支持 [FromQuery],你可以像这样在 action 中使用 RequestQuery
var value= context.Request.Query["value"];
但最好更新到 MVC 6。
我无法弄清楚如何通过模型绑定来做到这一点,但我能够使用 Action Filters 来完成同样的事情。
这是我使用的代码(请注意,每个操作仅支持一个空模型,但如果需要,可以轻松修复):
public class NullModelActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext context)
{
object value = null;
string modelName = string.Empty;
// are there any null models?
if (context.ActionArguments.ContainsValue(null))
{
// Yes => iterate over all arguments to find them.
foreach (var arg in context.ActionArguments)
{
// Is the argument null?
if (arg.Value == null)
{
// Search the parameter bindings to find the matching argument....
foreach (var parameter in context.ActionDescriptor.ActionBinding.ParameterBindings)
{
// Did we find a match?
if (parameter.Descriptor.ParameterName == arg.Key)
{
// Yes => Does the type have the 'Default' attribute?
var type = parameter.Descriptor.ParameterType;
if (type.GetCustomAttributes(typeof(DefaultAttribute), false).Length > 0)
{
// Yes => need to instantiate it
modelName = arg.Key;
var constructor = parameter.Descriptor.ParameterType.GetConstructor(new Type[0]);
value = constructor.Invoke(null);
// update the model state
context.ModelState.Add(arg.Key, new ModelState { Value = new ValueProviderResult(value, value.ToString(), CultureInfo.InvariantCulture) });
}
}
}
}
}
// update the action arguments
context.ActionArguments[modelName] = value;
}
}
}
我像这样创建了一个 DefaultAttribute class:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class DefaultAttribute : Attribute
{
}
然后我将该属性添加到我的描述符中 class:
[Default]
public class DescriptorModel
{
public bool Fiction { get; set; } = false;
// other properties with default arguments here
}
并最终在
中注册了动作过滤器public void Configure(IAppBuilder appBuilder)
{
var config = new HttpConfiguration();
// lots of configuration here omitted
config.Filters.Add(new NullModelActionFilter());
appBuilder.UseWebApi(config);
}
我绝对认为这是一个 hack(我认为我真的应该通过模型绑定来做到这一点)但它完成了我需要做的事情,我得到了 ASP.NET(不是核心)/ WebApi2 / .NET Framework 所以希望其他人能从中受益。