ASP.NET MVC required parameters in controller method - c#

I was wondering if it's possible to have something like:
[HttpGet]
public JsonResult AddTextFile(string path)
{
if(string.IsNullOrEmpty(path))
{
// return error
}
}
But in the case where I might have a lot of parameters in my controller method I don't want to use string.IsNullOrEmpty() for each one. I know that I could use view-models with a [Required] field indicator and that will allow me to use ModelState, but because these are all API endpoints, I'm requiring information through get parameters.
Is there an elegant way of requiring controller method parameters, so that if any of them are not set it would return a generic response message?

Use a complex object as the parameter:
[HttpGet]
public JsonResult AddTextFile(MyObject obj) {
if(!ModelState.IsValid) {
// return error
}
}
public class MyObject {
[Required]
public string Path { get; set; }
}
The properties of MyObject will be taken from the query parameters, like: /addtextfile?path=blah
And the model validation will apply.

Related

How do I handle path/route parameters in Swashbuckle?

I have a controller where I get the ID from the route.
[HttpGet]
[Route("{vehicleId}")]
public InfoDto GetInfo([FromUri] VehicleDetailsRequest request)
{
return ...;
}
The VehicleDetailsRequest object looks like this (the Validator is from FluentValidation):
[Validator(typeof(VehicleDetailsRequestValidator))]
public class VehicleDetailsRequest
{
public int VehicleId { get; set; }
public string Lang { get; set; }
}
I can query this action as I expect with http://localhost/controller/123?lang=sv but my swagger documentation looks like this:
How can I get Swashbuckle/Swagger to only show me one vehicleId but still keep the FluentValidation?
I'm using Swashbuckle 5.6 and .Net Framework 4.6.2.
In my opinion, there is something wrong with action method signature.
The parameter from the URL should be present in the action method parameters.
Also, if you want only lang in query string, then it should also be present as a parameter in the action.
Please note that if you specify object in the action parameters, all its properties would be forming the query string.
So, your action method should like below:
[HttpGet]
[Route("{vehicleId}")]
public InfoDto GetInfo(int vehicleId, [FromUri] string lang)
{
return ...;
}
This should help you to resolve the issue.

ASP.Net Core Web API Controller action with many parameters

I have ASP.Net Core Web API Controller's method that returns List<Car> based on query parameters.
[HttpPost("cars")]
public async Task<List<Car>> GetCars([FromBody] CarParams par)
{
//...
}
Parameters are grouped in record type CarParams. There are about 20 parameters:
public class CarParams
{
public string EngineType { get; set; }
public int WheelsCount { get; set; }
/// ... 20 params
public bool IsTruck { get; set; }
}
I need to change this method from POST to GET, because I want to use a server-side caching for such requests.
Should I create a controller's method with 20 params?
[HttpGet("cars")]
public async Task<List<Car>> GetCars(string engineType,
int wheelsCount,
/*...20 params?...*/
bool isTruck)
{
//...
}
If this is the case: Is there a way to automatically generate such a complex URL for a client-side app?
You can keep the model. Update the action's model binder so that it knows where to look for the model data.
Use [FromQuery] to specify the exact binding source you want to apply.
[HttpGet("cars")]
[Produces(typeof(List<Car>))]
public async Task<IActionResult> GetCars([FromQuery] CarParams parameters) {
//...
return Ok(data);
}
Reference Model Binding in ASP.NET Core
Just change [FromBody] attribute with [FromUrl]

NETCORE MVC - How to work with nested, multi-parameterized routes

Looking for best practices when working with nested routes in .NET Core MVC.
Let's say CampusController.cs works with a base model:
[Route("api/campus/")]
public class CampusController : Controller
{
...
[HttpGet]
[Route("{campusId}")]
public IActionResult GetCampusInfo ([FromQuery]int campusId) { ... }
}
And BuildingController.cs works with a child model:
[Route("api/campus/{campusId}/building")]
public class BuildingController : Controller
{
...
[HttpGet]
[Route("{buildingId}")]
public IActionResult GetBuilding ([FromQuery]int buildingId) { ... }
[Route("{buildingId}/")]
public IActionResult GetBuilding ([FromQuery]int buildingId) { ... }
....
(more Action Methods)
}
If buildingId maps directly to the database it could retrieved even if the provided campusId isn't the parent. To keep the URL clean when calling /api/campus/{campusId}/building/{buildingId} I'd like to validate {campusId} and return a 4xx coded IActionResult if it's invalid. There has to be a better way than including validation logic in every Action Method inside BuildingController.
Is there a way to cascade multiple Action methods on different controllers? So that a validation method on CampusController would be called first and in turn call a method onBuildingController?
Is there a way to have a controller-level verification of campusId that could short circuit and return a ActionResult if validation fails?
EDIT: When I refer to validation logic I mean API signals; not the business-logic that actually determines if campusId is/isn't valid.
Thanks in advance!
If using placeholder in the route prefix you would also need to include it in the action itself
[Route("api/campus/{campusId:int}/building")]
public class BuildingController : Controller {
//...
[HttpGet]
[Route("{buildingId:int}")] // Matches GET api/campus/123/building/456
public IActionResult GetBuilding ([FromRoute]int campusId, [FromRoute]int buildingId) {
//... validate campus id along with building id
}
}
If concerned about repeated code for validation then create a base controller for campus related request and have a shared validation method.
Another option is to have a service/repository that can be used to verify campus id and its relation to the provided building id if needed.
It sounds like you want your users to provide a campusId when talking to the BuildingController, and your BuildingController to validate campusId in a DRY kind of way.
If that's the case, you can create an input model for your BuildingController methods:
public class BuildingIdInput
{
[Required]
public int? CampusId { get; set; }
[Required]
public int? BuildingId { get; set; }
}
Then you can let MVC bind user input to this model.
[Route("api/campus")]
public class BuildingController : Controller
{
[HttpGet]
[Route("{campusId}/building/{buildingId}")]
public IActionResult GetBuilding (BuildingIdInput input)
{
if (ModelState.IsValid)
{...}
}
}

