Web Api OdataController PATCH action - c#

I am working on building CRUD OData (ODataController) controllers. For this request
PATCH http://localhost/MySite/api/MyData(1)
a PATCH action declared as below works. The model has single property marked with KeyAttribute
public async Task<IHttpActionResult> Patch(
[FromODataUri] int key,
Delta<MyModel> modelDelta)
This one doesn't work (note that I named parameter id instead of key). It returns 404 - resource not found:
public async Task<IHttpActionResult> Patch(
[FromODataUri] int id, // <--- invalid argument name?
Delta<MyModel> modelDelta)
I also tried to mark this action with [ODataRoute("({id})")] but I get this
The path template '({id})' on the action 'Patch' in controller 'MyData' is not a valid OData path template. Empty segment encountered in request URL. Please make sure that a valid request URL is specified.
Do I have flexibility to name parameters specifically? And especially when I may have 2-3 keys/parameters. I want to be able to use patch as
public async Task<IHttpActionResult> Patch(
[FromODataUri] int id1,
[FromODataUri] int id2,
Delta<MyModel> modelDelta)

Is your class declared like this:
//Must declare this attribute to relative ODataRoute Works
[ODataRoutePrefix("MyData")]
public class MyDataController : ODataController
....
otherwise must declare the route
[ODataRoute("MyData({id})")]
...

Related

Pass Int[] in HTTP Get to CoreWebAPI

I have a Blazor WebAssembly Hosted project and am in need of posting up an Array of Ids to get.
[AllowAnonymous]
[HttpGet("GetByTypePaged/{ingredientTypeIds}/{startIndex}/{count}/{search?}")]
public async Task<IActionResult> GetByTypePaged([FromQuery]int[] ingredientTypeIds, int startIndex, int count, string? search)
However the ingredientTypeIds comes back as int[0] so no Ids are being retrieved. Ive tried FromRoute and i get a 415 as well as without FromQuery
FromUri does not exist within the name spaces
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
Id have to install the Microsoft.AspNet.WebApi.Core nuget package to get the System.Web.Http namespace. However, then the
[Authorize]
[Route]
[HttpGet]
etc all have ambiguity between the System.Web.Http and AspNetCore. If I remove the references to the Microsoft.AspNetCore then I loose the [FromBody] for my HttpPut operation.
public async Task<IActionResult> Update(int id, [FromBody] Ingredient ingredient)
Whats my best approach here
[HttpGet("GetByTypePaged/{ingredientTypeIds}/{startIndex}/{count}/{search?}")]
In your code we can find you defined a route with route parameters ingredientTypeIds, startIndex, count and optional one search.
Please note that route data and query string values are used only for simple types, if you'd like to pass an array of Ids in URL and bind to action parameter int[] ingredientTypeIds, you can modify your route and pass it through query string in this format:
ingredientTypeIds[0]=1&ingredientTypeIds[1]=2
Besides, if you'd like to pass complex-type data to your backend API, you can make your endpoint support the HTTP POST method, and you can pass the expected data through request body.

Delete request without id parameter in .net webApi

I have .net core WebApi like below. And it is working perfectly. But, when I write [HttpDelete] instead of [HttpDelete("{id}")] , then it doesn't work. What can be reason ?
My url : http://localhost:5004/api/Student/DeleteStudent/23
[ApiController]
[Route("api/[controller]/[action]")]
public class StudentController : ControllerBase
{
//[HttpDelete] ///////////////// This is not working
[HttpDelete("{id}")] /////////// This is working
public async Task<ServiceResult> DeleteStudent(int id)
{
return await studentService.DeleteStudent(id);
}
}
Without the {id} route template parameter, the only other way to populate the value would be via query string
http://localhost:5004/api/Student/DeleteStudent?id=23
The route table will match the querystring parameter to the action parameter to make the match.
Either way, the id needs to be provided to know which action to call and which record to delete.
You have to tell the router about your api signature.. now by replacing [HttpDelete("{id}")] by [HttpDelete] your signature become api/[controller]/[action], thus your router will ignore anything after that signature.
You can define custom route if you like to leave it as [HttpDelete] where you will also specify id as a parameter.

