简体   繁体   English

如何使用 appSettings.json 中的值自动设置 API 模型属性的默认值?

[英]How to automatically set default value of API model's property with value from appSettings.json?

I'm creating a web API in Core 3.1 and I have PagingParams model that I will use on many controller actions. I'm creating a web API in Core 3.1 and I have PagingParams model that I will use on many controller actions. The model has PageNumber and PageSize parameters, both of which have a default value: model 有PageNumberPageSize参数,这两个参数都有一个默认值:

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
}

The PageSize parameter value needs to be set to 10000 and same with the max value in its Range attribute. PageSize参数值需要设置为 10000 并且与其Range属性中的最大值相同。 This prevents the user from getting more than 10k items per page.这可以防止用户每页获得超过 10k 个项目。

I want to be able to retrieve that value from appSettings.json so I can modify it without republishing the the application each time.我希望能够从appSettings.json中检索该值,这样我就可以修改它而无需每次都重新发布应用程序。 Throughout the code I've been using IOptionsSnapshot to read values from appSettings.json , but it appears that can't be done here because the model is required to have a parameterless constructor, resulting in the following error:在整个代码中,我一直在使用IOptionsSnapshotappSettings.json读取值,但这里似乎无法完成,因为 model 需要具有无参数构造函数,导致以下错误:

System.InvalidOperationException: Could not create an instance of type 'WebAPI.Models.PagingParams'. System.InvalidOperationException:无法创建“WebAPI.Models.PagingParams”类型的实例。 Model bound complex types must not be abstract or value types and must have a parameterless constructor. Model 绑定的复杂类型不能是抽象类型或值类型,并且必须具有无参数构造函数。 Alternatively, give the 'pagingParams' parameter a non-null default value.或者,给“pagingParams”参数一个非空的默认值。

How could I set up the model so that by default the int is taken from appSettings and set in the two places?我如何设置 model 以便默认情况下从 appSettings 获取int并在两个地方设置?

This is how the class is used:这就是 class 的使用方式:

[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;
        }
    }
}

NOTE: My question is similar to this one , except that I want to be able to set this up so it's automated.注意:我的问题类似于这个问题,除了我希望能够设置它以使其自动化。 I want to avoid calling a method each time I use the PagingParams class on an action and then having to manually call ModelState.IsValid and return the model error.我想避免每次在操作上使用PagingParams class 时调用方法,然后必须手动调用ModelState.IsValid并返回 model 错误。 With my current set up, the Range attribute automatically handles returning of a model error and I don't have to do anything further.使用我当前的设置, Range属性会自动处理返回 model 错误,我不需要做任何进一步的事情。

What you want is to conveniently have some properties initialized with some default values.您想要的是方便地使用一些默认值初始化一些属性。 The core mechanism is just simple like you instantiate an object and set its properties before it's being used in the next pipelines.核心机制很简单,就像您实例化 object 并在将其用于下一个管道之前设置其属性。 There are some ways to achieve it:有一些方法可以实现它:

  • Set the properties right in the class declaration: This is not what you want because you want to populate the default values from some settings (at runtime).在 class 声明中正确设置属性:这不是您想要的,因为您想从某些设置中填充默认值(在运行时)。
  • Set the properties everywhere you have the object: This means in the current context, you need the default values loaded from the settings/configuration so that you can initialize the properties.在您拥有 object 的任何地方设置属性:这意味着在当前上下文中,您需要从设置/配置中加载默认值,以便您可以初始化属性。

Usually you do it the second way.通常你用第二种方法。 However to avoid repetitive code as much as possible, we need to find a point in the pipeline (code execution - request processing flow) where you can obtain the object and initialize its properties (loaded from configuration) in one place.但是,为了尽可能避免重复代码,我们需要在管道(代码执行 - 请求处理流程)中找到一个点,您可以在其中获取 object 并在一个地方初始化其属性(从配置加载)。

It's fairly easy that in your case the PagingParams is not instantiated everywhere in your code, it just comes from controller actions (instantiated by model binding).在您的情况下, PagingParams没有在代码中的任何地方实例化,这相当容易,它只是来自 controller 动作(由 model 绑定实例化)。 So in that case we can use action filter (either IActionFilter or IAsyncActionFilter works) as the point we can obtain the argument (of type PagingParams ) and modify it in whatever way we want (initialize the properties with loaded default values from the configuration).因此,在这种情况下,我们可以使用动作过滤器( IActionFilterIAsyncActionFilter可以)作为我们可以获得参数(类型为PagingParams )并以我们想要的任何方式修改它(使用配置中加载的默认值初始化属性)的点。

It's pretty simple like this:这很简单:

public class InitPagingParamsAttribute : IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext context)
    {
        
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        var pp = context.ActionArguments.Values.OfType<PagingParams>().FirstOrDefault();
        //this means there is at least 1 PagingParams in the controller action
        //NOTE that you can get all args of PagingParams, but looks like usually 
        //there should be just 1 arg of such type (in one same controller action)
        if (pp != null)
        {
            //modify the pp here
            //...
        }
    }
}

In the Startup.cs file, you can configure the MVC middleware to register the global action filter.Startup.cs文件中,您可以配置 MVC 中间件以注册全局操作过滤器。 Without doing that, you need to decorate the attribute InitPagingParamsAttribute on the controller or action method you want.如果不这样做,您需要在 controller 或您想要的操作方法上装饰属性InitPagingParamsAttribute

public void ConfigureServices(IServiceCollection services){
    //...
    services.AddMvc(o => {
        o.Filters.Add<InitPagingParamsAttribute>();
    });
    //...
}

NOTE: you can inject your IOptionsSnapshot in the InitPagingParamsAttribute constructor.注意:您可以在InitPagingParamsAttribute构造函数中注入您的IOptionsSnapshot So you can load the default values from the configuration there.因此,您可以从那里的配置中加载默认值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM