如何在ASP.NET Core 2.0中删除错误消息上的ModelState前缀?

时间:2018-05-02 16:47:50

标签: c# .net-core asp.net-core-2.0 modelstate asp.net-core-mvc-2.0

我正在开发一个将由我的客户使用的ASP.NET Core 2.0 API。我遇到的一个问题是,当我使用ModelState对请求有效负载进行输入验证时,消费者看到的结果错误消息在响应JSON中有一个[objectPrefix] .PropertyName。我们的API文档列出了属性名称而不是对象类,因此当使用者编写将JSON响应反序列化到其本地对象模型的代码时,前缀会产生问题。

我可以在Service.AddMvc的Startup.cs ConfigureServices方法中设置任何选项,或类似的东西,这将禁用此前缀吗?

如果重要的话,我在我的API,.NET Core 2.0.4和VS2016 v15.5.7中使用了Microsoft.AspNetCore.All(2.0.7)依赖项。

我正在使用System.ComponentModel.DataAnnotations lib中的Data Annotations并装饰我的创建DTO类属性,如下所示;

    [Required]
    [MaxLength(14)]
    public string AccountNumber
    {
        get => _accountNumber;
        set => _accountNumber = !string.IsNullOrWhiteSpace(value) ? value.Trim() : string.Empty;
    }

当消费者未在请求有效负载中提供帐号时,返回的错误如下所示;

{
    "[AccountDto].AccountNumber": [
        "The AccountNumber field is required."
    ]
}

我想要做的是消除[AccountDto]。前缀,以便错误JSON然后看起来像这样;

{
    "AccountNumber": [
        "The AccountNumber field is required."
    ]
}

我发现了这个SO帖子,但它似乎引用了较旧的ASP.NET。

目前,我让我的客户端对json响应进行字符串替换,但我真的希望有更好的解决方案。

有什么想法吗?

更新5/16/18

似乎前缀的问题与我在* ForCreationDtos中使用Validate方法有关。

例如,

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {

        if (CompanyId == 0)
        {
            yield return new ValidationResult("A Company ID is required.", new[] { "CompanyId" });
        }

    }

但是,我找到了一个解决方法,使用全局ModelState处理程序并修改它以解析出前缀。

public class ValidateModelAttribute : ActionFilterAttribute
{

    /// <summary>
    /// Validates model state upon action execution
    /// </summary>
    /// <param name="context">ActionExecutingContext object</param>
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (context.ModelState.IsValid) return;
        var errorList = context.ModelState.Where(ms => ms.Value.Errors.Any()).ToDictionary(
            kvp => kvp.Key.Replace("[0].", ""),
            kvp => kvp.Value.Errors.Select(e => string.IsNullOrEmpty(e.ErrorMessage) ? e.Exception.Message : e.ErrorMessage).ToArray()
        );
        var globalErrorDto = new GlobalErrorDto { Errors = errorList };
        context.Result = new BadRequestObjectResult(globalErrorDto);
    }
}

这有点粗糙,并假设“[0]。”作为前缀,但这是我在DTO类中实现Validate方法时得到的那个。这似乎解决了我的具体问题。

1 个答案:

答案 0 :(得分:1)

我正在使用Microsoft.AspNetCore.All v2.0.8,Microsoft.NETCore.App v2.0.7和Visual Studio社区2017 v15.7.1,所有内容都符合您的要求。

屏幕截图#1:没有帐号 - 400

No account number - 400

屏幕截图#2:帐号太长 - 400

Account number too long - 400

屏幕截图#3:有效帐号 - 201

Valid account number - 201

我无法重现您的问题。我甚至认为我可能只是在Web项目中创建了模型,所以我甚至创建了一个单独的类项目来包含DTO。它仍然像你想要的那样工作!

DTO

using System.ComponentModel.DataAnnotations;

namespace DL.SO.ModelState.Dto.Users
{
    public class AccountModel
    {
        [Required]
        [MaxLength(14)]
        [Display(Name = "account number")]
        public string AccountNumber { get; set; }
    }
}

控制器

using DL.SO.ModelState.Dto.Users;
using Microsoft.AspNetCore.Mvc;

namespace DL.SO.ModelState.Controllers
{
    [Route("api/[controller]")]
    public class UsersController : ControllerBase
    {
        [HttpGet("{id}")]
        public IActionResult GetById(string id)
        {
            // Just testing 
            return Ok(id);
        }

        [HttpPost]
        public IActionResult Post(AccountModel model)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            // Just testing so I pass in null
            return CreatedAtAction(nameof(GetById), 
                 new { id = model.AccountNumber }, null);
        }
    }
}

启动

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace DL.SO.ModelState
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();
        }
    }
}