【发布时间】:2021-01-04 15:17:45
【问题描述】:
我正在 Core 3.1 中创建一个 Web API,我有 PagingParams 模型,我将在许多控制器操作中使用它。该模型有PageNumber和PageSize参数,这两个参数都有一个默认值:
public class PagingParams
{
[Range(1, Int32.MaxValue, ErrorMessage = "The field {0} must be greater than 0.")]
public int? PageNumber { get; set; } = 1;
[Range(1, 10000)] //TODO: Get 10000 from appsettings
public int? PageSize { get; set; } = 10000; //TODO: Get 10000 from appsettings
}
PageSize 参数值需要设置为 10000,并且与 Range 属性中的最大值相同。这可以防止用户每页获得超过 10k 个项目。
我希望能够从appSettings.json 中检索该值,这样我就可以修改它而无需每次都重新发布应用程序。在整个代码中,我一直使用IOptionsSnapshot 从appSettings.json 中读取值,但这里似乎无法完成,因为模型需要具有无参数构造函数,导致以下错误:
System.InvalidOperationException:无法创建“WebAPI.Models.PagingParams”类型的实例。模型绑定的复杂类型不能是抽象类型或值类型,并且必须具有无参数构造函数。或者,给“pagingParams”参数一个非空的默认值。
如何设置模型,以便默认情况下 int 取自 appSettings 并在两个位置进行设置?
这就是类的使用方式:
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly IMapper _mapper;
private readonly IUserRepo _userRepo;
public MissingNamesController(IMapper mapper, IUserRepo userRepo)
{
_mapper = mapper;
_userRepo = userRepo;
}
[HttpGet]
public IEnumerable<UserDto> Get([FromQuery] UsersQueryParams queryParams, [FromQuery] PagingParams pagingParams)
{
IEnumerable<User> models = _userRepo.Get(queryParams, pagingParams);
IEnumerable<UserDto> dtoModels = _mapper.Map<IEnumerable<UserDto>>(models);
return dtoModels;
}
}
}
注意:我的问题与this one 类似,只是我希望能够进行设置以使其自动化。我想避免每次在操作上使用 PagingParams 类时调用方法,然后必须手动调用 ModelState.IsValid 并返回模型错误。在我目前的设置下,Range 属性会自动处理模型错误的返回,我不需要做任何进一步的事情。
【问题讨论】:
-
这取决于您何时创建/获取
PagingParams的实例,真的。这对于实现你想要的非常重要。为属性预设默认值并不是什么神奇的事情,我们只是实例化对象并正常设置属性,尤其是当它只是一个包含数据/值的对象时(不是通过接口公开的服务或使用 IoC 容器注入的东西)。 -
感谢您的评论。我已经编辑了我的问题以显示
PagingParams的使用位置。它只是动作的参数之一。我不确定具体细节,但我猜想在接收到数据并将其绑定到该对象时会创建一个对象实例。可能只需要编写一些逻辑并手动检查PagingParams对象的属性值。 -
好吧,如果很简单,我只是告诉你机制,你可以搜索更多细节。这个想法是使用 global
IActionFilter(对于 Razor 页面,我们有IAsyncPageFilter)。它将有机会获取(调用的操作/处理程序的)输入参数,因此您可以在在一个地方修改PagingParams。有点像cross-cutting concern。这里的原则是找到一个点(在执行/请求流中)您可以修改您的对象,这样您只需要一个地方来编写您的(修改)代码。在这种情况下,IActionFilter就是其中之一。 -
是的,我曾想过使用动作过滤器,但问题是我必须硬编码参数对象的名称。因此,查看我提供的
Get操作,我需要在过滤器中使用filterContext.Controller.ValueProvider.GetValue("pagingParams").AttemptedValue之类的东西来获取对象。但是"pagingParams"是硬编码的,我需要始终小心确保PagingParams类型的每个动作的参数都被准确地调用pagingParams。如果有的话,也许有办法按类型获取它,但我还没有找到。 -
不,您过滤的是 参数类型,而不是参数名称。完全可行,实施起来也不难。
标签: c# asp.net asp.net-core asp.net-web-api