I have API like this , what i want his
When i call with out any parameters
http//localhostcustomer/employes/ it should return
StatusCode(StatusCodes.Status404NotFound)
And if pass with invalid parameters http//localhostcustomer/employes/customerid=abcsd&employedid=yyyy it should return
StatusCode(StatusCodes.Status400BadRequest);
how to do model validation for this
[Route("customer/employes")]
Public class testcontroller : controller
public List<Emplyees> get( [Required]int customerid, [Required]int employedid)
{
if (ModelState.IsValid)
return OK
else
StatusCode(StatusCodes.Status400BadRequest);
}
since you dont actually pas a model u dont realy get to place the validation on something since u just pass loose parameters.
u could create a method or class that validates your input parameters like
IsValid(CustomerId,EmployedId) return Ok()
Or modify the current ModelState.IsValid so
ModelState.IsValid(CustomerId,EmployedId) return Ok()
Since it seems that your are trying to create a Get request with Required parameters it might be interesting to format this as a proper REST call so your path looks like
/customer/[CustomerId]/employes/[employedId]
Without parameters - Not Found
If you haven't registered (either explicitly or implicitly via attributes) the route /employees then ASP.NET will not find a handler for that request that's why by default it will return with 404.
With malformed parameters - Bad Request
That's a bit more tricky. Whenever you have a route with parameters then you can define their types as well. By default if the request contains the route parameter, which can't be converted into that specific type then ASP.NET will not call your handler, rather than it will try to find a better route for that.
Request: /employees/123info
[Route("customer/employees")]
public List<Employee> Get( [Required]int someParameter) {
If you want to catch malformed requests as well, then you have to use string parameters and try to apply the conversion logic manually:
[Route("customer/employees")]
public IActionResult Get(string someParameter) {
bool isItAValidParameter = int.TryParse(someParameter, out var validParameter):
if (!isItAValidParameter)
...
You are not passing a model as your parameter, hence validating a model makes no sense. You need to validate your loose parameters explicitly. In case of invalid parameters:
Request: /employes/customerid=abcsd&employedid=yyyy
By default, the api will return status 400 - Bad Request Error.
In case of without any parameters:
you will get HTTP ERROR 404 - Page can't be found
I would take a look at FluentValidation, which is a popular .NET library for building strongly-typed validation rules.
You can create a custom validator that checks your controller method parameters and that creates a response accordingly.
Something along the lines of:
var validator = new CustomerEmployeeValidator();
var validation = validator.Validate(someparameter);
if (!validation.IsValid)
return BadRequest(validation.Errors);
On how to create the validator, you should look into:
https://fluentvalidation.net/
As a side note, when the input parameter is incorrect, you should always return a BadRequest StatusCode instead of a NotFound StatusCode.
A NotFound StatusCode is typically used when you are searching for specific data externally, and is not found in that repository.
Related
How can I get the parameters of an ASP.NET Core MVC controller action from the properties of a JSON object that was sent in the HTTP request body?
If the request body contains form data and the content type is appropriate, the action method parameters are populated automatically as expected.
But nothing works if the POST data format is JSON and the content type is "application/json". I think this is quite common for API requests? I tried adding the [FromBody] attribute to all parameters, but the documentation says I can only apply that once. Well, I can write it multiple times and nobody complains, but neither helps. Even if I want to bind all the JSON to a single string parameter, it remains null.
Can ASP.NET Core actually handle JSON POST data? In the usual parameter binding comfort? Or are we down to the feature level of PHP (or below) when it comes to JSON requests? Should I not use such advanced technology and revert my client-side code to plain old HTTP form data instead?
I think I used this before and saw it working. But can't find out what the difference is here. Maybe it only works in controllers with the [ApiController] attribute? This case is a regular web page controller, not a separate API. But it needs to provide functions to JavaScript as well, so it does both.
I tried adding the [FromBody] attribute to all parameters
This sounds fishy to me. Are you trying to send your params as a json object and expecting them to get unwrapped into individual action params?
Consider the following data type and MVC controller endpoint:
public class Sample
{
public int Id { get; set; }
public string Name { get; set; }
}
[HttpPost]
public IActionResult Post([FromBody] Sample sample)
=> new JsonResult(sample);
This is all you need for a typical POST to an MVC controller. The key point is probably the type that I'm using to bind to the body. As you can see, I create a matching json object in Postman and it binds correctly and returns what I sent.
To get what you want, I think you'd have to rely on query params. (or some other technique I'm unaware of) Here's an example if you need it.
[HttpPost]
public IActionResult PostQuery([FromQuery] int id, [FromQuery] string name)
=> new JsonResult(new Sample {Id = id, Name = name});
In ASP.NET Core, if an action accepts directly an enum type, and that enum for example has defined 1 to something, if we pass a value different than 1, we'll get a validation error.
This is good! But it doesn't work for when the enum is inside a complex object type, when they are built from the body of a request ([FromBody] attribute).
Why is this happening? I know that anything coming from the body, is being handled by JSON Converters. Why can't they handle this for us, when the Query binder (what/where is it?) does it for us?
Example Enum:
public Enum Example
{
One = 1
}
Example Action:
public object ExampleAction(Enum hello)
{
return Ok();
}
If you hit the action with a HTTP request, passing in the hello parameter in a query string with a value different than 1, you will get a validation error.
Now, if you annotate the hello parameter with the [FromBody] attribute and make a new request (passing in this time the data via the body instead of the query string), that behavior is lost.
I wrote a new method into my Controller of my ASP.Net MVC project and getting error below. I think InvalidOperationException coming from with Swagger. I marked it as "ignored Api" hoping it will skip the method but error still there:
[ApiExplorerSettings(IgnoreApi = true)]
public decimal CalculatePriceWithCampaign(
BeverageCapacityCampaign campaign,
BeverageCapacity capacity,
int count = 1)
{
switch (campaign.DiscountType)
{
case DiscountType.Fixed:
return (capacity.CapacityPrice - campaign.DiscountValue) * count;
case DiscountType.Percentage:
return (capacity.CapacityPrice * count) * campaign.DiscountValue;
default:
return capacity.CapacityPrice;
}
}
But when running I am getting this error:
An unhandled exception occurred while processing the request.
InvalidOperationException: Action 'Gorilla.WebApi.Source.Controller.Campaigns.BeverageCapacityCampaignController.CalculatePriceWithCampaign (Gorilla.WebApi)' has more than one parameter that was specified or inferred as bound from request body. Only one parameter per action may be bound from body. Inspect the following parameters, and use 'FromQueryAttribute' to specify bound from query, 'FromRouteAttribute' to specify bound from route, and 'FromBodyAttribute' for parameters to be bound from body:
BeverageCapacityCampaign campaign
BeverageCapacity capacity
Information I could find suggested to check nugets, but all my Nugets are up-to-date.
The error is coming from model binding and is not related to Swagger (the presence of ApiExplorerSettings attribute has no impact on error).
You have two complex parameters. i.e. of Complex types
BeverageCapacityCampaign
BeverageCapacity
The default for Model Binding is to bind complex parameters from the body of the request. However, only one parameter per action may be bound from body.
So you need to either
Combine them into one class that just wraps / holds both parameters
as properties - and have them bound from the body (as one object)
Decide which to bind from the body, and which from
the route or the query and add the attributes [FromRoute] or [FromQuery] to one, and [FromBody] to the other.
ApiExplorerSettings from System.Web.Http.Description will ignore the attributed action from a help page, or whatever else (maybe swagger)... but you will still get this exception - from problems at level of Model Binding
My controller has some refactored code whose methods are marked public. Looks like either moving them out of the controller or marking private corrects this problem. Or attributing the pesky methods with [NonAction] might also be a choice as asked at asp.net Core mvc hide and exclude Web Api Controller Method
For me in the definition of the a new controller automatically add this prerequisite.
I removed it and it works
[Route("api/[controller]")]
[Apicontroller] //remove this line
I received error "has more than one parameter that was specified ..." for having mention of [ApiController] on top of class and then inheriting class from APIController.
Corrected issue by inheriting the class from Controller.
[Authorize]
[Route("api/the")]
**[ApiController]**
public class TheController : **Controller**
I got this error by inheriting from BaseController instead of ControllerBase. It was a class from another library that is completely unrelated and I misremembered the name. The exception was a red herring for me.
I had this problem but solved it by declaring my method as private.
In my case, the problem was that in the controller I had public methods that are not endpoints. And the ASP.NET Core mapping for controllers treats all public methods as endpoints and tries to map the request model because it uses reflection.
Another possible solution is to nest the complex data types in a tuple:
[ApiExplorerSettings(IgnoreApi = true)]
public decimal CalculatePriceWithCampaign((BeverageCapacityCampaign campaign, BeverageCapacity capacity) data, int count = 1)
{
switch (data.campaign.DiscountType)
{
case DiscountType.Fixed:
return (data.capacity.CapacityPrice - data.campaign.DiscountValue) * count;
case DiscountType.Percentage:
return (data.capacity.CapacityPrice * count) * data.campaign.DiscountValue;
default:
return data.capacity.CapacityPrice;
}
}
However, NSwag (Swagger) does not seems to be able to automatically parse this case because a non-valid example gets generated. NSwagStudio recognizes the case correctly and generates valid client code.
I have an API action defined as the following:
[Route(Name="GetMembersTest"), HttpGet, ResponseType(typeof(MemberHeadersDto))]
public IHttpActionResult GetMembers[FromUri]MemberFilterDto filter, [FromUri]PagingOptionsDto paging)
This method works as expected, routing and all, requests are flowing through just fine. However, I'd like to supply a "NextUri" for paging so that the caller can just keep following NextUri until it is null to get all the results. I have to send back a uri to the same action, 1 page ahead, if that makes sense.
So I tried using UrlHelper.Route. This route is named "GetMembers" for the purpose of this example.
NextUri = Url.Route("GetMembers", new { filter, paging });
The problem is that instead of getting something like
/v1/members?filter.q=&filter.otherproperty=&paging.count=10&paging.startRow=11
I get
/v1/members?filter=WebApi.Models.MemberFilterDto&paging=WebApi.Models.PagingOptionsDto
It looks like UrlHelper.Route doesn't support complex types in the [FromUri] parameter of a GET Request. Is there anything I can do to get this functionality? My workaround right now is to take in all the Dto properties as individual parameters then build my Dtos from them on the server. This isn't ideal because if I ever add any more options I'd have to add more parameters to the action, which also makes the route value dictionary more fragile as well because it has to match with the method signature in UrlHelper.Route(routeName,routeValues).
Unfortunately, there is no way to pass in complex object to routing. Instead, you will need to pass in the simple properties individually.
I was not able to find a way to extend Url.Route, but that would be/have been your best option.
I have a fixedURL to which I'd like to post different types of xml message, deserialized using DataContracts. Depending on the type of the deserialized message, I'd like to route to:
overloaded methods, e.g.
void Process(ContractType1 request) {}
void Process(ContractType2 request) {}
So at some point I need to deserialize this message and hopefully allow the default routing rules to match the correct method. Which extensibility point should I use for this? Or even better, can I make this work out of the box?!
If it makes any difference, I'm using MVC 3.
ASP NET MVC does not respect the overload if they are not decorated for different HTTP methods - e.g. one for POST, other for GET.
You need to use [ActionName(Name = "Process2")] to change the route name. And you will have to use different routes to access (if the HTTP methods are the same)
Have a look here.
Apart from the technical workaround, passing different contracts to the same URL is against the REST principles. Data could be in different format (XML, JSON, ...) but it must be the same. The URI defines a unique intention. Now it is possible to have a common dumpster where documents are all dumped to the same URI but then ASP NET MVC default model binder would not be able to help you and you need to create your own model binder.
Contrary to the other answer I say this is possible
Asp.net MVC is a great platform that can be easily extended. And so basically I've written a special action method selector that makes it possible to write overloads that can serve the same HTTP method but defer in parameters. By default you'd get runtime error that action method can't be resolved. But when you use this action method selector you get rid of this error.
Basically if your parameter classes have distinct parameter names, you can actually select methods by that.
Action method selector is called RequiresRouteValuesAttribute and a typical usage scenario would be with default route where id is optional in:
{controller}/{action}/{id}
This means that you either have to write
public ActionResult Index(int? id)
{
if (id.HasValue)
{
// display details view
}
else
{
// display master view
}
}
but by using my action method selector you can easily write two action methods:
public ActionResult Index()
{
// display master view
}
[RequiresRouteValues("id")]
public ActionResult Index(int id)
{
// display details view
}
The same could be applied to your action methods as long as your custom types have distinct property names or methods use different parameter names. So in your case it could be something like:
[RequiresRouteValues("first.Id")] // when you provide prefix with your form
// or
[RequiresRouteValues("Some.ContractType1.Distict.Property.Name")]
public ActionResult Process(ContractType1 first)
{
// do what's appropriate
}
[RequiresRouteValues("second.Id")] // when you provide prefix with your form
// or
[RequiresRouteValues("Some.ContractType2.Distict.Property.Name")]
public ActionResult Process(ContractType2 second)
{
// do what's appropriate
}
Read all the details about this action method selector and get the code as well.