1

I have a scenario where it ignores the value passed on my query parameter if the value is a string.

Sample Route

v1/data?amount=foo

Here's the sample code

[HttpGet]
public async Task<IActionResult> GetData([FromQuery]decimal? amount)
{
}

So what I have tried so far is, I add a JsonConverter

public class DecimalConverter : JsonConverter
{
    public DecimalConverter()
    {
    }

    public override bool CanRead
    {
        get
        {
            return false;
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
    }

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(decimal) || objectType == typeof(decimal?) || objectType == typeof(float) || objectType == typeof(float?) || objectType == typeof(double) || objectType == typeof(double?));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        value = CleanupDecimal(value);

        if (DecimalConverter.IsWholeValue(value))
        {
            writer.WriteRawValue(JsonConvert.ToString(Convert.ToInt64(value)));
        }
        else
        {
            writer.WriteRawValue(JsonConvert.ToString(value));
        }
    }

    private static object CleanupDecimal(object value)
    {
        if (value is null) return value;

        if (value is decimal || value is decimal?)
        {
            value = decimal.Parse($"{value:G0}");
        }
        else if (value is float || value is float?)
        {
            value = float.Parse($"{value:G0}");
        }
        else if (value is double || value is double?)
        {
            value = double.Parse($"{value:G0}");
        }

        return value;
    }

    private static bool IsWholeValue(object value)
    {
        if (value is decimal decimalValue)
        {
            int precision = (decimal.GetBits(decimalValue)[3] >> 16) & 0x000000FF;
            return precision == 0;
        }
        else if (value is float floatValue)
        {
            return floatValue == Math.Truncate(floatValue);
        }
        else if (value is double doubleValue)
        {
            return doubleValue == Math.Truncate(doubleValue);
        }

        return false;
    }
}

So based on my observation, this only works on [FromBody] parameters.

Is there a way to validate query parameters without changing the datatype of it? I know it is possible to just change the datatype from decimal to string and validate it if it is a valid number.

Updated:

I want to have a response like

{
    "message": "The given data was invalid.",
    "errors": {
       "amount": [
          "The amount must be a number."
        ]
    }
}
2
  • It's not clear to me from reading your question what the expected behavior you want is. Are you trying to force a Bad Request response when they pass a string in the query string for 'amount'? Commented Aug 11, 2022 at 13:44
  • Do you mean you want to modify the automap to allow asp.net core map the decimal with string? By default the asp.net core auto map will auto check the string is number or not. Commented Aug 16, 2022 at 3:06

1 Answer 1

1

You can use custom model binding:

  [HttpGet]
    public async Task<IActionResult> GetData([ModelBinder(typeof(CustomBinder))]decimal? amount)
    {
    }

CustomBinder:

public class CustomBinder:IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }
            //get amount data with the following code
            
            var data=bindingContext.ValueProvider.GetValue("amount").FirstValue;
            
            //put your logic code of DecimalConverter  here
        
            bindingContext.Result = ModelBindingResult.Success(data);
            return Task.CompletedTask;
        }
    }
Sign up to request clarification or add additional context in comments.

1 Comment

sorry for the late response, thanks for the help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.