CreatedAtAction() Cannot Find Route with Matching Parameters - asp.net-core

I am attempting to return a CreatedAtAction() result from within an ApiController "NotUnitsController" which indicates a route on a separate ApiController "UnitsController".
Everything works as expected until generating the CreatedAtAction() response. I am receiving the error:
System.InvalidOperationException: No route matches the supplied values.
I am not sure what I am missing. I have tried to remedy the issue with the following:
Verified the spelling of the route parameters on both controllers
Attempted to provide a route name to the [HttpGet] in the UnitsController & reference that name from the NotUnitsController.
Added a / to the [HttpGet("/{unitKey}")] route as suggested in this answer.
The v1 portion of the routes are hard-coded. I found that it could be an issue with dynamic route versioning on this GitHub issue.
I am also able to perform GET requests against the UnitsController endpoint. Only the CreatedAtAction() response is failing.
Here are snippets of the two controllers in question.
NotUnitsController:
[ApiController]
[Route("v1/not-units/{notUnitsKey:guid}/units")]
public class NotUnitsController : ControllerBase
{
[HttpPost]
public async Task<IActionResult> Post(Guid notUnitsKey, Input unitInput)
{
// TODO: Create Unit
var unitKey = Guid.NewGuid();
return CreatedAtAction(actionName: nameof(UnitsController.Get),
controllerName: nameof(UnitsController),
routeValues: new { unitKey },
value: new { unitKey });
}
}
UnitsController:
[ApiController]
[Route("v1/units")]
public class UnitsController : ControllerBase
{
[HttpGet("{unitKey:guid}")]
public async Task<IActionResult> Post(Guid unitKey)
{
// TODO: Get Unit by key
var unit = $"My Unit with Id: {unitKey}";
return Ok(unit);
}
}
Any help would be greatly appreciated.

Probably you should use nameof(UnitsController.Post) as the actionName value, and new { unitKey = unitKey } as the routeValues value on your CreatedAtAction response.

Related

HTTP Errors When Using POST Methods

I can't quite seem to figure out how to call HTTP POST functions from my Blazor WASM project hosted with ASP.NET. I am having trouble finding any examples of using POST methods past .NET 6 likely because it's so new. I've tried setting content-headers to JSON and many different ways of retrieving the request body from the actual controller function, but I just get 500, 415, and 400 errors. I've also tried not using model binding the the controller function, but to no avail. I do not believe this is the issue though, as using the [ApiController] attribute infers proper model binding as far as I know. I can only imagine the issue stems from the HTTP call.
The service that calls the method:
public async Task CreateUser(User user)
{
await _httpClient.PostAsJsonAsync("users", user);
}
The controller function:
[HttpPost]
public async Task PostUser(User user)
{
_context.Users.Add(user);
await _context.SaveChangesAsync();
}
The given from the above code is just a simple 400 error.
Also, I've added a test user into the database manually, and I'm able to retrieve it without any issues.
Here's some code from one of my demo projects showing API calls to get WeatherForecast records.
Here's the Web Assembly project DataBroker:
public class WeatherForecastAPIDataBroker : IWeatherForecastDataBroker
{
private readonly HttpClient? httpClient;
public WeatherForecastAPIDataBroker(HttpClient httpClient)
=> this.httpClient = httpClient!;
public async ValueTask<bool> AddForecastAsync(WeatherForecast record)
{
var response = await this.httpClient!.PostAsJsonAsync<WeatherForecast>($"/api/weatherforecast/add", record);
var result = await response.Content.ReadFromJsonAsync<bool>();
return result;
}
public async ValueTask<bool> DeleteForecastAsync(Guid Id)
{
var response = await this.httpClient!.PostAsJsonAsync<Guid>($"/api/weatherforecast/delete", Id);
var result = await response.Content.ReadFromJsonAsync<bool>();
return result;
}
public async ValueTask<List<WeatherForecast>> GetWeatherForecastsAsync()
{
var list = await this.httpClient!.GetFromJsonAsync<List<WeatherForecast>>($"/api/weatherforecast/list");
return list!;
}
}
And here's the controller it's calling:
namespace Blazr.Demo.Controllers;
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private IWeatherForecastDataBroker weatherForecastDataBroker;
public WeatherForecastController(IWeatherForecastDataBroker weatherForecastDataBroker)
=> this.weatherForecastDataBroker = weatherForecastDataBroker;
[Route("/api/weatherforecast/list")]
[HttpGet]
public async Task<List<WeatherForecast>> GetForecastAsync()
=> await weatherForecastDataBroker.GetWeatherForecastsAsync();
[Route("/api/weatherforecast/add")]
[HttpPost]
public async Task<bool> AddRecordAsync([FromBody] WeatherForecast record)
=> await weatherForecastDataBroker.AddForecastAsync(record);
[Route("/api/weatherforecast/delete")]
[HttpPost]
public async Task<bool> DeleteRecordAsync([FromBody] Guid Id)
=> await weatherForecastDataBroker.DeleteForecastAsync(Id);
}
The Repo for the Demo Project Blazor.Demo
Controller Code
Data Broker Code