ASP.NET MVC request intercept and get model value

I have lots of controllers, which return data, based on parameters.
But users can fake the parameters they send to server, so I would like to intercept all requests, and if some parameter that come back with model is not valid, give warning/error.
the example header of controller is this
[Intercept<CarModel>] <-- I want something like this, <CarModel> because the interceptor knows what type to cast the model when intercepted
public object Cards(CarModel model) {
I would like to create this kind of attribute, that intercepts the controller, checks if model is valid.
How could I do this? I googled about it, but not found anything like this.
Just put your validation code in the action. Better yet, inherit from an IValidatable interface and implement HasPropertiesValid() on all your models. Then you just call model.HasPropertiesValid(); at the beginning of your action.
Validatable interface
interface IValidatable {
bool IsPropertiesValid();
}
A model example
public class CarModel : IValidatable {
public string ModelName {get;set;}
public string ManufacturerName {get;set;}
public bool IsPropertiesValid() {
if(ManufacturerName == "Toyota") {
if(ModelName == "Prius") return true;
}
return false;
}
}
Controller
public ActionResult ToyotaCar(CarModel model){
if(!model.IsPropertiesValid()) return RedirectToAction("QuitMessingAround","CaughtYou");
}

ActionResult cast parameter base class to derived class

I've written a base class and some classes which derive from it.
I want to use these classes in one ActionResult, but if I'm trying to cast PSBase to PS1 I'm getting a System.InvalidCastException that type PSBase can not be converted to PS1.
Classes:
public class PSBase
{
public int stationId { get; set; }
public string name { get; set; }
}
public class PS1 : PSBase
{
public string reference { get; set; }
}
public class PS2 : PSBase
{
}
ActionResult:
[HttpPost]
public ActionResult ProductionStep(PSBase ps)
{
if (ModelState.IsValid)
{
var product = db.Product.FirstOrDefault(.........);
switch (ps.stationId )
{
case 1:
{
product.Reference = ((PS1)ps).reference;
db.SaveChanges();
break;
}
}
}
return View();
}
As I don't want to have for each class a own ActionResult (repeating much of the same code many times) I wanted put all this to one ActionResult. Any Ideas how I could implement this?
What you are trying to do will never work without custom ModelBinder (and even then it will be a huge mess I'd not recommend to implement), sorry.
Only when you are passing a model from Controller to View it remembers what type it was originally (including inheritance, etc.) because at that point you are still on the server side of the page and you are merely passing an object.
Once you enter a view and submit a form all that does it creates some POST request with body containing list of values based on input names.
In your case if you have a form based on PS1 and used all the fields as inputs, you would get something like:
POST:
stationId = some value
name = some value
reference = some value
(there is no mention of the original type, controller, method, etc.)
Now, what MVC does is that it checks what argument you are using in the header of the method (in your case ProductionStep(PSBase ps)).
Based on the argument it calls a model binder. What the default model binder does is that it creates new instance of that class (in your case PSBase) and goes via reflection through all the properties of that class and tries to get them from the POST body.
If there are some extra values in the POST body those are forgotten.
Unless you write a custom model binder for this default MVC implementation can't help you there.
I'd recommend creating two separate methods, one of each accepting different implementation of PSBase.
If you want to read more on Model Binders check this out http://msdn.microsoft.com/en-us/magazine/hh781022.aspx
EDIT:
By creating two separate methods I mean something like this:
[HttpPost]
public ActionResult ProductionStepA(PS1 ps)
{
if (ModelState.IsValid)
{
}
return View();
}
[HttpPost]
public ActionResult ProductionStepB(PS2 ps)
{
if (ModelState.IsValid)
{
}
return View();
}
then you have to distinguish between them in the view via different form action.

Categories