Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make ASP.NET to stop interpret null as string

I have a Web API method:

public List<Task> GetTasks([FromUri] TaskFilter filter)
{

}

The method has parameter with list of nullable identifiers:

public class TaskFilter
{
  public IList<int?> Assignees { get; set; }
}

When I call it:

GET /tasks?assignees=null

Server returns an error:

{
  "message":"The request is invalid.",
  "modelState": {
    "assignees": [ "The value 'null' is not valid for Nullable`1." ]
  }
}

It works only if I pass empty string:

GET /tasks?assignees=

But standard query string converters (from JQuery, Angular, etc) do not work with nulls in such way.

How to make ASP.NET to interpret 'null' as null?

Upd: The query string can contain several identifiers, e.g.:

GET /tasks?assignees=1&assignees=2&assignees=null

Upd2: JQuery converts nulls in array to empty strings, and ASP.NET interprets them as null. So the question is about calling WebAPI from Angular 1.6 ($HttpParamSerializerProvider)

Upd3: I know about workarounds, but I do not ask for them. I want a solution for specific problem:

  • It is a GET method
  • Method accepts a list from Uri
  • A list can contain null values
  • It should be List<int?> because API docs are generated automatically, and I do not want to see text array as parameter type
  • By default ASP.NET expects empty strings for null values (JQuery.param works in that way)
  • But some client libraries (e.g. Angular) does not convert null array items to empty strings
like image 308
Artem Avatar asked May 04 '17 13:05

Artem


People also ask

How do you change a null to a string?

There are two ways to replace NULL with blank values in SQL Server, function ISNULL(), and COALESCE(). Both functions replace the value you provide when the argument is NULL like ISNULL(column, '') will return empty String if the column value is NULL.

How do you handle a string NULL value in C#?

IsNullOrEmpty() Method of C# This method is used when you want to check whether the given string is Empty or have a null value or not? If any string is not assigned any value, then it will have Null value. The symbol of assigning Null value is “ “or String. Empty(A constant for empty strings).

Can strings be null in C#?

C# | IsNullOrEmpty() MethodA string will be null if it has not been assigned a value. A string will be empty if it is assigned “” or String.

Can you string a null?

A null string does not refer to an instance of a System. String object and any attempt to call a method on a null string results in a NullReferenceException. e.g. given below. When run the above code it will throw NullReferenceException.


2 Answers

Finally, I found a solution using custom value provider:

using System;
using System.Collections.Generic;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.ValueProviders;
using System.Web.Http.ValueProviders.Providers;
using System.Globalization;
using System.Net.Http;
using System.Web.Http.ModelBinding;

public sealed class NullableValueProviderAttribute : ModelBinderAttribute
{
    private readonly string[] _nullableColumns;

    public NullableValueProviderAttribute(params string[] nullableColumns)
    {
        _nullableColumns = nullableColumns;
    }

    public override IEnumerable<ValueProviderFactory> GetValueProviderFactories(HttpConfiguration configuration)
    {
        return new ValueProviderFactory[] { new NullableValueProviderFactory(_nullableColumns) };
    }
}

public class NullableValueProviderFactory : ValueProviderFactory, IUriValueProviderFactory
{
    private readonly string[] _nullableColumns;

    public NullableValueProviderFactory(string[] nullableColumns)
    {
        _nullableColumns = nullableColumns;
    }

    public override IValueProvider GetValueProvider(HttpActionContext actionContext)
    {
        return new NullableQueryStringValueProvider(actionContext, CultureInfo.InvariantCulture, _nullableColumns);
    }
}

public class NullableQueryStringValueProvider : NameValuePairsValueProvider
{
    private static readonly string[] _nullValues = new string[] { "null", "undefined" };

    private static IEnumerable<KeyValuePair<string, string>> GetQueryNameValuePairs(HttpRequestMessage request, string[] nullableColumns)
    {
        foreach (var pair in request.GetQueryNameValuePairs())
        {
            var isNull = Array.IndexOf(nullableColumns, pair.Key) >= 0 && Array.IndexOf(_nullValues, pair.Value) >= 0;
            yield return isNull ? new KeyValuePair<string, string>(pair.Key, "") : pair;
        };
    }

    public NullableQueryStringValueProvider(HttpActionContext actionContext, CultureInfo culture, string[] nullableColumns) :
        base(GetQueryNameValuePairs(actionContext.ControllerContext.Request, nullableColumns), culture)
    { }
}

And specify it in Web API action:

public List<Task> GetTasks([NullableValueProvider("assignees")] TaskFilter filter)
{
}
like image 111
Artem Avatar answered Oct 03 '22 18:10

Artem


You can create a custom model bind for this specific type, inherithing from DefaultModelBinder, for sample:

using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
public class TaskFilterBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, System.Web.Mvc.ModelBindingContext bindingContext)
    {
        var request = controllerContext.HttpContext.Request;

        var assignees = request.QueryString["assignees"];

        if (assignees == "null") // check if assignees is null (string) then return NULL
            return null;
        return assignees;
    }

}

Finally we need to inform the controller as to the binding we want it to use. This we can specify using attributes

[ModelBinder(typeof(TaskFilterBinder))]

as below:

public List<Task> GetTasks([FromUri(ModelBinder=typeof(TaskFilterBinder))] TaskFilter filter)
{
// Do your stuff.
}

For more reference check this link on Custom Model Binders. Hope, this solves your problem . Thanks

like image 38
Prateek Gupta Avatar answered Oct 03 '22 18:10

Prateek Gupta