在我的情况下,ApiController属性无法正常工作,看起来像没用

时间:2020-02-18 09:46:57

标签: c# .net-core asp.net-core-3.1

根据Microsoft enter link description here,我不需要在操作方法中使用以下代码:

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

但这不适用于我的情况。我有以下控制器操作方法:

    [Route("/api/advertisers/create")]
    [HttpPost]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<ActionResult<AdvertiserViewModel>> Create([FromBody] AdvertiserViewModel viewModel)
    {        
        var advertiser = new Advertiser
        {
            Name = viewModel.Name,
            AdvertiserRed2Id = viewModel.InnerId.Value
        };

        await _dbContext.Advertisers.AddAsync(advertiser);
        await _dbContext.SaveChangesAsync();

        return Ok();
    }

以及以下视图模型:

public class AdvertiserViewModel
{
    [Required(ErrorMessage = "Empty inner id!!!!!!")]
    [JsonProperty("inner_id")]
    [DisplayName("inner_id")]
    public int ? InnerId { get; set; }

    [Required(ErrorMessage = "Empty name!!!!!")]
    [JsonProperty("name")]
    [DisplayName("name")]
    public string Name { get; set; }
}

但是在这里我有两个问题。 1)如果InnerId为null,则没有任何验证消息,.NET Core只需将此值写入我的数据库即可。我的意思是,此JSON将不会发出任何验证错误:

{
    "name": "hello!!!",
    "inner_ididididididi": 123234
}

如您所见,我这里没有inner_id。因此,文档中的链接讲的是谎言吗?)还是我不知道如何使用它?我们需要

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

2)第二个问题。我看到了一种趋势,Microsoft正在尝试使所有功能变得更加便捷和简单。没关系。但是我找不到从我的[JsonProperty('some_value')]中查看验证答案的正确方法。我的意思是这里?例如,我们有以下控制器:

    [Route("/api/advertisers/create")]
    [HttpPost]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<ActionResult<AdvertiserViewModel>> Create([FromBody] AdvertiserViewModel viewModel)
    {     
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var advertiser = new Advertiser
        {
            Name = viewModel.Name,
            AdvertiserRed2Id = viewModel.InnerId.Value
        };

        await _dbContext.Advertisers.AddAsync(advertiser);
        await _dbContext.SaveChangesAsync();

        return Ok();
    }

以及以下请求:

{
    "name": "hello!!!",
    "inner_id": null
}

那么,我将得到验证答案吗?以下:

{
  "InnerId": [
    "Empty inner id!!!!!!"
  ]
}

剃须刀和剃须刀都可以。但是我的客户是JS应用程序。 客户不了解InnerId是什么,我的客户只了解inner_id

要让我们有可能从api控制器if (!ModelState.IsValid) ...进行清理,这是一个好的开始,bur为什么没有那么简单的功能(根据请求的JSON字段获取验证答案)?在我看来,[ApiController]可以做到。

根据我的研究,我得到了以下过滤器:

public class DisplayNameValidationFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (context.ModelState.ErrorCount > 0)
        {
            var modelType = context.ActionDescriptor.Parameters
                .FirstOrDefault(p =>
                    p.BindingInfo.BindingSource.Id.Equals("Body", StringComparison.InvariantCultureIgnoreCase) ||
                    p.BindingInfo.BindingSource.Id.Equals("Custom", StringComparison.InvariantCultureIgnoreCase)
                    )
                ?.ParameterType; //Get model type  

            var expandoObj = new ExpandoObject();
            var expandoObjCollection =
                (ICollection<KeyValuePair<String, Object>>)
                expandoObj; //Cannot convert IEnumrable to ExpandoObject  

            var dictionary = context.ModelState.ToDictionary(k => k.Key, v => v.Value)
                .Where(v => v.Value.ValidationState == ModelValidationState.Invalid)
                .ToDictionary(
                    k =>
                    {
                        if (modelType != null)
                        {
                            var property = modelType.GetProperties().FirstOrDefault(p =>
                                p.Name.Equals(k.Key, StringComparison.InvariantCultureIgnoreCase));
                            if (property != null)
                            {
                                //Try to get the attribute  
                                var displayName = property.GetCustomAttributes(typeof(DisplayNameAttribute), true)
                                    .Cast<DisplayNameAttribute>().SingleOrDefault()?.DisplayName;
                                return displayName ?? property.Name;
                            }
                        }

                        return k.Key; //Nothing found, return original vaidation key  
                    },
                    v => v.Value.Errors.Select(e => e.ErrorMessage).ToList() as Object); //Box String collection  
            foreach (var keyValuePair in dictionary)
            {
                expandoObjCollection.Add(keyValuePair);
            }

            dynamic eoDynamic = expandoObj;
            context.Result = new BadRequestObjectResult(eoDynamic);
        }

        base.OnActionExecuting(context);
    }
}

我可以像这样使用它

[DisplayNameValidationFilter]
MyControllerClass : ControllerBase {...} 

但是从我的角度来看,这是一个hack。我被迫将其从一个项目复制到另一个项目。也许包装盒中有一些好的解决方案?

更新

我将向您展示我的Startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers().ConfigureApiBehaviorOptions(options =>
        {
            options.SuppressInferBindingSourcesForParameters = true;
            options.SuppressModelStateInvalidFilter = true;
           // options.SuppressMapClientErrors = true;
        }).AddNewtonsoftJson(); 

        services.AddAutoMapper(typeof(Startup));

        // db context
        services.AddEntityFrameworkNpgsql()
            .AddDbContext<ApplicationDbContext>();

        // services are here
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();

        app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
    }
}

如您所见,我正在将.AddNewtonsoftJson()用于控制器和[JsonProperty(...)](软件包Microsoft.AspNetCore.Mvc.NewtonsoftJson)。

0 个答案:

没有答案