1
votes

I'm using .NET Core and WebApi and I am trying to figure out what the url would look like to send an array of objects through.

For example

public class DataObject
{
  public int id { get; set;}
  public string name { get; set }
}

[HttpGet()]
public <ActionResult<string>> GetSomething(DataObject[] data))
{
  //do something and return a string
}

what would the url look like to do this? Should I use FromQuery or FromRoute on data? On the HttpGet(), what should be in the parenthesis? "{data}" or something else?

Everything I can find so far has been on integer arrays or string arrays, but not complex arrays to a get call.

Update

Still not able to get this to work even though I'm sure the reply I have gotten should work. Here is some more code.

[Route("api/[controller]/[action]")]

 [HttpGet()]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType(typeof(GridResult), (int)HttpStatusCode.OK)]
    public async Task<ActionResult<GridResult>> GetGridData<TFilter1, TFilter2, TItem1>
        ([FromQuery]string sessionID, [FromQuery] GridDetails details, [FromQuery] TFilter1[] TFilters1, [FromQuery] TFilter2[] TFilters2, [FromQuery] TItem1[] TSorts)

and finally the url that I have generated that throws a 404.

https://localhost:44366/api/grid/GetGridData/sessionID=598357390&details?NUMBER_OF_ROWS_FIRST_RETURNED=100&CURSOR_POSITION=0&RESULT_IN_SAXORDER=false&TERSERESPONSE=true&IsStaticList=true&GRID_TYPE=list&REQUEST_TYPE=LIST.DATA_ONLY.STORED&GRID_NAME=WUWP09&TFilters1[0].AliasName=PRO_CODE&TFilters1[0].Operator=%3D&TFilters1[0].SEQNUM=1&TFilters1[1].AliasName=APR_CLASS&TFilters1[1].Operator=%3D&Tsorts[1].SEQNUM=2&Tsorts[0].ALIAS_NAME=pvd_value&Tsorts[0].TYPE=ASC

Update 2

https://localhost:44366/api/grid/GetGridData?sessionID=598357390&details.NUMBER_OF_ROWS_FIRST_RETURNED=100&details.CURSOR_POSITION=0&details.RESULT_IN_SAXORDER=false&details.TERSERESPONSE=true&details.IsStaticList=true&details.GRID_TYPE=list&details.REQUEST_TYPE=LIST.DATA_ONLY.STORED&details.GRID_NAME=WUWP09&details.TAB_NAME&details.LOCALIZE_RESULT&details.USER_FUNCTION_NAME&details.TOTALRECORDS&details.RES_IsMoreRecords&details.RES_CURRENT_CURSOR_POSITION&TFilters1[0].AliasName=PRO_CODE&TFilters1[0].Operator=%3D&TFilters1[0].SEQNUM=1&TFilters1[1].AliasName=APR_CLASS&TFilters1[1].Operator=%3D&Tsorts[1].SEQNUM=2&Tsorts[0].ALIAS_NAME=pvd_value&Tsorts[0].TYPE=ASC

Update 3

Startup.cs public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        var _accessor = services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        var config = new GridServices.Models.config();
        Configuration.Bind("Connections", config);
        services.AddSingleton(config);
        services.AddSingleton(new Controllers.GridController(config));
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }
        app.UseHttpsRedirection();
        app.UseMvc();
    }
}

GridController

namespace EAMWebApi.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class GridController : ControllerBase
{
    config Config { get; }
    //private readonly LinkGenerator _linkGenerator;

    public GridController(config config)
    {
        config = Config;
        //_linkGenerator = linkGenerator;
    }

    [HttpGet()]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType(typeof(GridResult), (int)HttpStatusCode.OK)]
    public async Task<ActionResult<GridResult>> GetGridData<TFilter1, TFilter2, TItem1>
        ([FromQuery]string sessionID, [FromQuery] GridDetails details, [FromQuery] TFilter1[] TFilters1 = null, [FromQuery] TFilter2[] TFilters2 = null, [FromQuery] TItem1[] TSorts = null)
    {//Do something}
}

GridDetails

