I'm creating an API that will take parameters (type double) and return the result. ex. http://localhost:54897/api/Power/Nominal/6/-2
In the browser, I'll see: 4.0
Here's the code I have so far:
Model
public class PowerModel
{
[Required]
[Range(0,50)]
public double PowerFront { get; set; }
[Required]
[Range(-50,-1)]
public double PowerBack { get; set; }
[Required]
public double Result { get; set; }
}
Controller
[Produces("application/json")]
[Route("api/Power")]
public class PowerController : Controller
{
[HttpGet("Nominal/{powerFront}/{powerBack}")]
public double NominalPower(PowerModel powerModel)
{
if (ModelState.IsValid)
{
powerModel.Result = Power.NominalPower(powerModel.PowerFront, powerModel.PowerBack);
return powerModel.Result;
}
else
{
return 0;
}
}
}
With the above code, I can take in parameters, validate them and return a numeric result.
Something doesn't seem right though. I don't want to return 0 on an invalid model state, I want to return a message in the event of invalid input.
What would be the best way to take in parameters (preferably named), validate those parameters with attributes, perform a calculation, return a value on successful input or show error message on incorrect input?
This is something that HTTP Status Codes are used for. In your successful case, 200
is returned to indicate that the request was successful. For an invalid ModelState
, it's common to return 400
(which indicates a bad request was made).
To achieve this in ASP.NET Core, you can take advantage of ActionResult<T>
. Here's a complete example of how this would affect NominalPower
:
[HttpGet("Nominal/{powerFront}/{powerBack}")]
public ActionResult<double> NominalPower(PowerModel powerModel)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
powerModel.Result = Power.NominalPower(powerModel.PowerFront, powerModel.PowerBack);
return powerModel.Result;
}
In the example above, we pass ModelState
into the BadRequest
method, which will be serialised as JSON to show a list of errors that occurred when validing the model. If you'd prefer not to include this, you can just omit the ModelState
argument when calling BadRequest
.
Alternatively, you could simply decorate your PowerController
class with the ApiController
attribute, which will cause any requests that result in an invalid ModelState
to automatically return a 400
with JSON-serialised errors. Here's an example of that approach:
[Produces("application/json")]
[Route("api/Power")]
[ApiController]
public class PowerController : Controller
{
[HttpGet("Nominal/{powerFront}/{powerBack}")]
public double NominalPower(PowerModel powerModel)
{
powerModel.Result = Power.NominalPower(powerModel.PowerFront, powerModel.PowerBack);
return powerModel.Result;
}
}
In this version, there's no need to check ModelState
as that's already been checked thanks to the presence of the ApiController
attribute. You can even customise the automatic response that gets returned, if needed, as I've detailed in another answer, here.