【问题标题】:Custom model binder not firing for a single property in ASP.NET Core 2自定义模型绑定器未针对 ASP.NET Core 2 中的单个属性触发
【发布时间】:2019-09-04 06:16:22
【问题描述】:

我已经尝试过this,但我不认为这是我的情况。 This 也不起作用。

我正在使用 ASP.NET Core 2 Web API。我刚刚创建了一个虚拟模型绑定器(现在它的作用无关紧要):

public class SanitizeModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        var modelName = bindingContext.ModelName;

        return Task.CompletedTask;
    }
}

现在,我有一个模型。这个:

public class UserRegistrationInfo
{
    public string Email { get; set; }

    [ModelBinder(BinderType = typeof(SanitizeModelBinder))]
    public string Password { get; set; }
}

还有一个动作方法:

[AllowAnonymous]
[HttpPost("register")]
public async Task<IActionResult> RegisterAsync([FromBody] UserRegistrationInfo registrationInfo)
{
    var validationResult = validateEmailPassword(registrationInfo.Email, registrationInfo.Password);
    if (validationResult != null) 
    {
        return validationResult;
    }

    var user = await _authenticationService.RegisterAsync(registrationInfo.Email, registrationInfo.Password);

    if (user == null)
    {
        return StatusCode(StatusCodes.Status500InternalServerError, "Couldn't save the user.");
    }
    else
    {
        return Ok(user);
    }
}

如果我从客户端发出 post 请求,我的自定义模型绑定器不会被触发,并且在 action 方法中继续执行。

我尝试过的事情:

ModelBinder 属性应用于整个模型对象:

[ModelBinder(BinderType = typeof(SanitizeModelBinder))]
public class UserRegistrationInfo
{
    public string Email { get; set; }
    public string Password { get; set; }
}

这可行,但对于整个对象,我不希望这样。我希望默认模型绑定器完成其工作,然后仅将我的自定义模型绑定器应用于某些属性。

我读到 hereFromBody 的错,所以我将它从操作方法中删除。它也不起作用。

我尝试在此处将ModelBinder 属性更改为BindProperty

public class UserRegistrationInfo
{
    public string Email { get; set; }

    [BindProperty(BinderType = typeof(SanitizeModelBinder))]
    public string Password { get; set; }
}

但它不起作用。

令人失望的是,本来应该很简单的事情变得相当繁琐,而且分散在几个博客和 github 问题上的信息根本没有帮助。所以,如果你能帮助我,那将不胜感激。

【问题讨论】:

  • 有趣,也许你刚刚发现了一个错误。您的代码看起来正确 (docs.microsoft.com/en-us/aspnet/core/mvc/advanced/…)。在 MVC 存储库中创建问题可能会更好。您也可以尝试使用较新的版本来确定。
  • 删除[FromBody] 后会发生什么?通过这样做,您实际上是从 JSON 正文切换到 application/x-www-form-urlencoded 正文,所以当您删除它时您是否也在更改正文的格式?
  • @KirkLarkin 不,我刚刚删除了[FromBody] 并在操作方法中设置了一个断点。执行操作方法,但不执行自定义模型绑定器。
  • 那么当你这样做时,EmailPassword 有值吗?我想知道这种变化是否真的意味着什么都没有受到任何约束,因为您发送的 JSON 是预期的application/x-www-form-urlencoded(假设您正在使用 JSON)。
  • 这确认您需要自定义 JsonConverter 而不是自定义 IModelBinder。当你说你有时,你并没有真正删除 [FromBody],因为 [ApiController] 推断它。

标签: asp.net asp.net-core asp.net-core-2.0 asp.net-core-webapi custom-model-binder


【解决方案1】:

对于ModelBinder,您需要在客户端使用application/x-www-form-urlencoded,在服务器端使用[FromForm]

对于ApiController,其默认绑定是JsonConverter

按照以下步骤操作:

  1. 改变动作

    [AllowAnonymous]
    [HttpPost("register")]
    public async Task<IActionResult> RegisterAsync([FromForm]UserRegistrationInfo registrationInfo)
    {
        return Ok(registrationInfo);
    }
    
  2. 角度

    post(url: string, model: any): Observable <any> {
        let formData: FormData = new FormData(); 
        formData.append('id', model.id); 
        formData.append('applicationName', model.applicationName); 
        return this._http.post(url, formData)
            .map((response: Response) => {
                return response;
            }).catch(this.handleError); 
    }
    

对于将json 与自定义绑定一起使用,您可以自定义格式化程序,并参考Custom formatters in ASP.NET Core Web API

【讨论】:

  • 我赞成你的回答,因为它显示了解决问题的另一种方法(我很感激),但我的想法是稍后使用移动应用程序中的相同 API,我不知道它是否让移动应用程序发送 URL 编码数据非常合适。以 JSON 格式发送有效负载似乎更“标准”。
  • @amedina for formdata,它发送请求正文中的数据而不是 url 编码数据。 FormDataJson 是绑定数据的两个选项。两者都是标准的。如果你更喜欢json,你需要自定义jsoninput formatter。
  • @TaoZhuo 我知道它将正文内的数据作为 URL 编码的表单数据发送,我的意思是。我将尝试自定义 JSON 输入格式化程序方法。请在您的回答中提及使用自定义 JSON 格式化程序的可能性,以便其他人知道这一点,我将其标记为已接受的答案。
猜你喜欢
  • 2019-03-13
  • 1970-01-01
  • 2017-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多