Autorest: The first item in a list-expansion cannot be null

Created on 22 Jun 2017  路  7Comments  路  Source: Azure/autorest

When generating a client, Autorest sets an empty string to the parameter..

if (entityTypes.Count == 0)
{
     _queryParameters.Add(string.Format("Blah={0}", System.Uri.EscapeDataString(string.Empty)));
}

This causes issues on model binding in controllers because now the list has a null item in it. It would be better not to set the parameter at all if the list is empty.

if (blah != null && blah.Count != 0)
{
    foreach (var _item in blah)
    {
        _queryParameters.Add(string.Format("Blahs={0}", System.Uri.EscapeDataString("" + _item)));
    }
 }

Most helpful comment

This issue should not be closed!

Why would we want to send a list with a single null element?

We had a nasty _side-effect_ introduced in our generated code given my colleague updated the client code to contact one of our services and he wondered if the parameter should be set as an empty list or anything else. We should not have to look at the generated code to ensure that the code is correct. It is our fault to not have tested thoroughly after the code was regenerated, but this behavior is very strange.

The suggestion by @zunama was great, in my opinion, but it might diverge from @olydis.

All 7 comments

could you please send us a Swagger file that causes the above issue so we can reliably reproduce - and fix 馃檪

closing due to inactivity - please reopen with a repro if this is still an issue

Hi @olydis,

This is still an issue. You can download a quick repro here. Just make sure the Port number in AutorestRepro.Client matches the port with the AutorestRepro app is exposed.
https://www.dropbox.com/sh/vw390yuph4lp5i1/AAB9YLSDrtSVCp6s0fQTVjQFa?dl=0

Open the two projects in two instances of visual studio so you can run them at the same time. Place a breakpoint in the Post method of the controller in AutorestRepro.

When the client posts an empty list, the list appears in the controller as having one element; null :/

From what I gather, it's autorest converts an empty list to this: http://localhost:80/api/values?list=

Since the Asp.Net controller is expecting a List, it binds that as a list with one null element.

Thoughts?

@eanyanwu Thanks a lot for the nice repro and the details!

Hmmm, I see, but then I wonder how one would even represent empty list as a query parameter! http://localhost:80/api/values?list= looks to me like it's conforming with the Swagger spec - null would be represented by not having list sent at all, i.e. http://localhost:80/api/values. I'm not an Asp.Net expert, but seems to me as if that should be configurable on "their" side. 馃檪 But I'm open to suggestions: How would you represent empty list, or rather what does Asp.Net need to see to deserialize as empty list rather than null?

Hmm good question. I did a little bit more digging and found that asp.net has some interesting behavior here:

  • If the controller accepts an actual list e.g. (List myList), model binding will create an empty list if the string "myList" is not sent at all. So POSTing http://localhost:80/api/values to a controller that accepts a list, will actually initialize the list parameter and have it empty. This is a bit odd, but it is
    documented to some degree (Search for "Reference Types"). This is the case in the original repro I linked. Including the "myList" string like so: http://localhost:80/api/values?myList= will create a list with one null element.
  • Now for the weird part. If the controller accepts an object of some type which has a list property, omitting the list parameter from the query like above doesn't give the same behavior. To get an empty list, you need to post http://localhost:80/api/values?testlist[]. Oddly enough, you will get a different result if you post http://localhost:80/api/values?testlist (no brackets)

To repro my second bullet point, simply modify the original AutoRest project by adding a class similar to this:

public class Wrapper
{
    public List<string> TestList{ get; set; }
}

My original problem was the second bullet point, but this seems to be a bit more convoluted than I thought at first, so I think I'll switch to passing the object in the body of the request instead :0 since I can just send this: { TestList : [] } in the request body.

So I am all set 馃憤

@eanyanwu Wow thanks for the investigation, that's super valuable information 馃檪

This issue should not be closed!

Why would we want to send a list with a single null element?

We had a nasty _side-effect_ introduced in our generated code given my colleague updated the client code to contact one of our services and he wondered if the parameter should be set as an empty list or anything else. We should not have to look at the generated code to ensure that the code is correct. It is our fault to not have tested thoroughly after the code was regenerated, but this behavior is very strange.

The suggestion by @zunama was great, in my opinion, but it might diverge from @olydis.

Was this page helpful?
0 / 5 - 0 ratings