Как удалить префикс ModelState в сообщениях об ошибках в ASP.NET Core 2.0? - PullRequest
0 голосов
/ 02 мая 2018

Я работаю над API ASP.NET Core 2.0, который будет использоваться моими клиентами. Одна из проблем, с которыми я сталкиваюсь, заключается в том, что когда я использую ModelState для проверки входных данных полезной нагрузки запроса, полученное сообщение об ошибке, которое видит потребитель, имеет [objectPrefix] .PropertyName в ответном JSON. В нашей документации API указано имя свойства, но не класс объекта, поэтому префикс создает проблему, когда потребитель пишет код, десериализующий ответ JSON, в свою локальную объектную модель.

Есть ли какая-либо опция, которую я могу установить в методе Startup.cs ConfigureServices для Service.AddMvc, или что-то подобное, которое отключит этот префикс?

Я использую зависимость Microsoft.AspNetCore.All (2.0.7) в моем API, .NET Core 2.0.4 и VS2016 v15.5.7, если это имеет значение.

Я использую аннотации данных из библиотеки System.ComponentModel.DataAnnotations и украшаю свойства моего класса создания 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

Кажется, что проблема с префиксом связана с моим использованием метода Validate в моем * ForCreationDtos.

Например,

    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]". в качестве префикса, но это тот, который я получаю всякий раз, когда я реализую метод Validate в классе DTO. Кажется, это решило мою конкретную проблему.

1 Ответ

0 голосов
/ 15 мая 2018

Я использую Microsoft.AspNetCore.All v2.0.8, Microsoft.NETCore.App v2.0.7 и Visual Studio Community 2017 v15.7.1, и все получилось так, как вы хотите.

Снимок экрана № 1: Нет номера счета - 400

No account number - 400

Снимок экрана № 2: слишком длинный номер счета - 400

Account number too long - 400

Снимок экрана № 3: Действительный номер счета - 201

Valid account number - 201

Я не могу воспроизвести вашу проблему. Я даже подумал, что, может быть, я только что создал модель в веб-проекте, поэтому я даже создал отдельный проект класса, содержащий 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();
        }
    }
}
...