English 中文(简体)
ASP.NET 核心网络 2. 当数据类型错误时,APIC的错误信息
原标题:ASP.NET Core Web API custom error message when data type is wrong

I have a controller that accepts data from the request s body

[HttpPatch("update-name")]
public async Task<IActionResult> UpdateName([FromBody] UpdateNameData data)
{
    // ...
}
public class UpdateNameData
{
    [Required(ErrorMessage = "Name is required")]
    public string Name { get; set; } = string.Empty;
}

当我发出<条码>{“姓名”: 1 }时,APIC回归这一错误:

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "data": [
      "The data field is required."
    ],
    "$.name": [
      "The JSON value could not be converted to System.String. Path: $.name | LineNumber: 1 | BytePositionInLine: 10."
    ]
  },
  "traceId": "00-4cb41bb3f51e5253c5591ead5fccf96a-ea1c751ebed4be5b-00"
}

我怎么能够这样说,当用户发出错误的类型时,它就会退回一种习惯错误的信息?

问题回答

"The data field is required."

我误解错误信息的含义。 这意味着需要<代码>更新数据

详细解释一下,你之所以获得这一错误信息,是因为你发送了错误的数据类型价值,而具有约束力的模型系统不能对模型的价值加以约束。 因此,附后参数将获得<代码>null模型。 并且从“网络6”中,必须要求“<代码>不可损坏的财产”,否则“示范国家”将是无效的。 正因如此,你收到<代码>的错误。 数据领域是需要的。

If you add the ? which can the parameter nullable:[FromBody] UpdateNameData? data, then you can see it only receives one error message for cannot convert to string.

关于<代码>Name的财产,它不会收到要求验证的错误,因为你寄送了现场,而只是寄出错误的类型值。

如果你发出空洞的json:{><>>>>>>>/code>,你可以收到你希望的海关错误信息,因为你没有发送<条码>Name在json string的现场。

UPDATE

How can I make it so that when the user sends a wrong type it returns a custom error message?

ASP.NET 核心2.1和后版本添加了<代码>[ApiController]属性,自动处理模型验证错误,将其退回到BadRequestObjectResult>。

简单的解决办法是,删除<代码>[ApiController],并完全退回你的错误信息:

if (!ModelState.IsValid)
{
    return BadRequest(new { ErrorMessage = "Cannot deserialize the string" });
}

错误信息,如<代码> JSON数值不能转换为系统。 Sting. xxx是建筑中的错误。 480份答复的缺省答复类型为。 因此,我们将创立一个习俗类别,继承<代码>ValidationProblemDetails,并界定我们的习俗错误信息。

您目前的错误信息是:。 努力。 Path:$.name LineNumber: 1 P-4, 1 P-3, 1 P-2, 1 GS

public class CustomBadRequest : ValidationProblemDetails
{
    public CustomBadRequest(ActionContext context)
    {
        ConstructErrorMessages(context);
        Type = context.HttpContext.TraceIdentifier;
    }

    private void ConstructErrorMessages(ActionContext context)
    {
       //the build-in error message you get
        var myerror = "The JSON value could not be converted to System.String. Path: $.name | LineNumber: 1 | BytePositionInLine: 10.";
        foreach (var keyModelStatePair in context.ModelState)
        {
            var key = keyModelStatePair.Key;
            var errors = keyModelStatePair.Value.Errors;
            if (errors != null && errors.Count > 0)
            {
                if (errors.Count == 1)
                {
                    var errorMessage = GetErrorMessage(errors[0]);
                    if (errorMessage == myerror)
                    {
                        Errors.Add(key, new[] { "The Name must be string" });
                    }
                    else
                    {
                        Errors.Add(key, new[] { errorMessage });
                    }

                }
                else
                {
                    var errorMessages = new string[errors.Count];
                    for (var i = 0; i < errors.Count; i++)
                    {
                        errorMessages[i] = GetErrorMessage(errors[i]);
                        if (errorMessages[i] == myerror)
                        {
                            errorMessages[i] = "The Name must be string";
                        }
                    }

                    Errors.Add(key, errorMessages);
                }
            }
        }
    }

    string GetErrorMessage(ModelError error)
    {
        return string.IsNullOrEmpty(error.ErrorMessage) ?
            "The input was not valid." :
        error.ErrorMessage;
    }
}

方案:

builder.Services.AddControllers().AddXmlSerializerFormatters().ConfigureApiBehaviorOptions(options =>
{
    options.InvalidModelStateResponseFactory = context =>
    {
        var problems = new CustomBadRequest(context);

        return new BadRequestObjectResult(problems);
    };
});

www.un.org/spanish/ecosoc 另一种方式是,如果你不想 man弄错误的信息,你可以习惯JsonConverter:

public class CustomStringConverter : JsonConverter<string>
{
    public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        try
        {
            if (reader.TokenType == JsonTokenType.String)
            {
                // If the token is already a string, read and return it
                return reader.GetString();
            }
            else
            {
                // Handle other token types or unexpected situations
                throw new JsonException("Invalid token type. Expected a string.");
            }
        }
        catch (JsonException ex)
        {
            // Custom error message for JSON serialization failure
            throw new JsonException("Error converting value to string");
        }
    }

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
    {
        writer.WriteStartObject();
        writer.WriteString("Name", value);
        writer.WriteEndObject();
    }
}

你可以简单地回复坏要求的答复:

    builder.Services.AddControllers().AddXmlSerializerFormatters().ConfigureApiBehaviorOptions(options =>
{
    options.InvalidModelStateResponseFactory = context =>
    {
        var problems = context.ModelState.Values.SelectMany(
                    o => o.Errors.Select(
                        e => e.ErrorMessage));

        return new BadRequestObjectResult(problems);
    };
}).AddJsonOptions(options =>
{
    options.JsonSerializerOptions.Converters.Add(new CustomStringConverter());
});

或按惯例,反应方式,如拖欠的巴德雷斯反应:

builder.Services.AddControllers().AddXmlSerializerFormatters().ConfigureApiBehaviorOptions(options =>
{
    options.InvalidModelStateResponseFactory = context =>
    {
        var problems = new CustomBadRequest(context);

        return new BadRequestObjectResult(problems);
    };
}).AddJsonOptions(options =>
{
    options.JsonSerializerOptions.Converters.Add(new CustomStringConverter());
});

习俗:

 public class CustomBadRequest : ValidationProblemDetails
    {
        public CustomBadRequest(ActionContext context)
        {
            ConstructErrorMessages(context);
            Type = context.HttpContext.TraceIdentifier;
        }

        private void ConstructErrorMessages(ActionContext context)
        {
            foreach (var keyModelStatePair in context.ModelState)
            {
                var key = keyModelStatePair.Key;
                var errors = keyModelStatePair.Value.Errors;
                if (errors != null && errors.Count > 0)
                {
                    var errorMessages = new string[errors.Count];
                    for (var i = 0; i < errors.Count; i++)
                    {
                        errorMessages[i] = GetErrorMessage(errors[i]);
                    }

                    Errors.Add(key, errorMessages);
                    
                }
            }
        }

        string GetErrorMessage(ModelError error)
        {
            return string.IsNullOrEmpty(error.ErrorMessage) ?
                "The input was not valid." :
            error.ErrorMessage;
        }
    }




相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...