Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebApi - Passing an Array of Values

I need to build an API using ASP.NET Web API (version 4.5.2). To get started, I'm just trying to create a basic endpoint that adds some numbers. In an attempt to do this, I've created:

[RoutePrefix("api/test")]
public class MyController : ApiController
{
  [HttpGet]
  public IEnumerable<int> Calulate(decimal[] op1, decimal[] op2)
  {
    var results = new List<Calculation>();
    for (var i=0; i<op1.Length; i++) 
    {
      var calculation = new Calculation();
      calculation.Operand1 = op1[i];
      calculation.Operand2 = op2[i];
      calculation.Calculate();

      results.Add(calculation);
    }
    return results;
  }

  public class Calculation
  {
    public int Operand1 { get; set; }
    public int Operand2 { get; set; }
    public int Result { get; set; }

    public void Calculate()
    {
      this.Result = this.Operand1 + this.Operand2;
    }
  }
}

I am now trying to hit this endpoint via the Postman Chrome app. When I run it via Postman, I'm getting an error. Here is what I'm doing:

In Postman, I've put "http://localhost:50668/api/test/calculate" in the URL field next to the "GET" drop down. I then click "Send". I'm receiving the following error:

{
  "Message": "An error has occurred.",
  "ExceptionMessage": "Can't bind multiple parameters ('op1' and 'op2') to the request's content.",
  "ExceptionType": "System.InvalidOperationException",
  "StackTrace": "..."
} 

I think (I don't know) the cause is because I'm not passing the values to the API from Postman correctly. However, I'm not sure how to do that. How do I pass an array of values to an API?

like image 273
user70192 Avatar asked Dec 16 '15 14:12

user70192


2 Answers

Short answer

To send arrays of decimals, WebApi expects url signature like: GET http://localhost:50668/api/test/calculate?Operand1=1.0&Operand1=2.0&Operand2=3.0&Operand2=4.0

That url will send [1.0,2.0] as Operand1 and [3.0,4.0] as Operand2.

Long answer

By calling your api using GET http://localhost:50668/api/test/calculate, you actually send nothing to your server. (aside of headers content)

If you want to send data to your server, you have (at least) 2 options:

Option 2: Use GET method if operation is idempotent Like William Xifaras already pointed out, specify that your inputs will come from the URL so WebApi interprets properly. To do so, use [FromUri].

    [HttpGet]
    [Route("calculate")]
    public List<Calculation> CalculateWithGet([FromUri]decimal[] Operand1, [FromUri]decimal[] Operand2)
    {
        var results = new List<Calculation>();
        for (var i = 0; i < Operand1.Length; i++)
        {
            var calculation = new Calculation();
            calculation.Operand1 = Operand1[i];
            calculation.Operand2 = Operand2[i];
            calculation.Calculate();

            results.Add(calculation);
        }
        return results;
    }

    public class Calculation
    {
        public decimal Operand1 { get; set; }
        public decimal Operand2 { get; set; }
        public decimal Result { get; set; }

        public void Calculate()
        {
            Result = this.Operand1 + this.Operand2;
        }
    }

With a REST client, it should look like: Query using GET method

With GET, data is sent via the URL

Note that if you use GET Method, the server will expect to receive inputs from the URL. You should therefore send queries like: GET http://localhost:50668/api/test/calculate?op1=1.0&op1=2.0&op2=3.0&op2=4.0

Use POST method if operation is not idempotent

Since the operation does some server side calculation, I pretend it may not always be idempotent. If it is the case, POST might be more appropriate.

    [HttpPost]
    [Route("calculate")]
    public List<Calculation> CalculateWithPost(CalculationInputs inputs)
    {
        var results = new List<Calculation>();
        for (var i = 0; i < inputs.Operand2.Length; i++)
        {
            var calculation = new Calculation();
            calculation.Operand1 = inputs.Operand1[i];
            calculation.Operand2 = inputs.Operand2[i];
            calculation.Calculate();

            results.Add(calculation);
        }
        return results;
    }

    public class CalculationInputs
    {
        public decimal[] Operand1 { get; set; }
        public decimal[] Operand2 { get; set; }
    }

    public class Calculation
    {
        public decimal Operand1 { get; set; }
        public decimal Operand2 { get; set; }
        public decimal Result { get; set; }

        public void Calculate()
        {
            Result = this.Operand1 + this.Operand2;
        }
    }

With POST, data is sent via the body

With that structure, the server expects to receive inputs from the request body. WebApi will deserialize the body if it matches the signature of your function.

With a REST client, it should look like: Query using POST method

Sidenote

The nuget package used to get the SwaggerUI generated (printscreens) can be find here. Very useful to run adhoc tests on WebApis.

like image 182
plog17 Avatar answered Oct 16 '22 10:10

plog17


Add from [FromUri] before the parameter.

public IEnumerable<int> Calulate([FromUri] decimal[] op1, [FromUri] decimal[] op2)

To force Web API to read a complex type from the URI, add the [FromUri] attribute to the parameter

http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api

like image 29
William Xifaras Avatar answered Oct 16 '22 12:10

William Xifaras