Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why WebApi marks empty string as error in model state?

I have strange behaviour of Web API, .Net 4.5.2. If optional string parameter is null, ModelState has no error. If it is not null and not empty, no errors again. But if it is just an empty string I have model state error.

Why do I get it and how to disable it?

Assuming app served on localhost:82 I have those results:

Url: http://localhost:82/
Response: "null"

Url: http://localhost:82/?q=1
Response: "1"

Url: http://localhost:82/?q=
Response: {
  "Message": "The request is invalid.",
  "ModelState": {
    "q.String": [
      "A value is required but was not present in the request."
    ]
  }
}

Test controller and config is below. This is reduced to bare minimum default "Asp.net web application" with "WebApi" in VS2013.

namespace Web.Api.Test.Controllers
{
    using System.Web.Http;

    [Route]
    public class HomeController : ApiController
    {
        [Route]
        [HttpGet]
        public IHttpActionResult Search(string q = default(string))
        {
            return this.ModelState.IsValid
                ? this.Ok(q ?? "null")
                : (IHttpActionResult)this.BadRequest(this.ModelState);
        }
    }
}

Startup.cs is:

using Microsoft.Owin;

using WebApplication1;

[assembly: OwinStartup(typeof(Startup))]

namespace WebApplication1
{
    using System.Web.Http;

    using Newtonsoft.Json;

    using Owin;

    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            GlobalConfiguration.Configure(config =>
            {
                config.MapHttpAttributeRoutes();
                config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented;

                config.Formatters.Remove(config.Formatters.XmlFormatter);
            });
        }
    }
}

PS: This question has a workaround, but it does not answer the main question: why does this situation happen and what reasons are behind this design decision.

like image 592
Aen Sidhe Avatar asked Aug 03 '15 15:08

Aen Sidhe


People also ask

How do I check if a web API model is valid?

Inherited IvalidatableObject interface for Model class. Now, implement Validate method to write your custom rules to validate the model. Controller uses ModelState. IsValid to validate the model.

Why model state is false in MVC?

That's because an error exists; ModelState. IsValid is false if any of the properties submitted have any error messages attached to them. What all of this means is that by setting up the validation in this manner, we allow MVC to just work the way it was designed.

What is the point of an empty string?

Sometimes strings can be empty or NULL. The difference is that NULL is used to refer to nothing. However, an empty string is used to point to a unique string with zero length.


2 Answers

I have had the same issue, came up with the following eventually:

public class SimpleTypeParameterBindingFactory
{
    private readonly TypeConverterModelBinder converterModelBinder = new TypeConverterModelBinder();
    private readonly IEnumerable<ValueProviderFactory> factories;

    public SimpleTypeParameterBindingFactory(HttpConfiguration configuration)
    {
        factories = configuration.Services.GetValueProviderFactories();
    }

    public HttpParameterBinding BindOrNull(HttpParameterDescriptor descriptor)
    {
        return IsSimpleType(descriptor.ParameterType)
            ? new ModelBinderParameterBinding(descriptor, converterModelBinder, factories)
            : null;
    }

    private static bool IsSimpleType(Type type)
    {
        return TypeDescriptor.GetConverter(type).CanConvertFrom(typeof (string));
    }
}

public class Startup
{
    public void Configure(IAppBuilder appBuilder)
    {
        var configuration = new HttpConfiguration();
        configuration.ParameterBindingRules.Insert(0, new SimpleTypeParameterBindingFactory(configuration).BindOrNull);
        configuration.EnsureInitialized();
    }
}

The problem is rooted in some magic code in ModelValidationNode, which creates model errors for null models even if corresponding parameter has default value. The code above just replaces CompositeModelBinder (which calls ModelValidationNode) with TypeConverterModelBinder for simple type parameters.

like image 58
Gusev Petr Avatar answered Oct 19 '22 04:10

Gusev Petr


Why do I get it and how to disable it?

Don't know why you get it. This maybe how you disable it, but after reading I don't think you want to really as there are simpler solutions, e.g:

Use of a model class solves this in a cleaner way.

public class SearchModel
{
    public string Q { get; set; }
}

public IHttpActionResult Search([FromUri] SearchModel model)
{
    return ModelState.IsValid
        ? Ok(model.Q ?? "null")
        : (IHttpActionResult) BadRequest(ModelState);
}
like image 43
weston Avatar answered Oct 19 '22 04:10

weston