In asp.net API HttpGet{id} works but when I change the {id} to something else it dose not work

Im pretty new to ASP core webAPI. Hopefully you guys can give me a hand.
I have been successfully using HttpGet{id} - postman has been serving the correct information back. However, I want to be able use "partNumber" in the url.
below is the working id get method:
'''
[HttpGet("{id}")]
public async Task<ActionResult<Model>> GetModel(int id)
{
return await _context.Models.FindAsync(id);
}
'''
Here is the actual parameters that I would like to search on:
```
[HttpGet("{PartNumber}")]
public async Task<ActionResult<Model>> GetModel(int partNumber)
{
return await _context.Models.FindAsync(partNumber);
}
```
Postman returns 404 not found in the latter when I should see data returned.
Any help would be greatly appreciated.
thanks
404 means the request url is not correct.
Here is my whole backend code and my controller contains [Route("api/[controller]")] attribute:
[Route("api/[controller]")]
[ApiController]
public class TestsController : ControllerBase
{
[HttpGet("{partNumber}")]
public async Task<ActionResult<Model>> GetModel(int partNumber)
{
}
}
The request url should be:https://localhost:44371/api/tests/YourPortNumber(https://localhost:44371/api/tests/1).
Result:
For NoContent return in postman,please set breakpoint and check if database contain such record with the specific partNumber:
[HttpGet("{partNumber}")]
public async Task<ActionResult<Model>> GetModel(int partNumber)
{
//set breakpoint here...
var data = await _context.Models.FindAsync(partNumber);
return await _context.Models.FindAsync(partNumber);
}
Result:
[HttpGet("{partNumber}")]
public async Task<ActionResult<Model>> GetModel(int partNumber)
{
return await _context.Models.FindAsync(partNumber);
}
The casing of the string in the route should match the parameter

Why does not working version in asp.net core controller