WebAPI 2 : Default GET ALL is invoked with wrong parameter

I am using WebAPI 2 with EF and scaffolding webapi controllers from visual studio.
Each controller is created with 4 default verbs (GET,PUT,DELETE,POST) and 5 actions. while there are two versions of GET action.
IQueryable<entity> GetEntities ()
Task<IHttpActionResult> GetEntity(GUID key) // default is int id but I changed to guid.
I am using attribute routing and route prefix for the controller. just some fancy keywords for better management of url. [RoutePrefix("api/v3/Company")]
Problem :
Ideally when a wrong parameter is sent in url, it should return error, but it is not raising error, instead it fall back to the action without parameter.while if I send a wrong GUID, it shows error.
Like if I call :
http://localhost:8080/api/v3/Company/1f7dc74f-af14-428d-aa31-147628e965b2
it shows the right result.
when I call :
http://localhost:8080/api/v3/Company/1f7dc74f-af14-428d-aa31-147628e96500 (wrong key)
it set back to GetEntity() function and shows all records
when I call:
http://localhost:8080/api/v3/Company/1 (not a GUID length parameter)
it do the same and shows all records.
I am using attribute [Route("{id:guid}")]
Really appreciate if I can get some guidance on this!
It is most likely that the route is defaulting back to the convention-based mapping.
You need to explicitly make apply the route attribute on actions to let the routing know that it is the default route got GET
[RoutePrefix("api/v3/Company")]
public class CompanyController : ApiController {
//GET api/v3/Company
[HttpGet]
[Route("")] //Default Get
public IQueryable GetEntities() { ... }
//GET api/v3/Company/1f7dc74f-af14-428d-aa31-147628e965b2
[HttpGet]
[Route("{id:guid}")] // ALSO NOTE THAT THE PARAMETER NAMES HAVE TO MATCH
public Task<IHttpActionResult> GetEntity(Guid id) { ... }
//...other code removed for brevity
}
Make sure that attribute routing is enabled in the web api config
config.MapHttpAttributeRoutes();

Route Disambiguation in ASP.NET Core (MVC 6)