namespace GridServices.Models
{
    public class GridDetails
    {
        public string GRID_NAME { get; set; }
        public string NUMBER_OF_ROWS_FIRST_RETURNED { get; set; }        
        public string CURSOR_POSITION { get; set; }        
        public string TAB_NAME { get; set; }        
        public string RESULT_IN_SAXORDER { get; set; }        
        public string TERSERESPONSE { get; set; }        
        public string LOCALIZE_RESULT { get; set; }        
        public string USER_FUNCTION_NAME { get; set; }        
        public string TOTALRECORDS { get; set; }        
        public bool RES_IsMoreRecords { get; set; }        
        public bool IsStaticList { get; set; }        
        public string GRID_TYPE { get; set; }        
        public string REQUEST_TYPE { get; set; }        
        public string RES_CURRENT_CURSOR_POSITION { get; set; }
    }
}

MultiAddOnFilter

public class MultiAddOnFilter
{        
    public string ALIAS_NAME { get; set; }
    public string OPERATOR { get; set; }
    public string OPERATORSpecified { get; set; }
    public string VALUE { get; set; }
    public string LPAREN { get; set; }
    public string RPAREN { get; set; }        
    public string JOINER { get; set; }        
    public string JOINERSpecified { get; set; }        
    public string SEQNUM { get; set; }

    public MultiAddOnFilter(string _ALIAS_NAME, string _OPERATOR, string _VALUE)
    {
        ALIAS_NAME = _ALIAS_NAME;
        OPERATOR = _OPERATOR;
        OPERATORSpecified = "true";
        VALUE = _VALUE;
    }
}

Sorts namespace GridServices.Models { public class Sort { public string ALIAS_NAME { get; set; } public string TYPE { get; set; } public string TYPESpecified { get; set; }

    public Sort(string _ALIAS_NAME, string _TYPE)
    {
        ALIAS_NAME = _ALIAS_NAME;
        TYPE = _TYPE;
        TYPESpecified = "true";
    }
}

}

1

1 Answers

1
votes

what would the url look like to do this?

It should be something like the following:

GET /Somecontroller/GetSomething?data[0].id=1&data[0].name=nameA&data[1].id=2&data[1].name=nameB&data[2].id=3&data[2].name=nameC

This payload is almost the same as you post in application/x-www-form-urlencoded format, except you'll send it as a querystring.


[Edit]

If one of those items were to be empty, would I have to pass %00 to it to indicate a null value?

  1. Let's say you have such an object array:
data = [
  {
    "id": 1,
    "name": "nameA"
  },
  {
    "id": 2,
    "name": null            
  },
  {
    "id": 3,
    "name": "nameC"
  }
]

Note the data[1].name==null. you don't have to specify data[1].name :

?data[0].id=1&data[0].name=nameA&data[1].id=2&data[2].id=3&data[2].name=nameC
  1. If the whole data[1] item is null, just adjust the index of data[2] to data[1]:
data[0].id=1&data[0].name=nameA&data[1].id=3&data[1].name=name

Or you could add an empty field for this item:

?data[0].id=1&data[0].name=nameA&data[1].id=&data[2].id=3&data[2].name=nameC

What if the whole DataObject was null? /GetSomething?data=%00 ?

you don't have to specify /GetSomething?data=%00, just send a request to /GetSomething?, and then you'll get an empty array.


[Edit2]

There're two reasons that always routes you to a 404 result:

  1. You're registering your GridController as a singleton. MVC will register controllers automatically (as a scoped service). Just remove that line :
services.AddSingleton(new Controllers.GridController(config));
  1. Your controller action of GetGridData<TFilter1, TFilter2, TItem1> is a generic method. It won't work by default. There's already a thread on SO talking about this. I would also suggest you use a specific GridFilter type for each method. If you find yourself repeating the same logic, you could put your generic method into a parent MySupperGridBaseController<TFilter1, TFilter2, TItem1> class, something as below:
public class MySupperGridBaseController<TFilter1, TFilter2, TItem1> : ControllerBase
{
    public async Task<ActionResult<GridResult>> GetGridData
            ([FromQuery]string sessionID, [FromQuery] GridDetails details, [FromQuery] TFilter1[] TFilters1 = null, [FromQuery] TFilter2[] TFilters2 = null, [FromQuery] TItem1[] TSorts = null)
    {
         ...
    }
}


// now we could reuse the same logic inherited from parent 
public class GridController : MySupperGridBaseController<MultiAddOnFilter, MultiAddOnFilter, Sort>
{

}