I want to use api version in my .net core project.Then search web and find that's solution.
Even though do exactly all solutions,but I can't get desired result.
So if any can help me,Please show me..
I add Microsoft.AspNetCore.Mvc.Versioning 4.0.0 Package in my project and ..
StartUp.cs
Then in my Controller Add Rout Attribute as Shown :
[ApiController]
[Authorize]
[Route("v{version:apiVersion}/[Controller]")]
[ApiVersion("1.0")]
public class SellerController : Controller
{
private readonly IBus _client;
private readonly string AppBaseUrl = MyHttpContext.AppBaseUrl;
//private readonly IGetUrl _globalUrl;
public SellerController(IBus client/*, IGetUrl globalUrl*/)
{
_client = client;
//_globalUrl = globalUrl;
}
[HttpGet("/Sellers/{SellerId}")] // Dashboard
public async Task<IActionResult> Info(long SellerId)
{
...
}
}
With these code I expected that I can send request to 'Info' method by this url :
But that's not working and get 404 error code status.. when I delete "/v1.0" from url and send request, that's working. I will be glad to help me .. Thanks
In your code, we can find that you applied [HttpGet("/Sellers/{SellerId}")] with route
template begin with / to Info action method, which don't get combined with route templates applied to the controller. To make request to 'Info' method, you could use below URL.
https://localhost:5090/sellers/17
I expected that I can send request to 'Info' method by this url : https://localhost:5090/v1.0/sellers/17
To achieve your requirement, you can try to modify the code like below.
[HttpGet("/v{version:apiVersion}/Sellers/{SellerId}")]
public async Task<IActionResult> Info(long SellerId)
{
//...
//for testing purpose
return Ok(SellerId);
}
Test Result
Update:
If you'd like to include v{version:apiVersion} in route template of controller level attribute routing, you can try to apply [HttpGet("{SellerId}")] to Info action method and make request with https://localhost:5090/v1.0/seller/17.
[ApiController]
[Authorize]
[Route("v{version:apiVersion}/[Controller]")]
[ApiVersion("1.0")]
public class SellerController : Controller
{
[HttpGet("{SellerId}")] // Dashboard
public async Task<IActionResult> Info(long SellerId)
{
//...

Unable to configure route for Get in WebApi 2

I'm struggling with something very basic. I'm trying to be get a response from my WebApi2 restful service, and I can't.
I have not edited the default WebApi (WebApiConfig.cs) route.
This is the controller
public class AboutController
{
[Route("api/about/{id:int}/{service1}/{service2}")]
public async Task<IHttpActionResult> Get(int accountId, string mainservice, string secondaryservice)
{
//logic
}
}
If I navigate (in a browser) to http://localhost:58090/api/about I get the error message The requested resource does not support http method 'GET'. I guess this makes sense, as it doesn't match the route (path).
If I update the path to something which matches the signature, such as http://localhost:58090/api/about/1/a/b I get the error message No action was found on the controller About' that matches the request.
Even if I add [HttpGet] to the controller, it makes no difference.
As a sanity test, I updated to
public class AboutController
{
public async Task<IHttpActionResult> Get()
{
//logic
}
}
and it does what is expected. I'm lost as to why adding the parameters has confused things so much.
I'm lost as to what I've done wrong
The route must match the parameters
[Route("api/about/{id:int}/{service1}/{service2}")]
public async Task<IHttpActionResult> Get(int id, string mainService, string secondaryService)
{
The above won't work, because it is expecting to see service1 and service2 based upon the route.
Update as per the example below
[Route("api/about/{id:int}/{service1}/{service2}")]
public async Task<IHttpActionResult> Get(int id, string service1, string service2)
{

Default parameter value in MVC 4 Web API

I am curious why the ApiController handles default parameter values on actions differently than a 'regular' Controller.
This code works just fine, request to /Test means page gets value 1
public class TestController : Controller
{
public ActionResult Index(int page = 1)
{
return View(page);
}
}
This code doesn't work when a request is made to /api/Values. It fails with:
"The parameters dictionary contains a null entry for parameter 'page' of non-nullable type 'System.Int32' for method 'System.Collections.Generic.IEnumerable`1[System.String] Get(Int32)' in 'MvcApplication1.Controllers.Controllers.ValuesController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
public class ValuesController : ApiController
{
public IEnumerable<string> Get(int page = 1)
{
return new string[] { page.ToString() };
}
}
Any hints on why this is?
Try adding the [FromUri] or [FromForm] parameter attribute.
public class ValuesController : ApiController
{
public IEnumerable<string> Get([FromUri]int page = 1)
{
return new string[] { page.ToString() };
}
}
Mike Stall has two good posts about parameter binding in Webapi which does not work as it does in ASP MVC. The big problem to get used to is that you can only read the request body once in your pipeline. So if you need to read more than 1 complex object as a parameter, you probably need to resort to ModelBinding by parameter. I had a problem similar to yours when I was reading the content body earlier in the pipeline for logging purposes and did not realize about the read once restriction above and had to solve with my own custom model binder.
Explains model binding at http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx and then suggests a way to make WebAPI model binding more like ASP MVC http://blogs.msdn.com/b/jmstall/archive/2012/04/18/mvc-style-parameter-binding-for-webapi.aspx
Try defining as Nullable<T>:
public class ValuesController : ApiController
{
public IEnumerable<string> Get(int? page = 1)
{
return new string[] { page.ToString() };
}
}