代码之家  ›  专栏  ›  技术社区  ›  Umair

如果存在无效的属性值,则Asp.net核心不绑定后期模型

  •  2
  • Umair  · 技术社区  · 7 年前

    我正在将应用程序从传统的asp.net webapi迁移到asp.net核心mvc。我注意到一个问题对于某些请求,我们在POST主体中发送部分甚至无效的值。而asp.net内核拒绝对其进行反序列化。

    例如柱模型

    public class PostModel
    {
        public int Id { get; set; }
    
        public Category? Category { get; set; }
    }
    
    public enum Category
    {
        Public,
        Personal
    }
    

    行动

    [HttpPost]
    public async Task<Response> Post([FromBody]PostModel model)
        => this.Service.Execute(model);
    

    对于以下示例请求

    POST /endpoint
    {
        id: 3,
        category: "all"
    }
    

    这个 ModelState 集合记录错误-表示 all 是无效类别,并且 PostModel 论点 model 为空。是否可以禁用此行为并尝试绑定post主体中可能的所有属性,而忽略它无法绑定的属性?这就是在我们的遗留api中为我们所做的,现在,我需要将它移植到另一个api中。

    禁用模型验证对我们没有帮助。这个 模型 参数仍然为空。

    3 回复  |  直到 7 年前
        1
  •  1
  •   Edward    7 年前

    为了 FromBody ,它将绑定 request body Model 通过 JsonInputFormatter .

    为了 JsonInputFormatter ,它将调用 return InputFormatterResult.Success(model) 如果没有错误,请致电 return InputFormatterResult.Failure(); 如果有任何错误。为了 返回inputFormatResult.Failure(); ,它将不会绑定有效属性。

    对于解决方案,可以实现自定义格式化程序以返回 返回inputformattresult.Success(model) .

    1. 实现自定义格式化程序 CustomFormatter 基于 JsonInputFormatter .
    2. 替换 InputFormatterResult.Failure() 具有 InputFormatterResult.Success(model) .

                      if (!(exception is JsonException || exception is OverflowException))
                  {
                      var exceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception);
                      exceptionDispatchInfo.Throw();
                  }
                  return InputFormatterResult.Success(model);
      
    3. 注入 自定义格式化程序 在里面 Startup.cs

              services.AddMvc(o =>
          {
              var serviceProvider = services.BuildServiceProvider();
              var customJsonInputFormatter = new CustomFormatter(
                       serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger<CustomFormatter>(),
                       serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value.SerializerSettings,
                       serviceProvider.GetRequiredService<ArrayPool<char>>(),
                       serviceProvider.GetRequiredService<ObjectPoolProvider>(),
                       o,
                       serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value
                  );
      
              o.InputFormatters.Insert(0, customJsonInputFormatter);
          }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
      
        2
  •  1
  •   Alex Riabov Vikrant    7 年前

    实际上,您的问题与数据绑定有关,而与验证无关,这就是禁用模型验证没有帮助的原因。您可以实现自定义绑定器并将其配置为手动绑定您的属性,例如:

    public class PostModelBinder : IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }
    
            string valueFromBody = string.Empty;
    
            using (var sr = new StreamReader(bindingContext.HttpContext.Request.Body))
            {
                valueFromBody = sr.ReadToEnd();
            }
    
            if (string.IsNullOrEmpty(valueFromBody))
            {
                return Task.CompletedTask;
            }
    
            string idString = Convert.ToString(((JValue)JObject.Parse(valueFromBody)["id"]).Value);
            string categoryString = Convert.ToString(((JValue)JObject.Parse(valueFromBody)["category"]).Value);
    
            if (string.IsNullOrEmpty(idString) || !int.TryParse(idString, out int id))
            {
                return Task.CompletedTask;
            }
    
            Category? category = null;
    
            if(Enum.TryParse(categoryString, out Category parsedCategory))
            {
                category = parsedCategory;
            }
    
            bindingContext.Result = ModelBindingResult.Success(new PostModel()
            {
                Id = id,
                Category = category
            });
    
            return Task.CompletedTask;
        }
    }
    

    然后您可以将此活页夹应用于您的课程:

    [ModelBinder(BinderType = typeof(PostModelBinder))]
    public class PostModel
    {
        public int Id { get; set; }
    
        public Category? Category { get; set; }
    }
    

    或采取行动:

    [HttpPost]
    public async Task<Response> Post([ModelBinder(BinderType = typeof(PostModelBinder))][FromBody]PostModel model)
        => this.Service.Execute(model);
    

    或创建CustomModelBinderProvider:

    public class CustomModelBinderProvider : IModelBinderProvider  
    {  
        public IModelBinder GetBinder(ModelBinderProviderContext context)  
        {  
            if (context.Metadata.ModelType == typeof(PostModel))  
                return new PostModelBinder();  
    
            return null;  
        }  
    }
    

    并将其注册到启动类的ConfigureServices方法中:

    public void ConfigureServices(IServiceCollection services)  
    {  
        ...
        services.AddMvc(  
            config => config.ModelBinderProviders.Insert(0, new CustomModelBinderProvider())  
        ); 
        ... 
    } 
    
        3
  •  0
  •   Rahul    7 年前

    不,因为属性绑定到枚举。如果你真的想成为你发布的内容,那么把模型改成

    public class PostModel
    {
        public int Id { get; set; }
    
        public string Category { get; set; }
    }
    

    然后在端点中将字符串解析为枚举

    Enum.TryParse("All", out Category cat);
    
    推荐文章