When ASP.NET Core encounters ambiguously named routes, it becomes inert. That is, the application will run without exceptions thrown but, it will fail to process any requests, on any controllers. The calling client receives 500 responses.
I'll show how I got into this mess, and I'd like suggestions of how to fix it.
I have a controller that looks like this:
[Route("api/Subscribers/{id}/[controller]")]
[Route("api/Organizations/{id}/[controller]")]
public class AddressesController : Controller
{
[HttpGet("{aid}", Name = "PostalLink")]
public async Task<IActionResult> GetAddress(Guid id, Guid aid)
{
//...implementation is irrelevant for this question.
}
[HttpPost]
[SwaggerResponseRemoveDefaults]
[SwaggerResponse(HttpStatusCode.Created, Type = typeof(PostalRecord))]
public async Task<IActionResult> CreateAddress(Guid id, [FromBody] PostalAddress address)
{
address.ID = Guid.NewGuid();
await createAddress.Handle(address);
return CreatedAtRoute("PostalLink", new { id = id, aid = address.ID });
}
Why the two route prefixes on the controller? Because it fits my microservices (and Swagger documentation) strategy. Nevertheless, in this example ASP.NET Core does not know how to resolve the route name "PostalLink" because it is implicitly bound to the two prefixes:
[Route("api/Subscribers/{id}/[controller]")]
[Route("api/Organizations/{id}/[controller]")]
I can fix the problem simply by changing the HttpGet so that instead of this:
[HttpGet("{aid}", Name = "PostalLink")]
I have this:
[HttpGet("{aid}")] //the route is no longer "named"
Unfortunately, removing the route name is not a real option for me.
What is the prescribed way to fix this?
Below are some of the options I'm considering.
Possibility #1
Theoretically, ASP.NET could simply "figure it out" by itself. For example, if the current request resolved to the route containing the word "Subscribers", then the "PostalLink" name should reference that route. Seen this way, perhaps my code is exposing a bug, defect, or oversight in ASP.NET Core.
Possibility #2
I could collapse my two prefix routes into a single route like this:
[Route("api/{parent}/{id}/[controller]")]
This works, but it undermines my REST documentation strategy. I'm using Swashbuckle to publish endpoint metadata. I want a user of my API to expressly see that my "Addresses" API is serving either "Subscribers" or "Organizations". When I have two explicit route prefixes, the Swagger documentation works correctly (and I properly validate the URI used by the client).
Possibility #3
I could simply override the two prefixes like this:
[HttpGet("~/api/Subscribers/{id}/Addresses/{aid}", Name = "SubscriberLink")]
[HttpGet("~/api/Organizations/{id}/Addresses/{aid}", Name = "OrganizationLink")]
public async Task<IActionResult> GetAddress(Guid id, Guid aid)
{
//...implementation is irrelevant for this question.
}
Now my documentation and route validation works, but my implementation is forced to check which route was used to reach the endpoint. That is very doable, but very annoying.
Possibility #4
Perhaps there is a more expressive way to handle this problem without attribute-based-routing? If yes, please share!
Details
My project.json is configured as follows:
"frameworks": {
"dnx46": { }
},
I am using DNX SDK version 1.0.0-rc1-update1. Also, I posted a related SO question for those who would like more context of what I am trying to do.
If your route names are the same for all your actions, why not specify them directly on the controller ?
[Route("api/Subscribers/{id}/[controller]", Name = "SubscriberLink")]
[Route("api/Organizations/{id}/[controller]", Name = "OrganizationLink")]
public class AddressesController : Controller
{
[HttpGet("{aid}")]
public async Task<IActionResult> GetAddress(Guid id, Guid aid)
{
//...implementation is irrelevant for this question.
}
}
Have you looked into attribute routing?
E.g. Registering routes with ASP.Net 5's MVC 6 Attribute Routing
Sample from the relevant documentation:
In the following example, app.UseMvc(); is used in the Configure method and no route is passed.
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
public IActionResult Index()
{
return View();
}
[Route("Home/About")]
public IActionResult About()
{
return View();
}
[Route("Home/Contact")]
public IActionResult Contact()
{
return View();
}
}
The HomeController.Index() action will be executed for any of the URL paths /, /Home, or /Home/Index.

How do I use ASP.NET Web API Attribute Routing with a complex object parameter?

I have a Web API action that looks like the following:
[HttpGet]
[Route("api/query/hello/{query}")]
public HttpResponseMessage Hello([FromUri]Query query)
{
return null;
}
where the Query class has a public string property named QueryText. When I hit the following URL, I get a 404 error:
/api/query/hello?QueryText=bacon
This worked before I started using Attribute Routing. If I have no parameters or primitive type parameters, I can get Attribute Routing to work. But with a complex parameter, I get 404s. How does Attribute Routing work with complex action parameters? Is it compatible with the FromUri attribute?
The solution here was that the {query} token in the Route definition was superfluous. Removing it, as follows, fixed the issue:
[Route("api/query/hello")]
The [FromUri] attribute will be needed because you're reading from the URL. Your route should look something like:
public HttpResponseMessage Hello([FromUri]Query query)
{
//Implement whatever
return null;
}
/api/{Controller Name}/hello?QueryText=bacon
Should then work correctly.
The model binder will take whatever query parameters you provided then try to bind whatever is inside that Query object. I'd worry about the Route Attribute after you've got it working first.

Categories

Resources