I have a controller that should return full urls of the other Actions in the same controller. I dont know how I can do this. Please look at my example (using pseudo results):
[Route("api/v{api-version:apiVersion}/Tests")]
[ApiController][AllowAnonymous]
public class TestsController : ControllerBase
{
[HttpGet]
public async Task<IActionResult> GetActions()
{
var idDemo = 1;
//TODO
urls.Add("Get full url for Call method with parameter : idDemo ");
urls.Add("Get full url for Something method with parameter : idDemo ");
return Ok(urls);
}
[HttpPost("call/{id}")]
public async Task<IActionResult> Call(int id)
{
return Ok();
}
[HttpPost("do/it/{id}")]
public async Task<IActionResult> Something(int id)
{
return Ok();
}
}
Related
Im trying to run controller method based on query parameters instead normal route (e.g.": MyController\DoSomething) but getting error. How solve this problem?
[Route("MyController?Command=DoTask1")]
public IHttpActionResult Task1()
{
return Ok();
}
[Route("MyController?Command=DoTask2")]
public IHttpActionResult Task2()
{
return Ok();
}
....
[Route("[controller]")]
public class MyController : Controller
{
[HttpGet("")]
public IActionResult Hello(string command)
{
switch (command.ToLowerInvariant())
{
case "dotask1":
Task1();break;
case "dotask2":
Task2(); break;
}
return Ok();
}
private void Task1()
{
}
private void Task2()
{
}
}
This way you'll have only one endpoint and those routes would be valid :
/my?command=DoTask1
/my?command=DoTask2
etc
I have a WebApi using .NET core 3.1. I'm not sure if this isn't best practice or if there's another way to do this. But, I have a WebApi that will have 3 get verbs.
1. Get All.
2. Get by Id.
3. Get by a search string.
It work if I have just 2, the get all and get by id but once I had a 3rd, get by string I get an error. Then when I go to test in swagger I get,
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The
request matched multiple endpoints. Matches:
Code:
[ApiController]
[Route("api/v1/[controller]")]
public class ProfessionalLocalController : ControllerBase
{
private IUnitOfWork _unitOfWork;
private readonly IMapper _mapper;
public ProfessionalLocalController(IUnitOfWork unitOfWork, IMapper mapper)
{
_unitOfWork = unitOfWork;
_mapper = mapper;
}
[HttpGet]
public async Task<IActionResult> Get()
{
var profLocals = await _unitOfWork.ProfessionalLocalUsers.GetAllAsync();
return Ok(_mapper.Map<IEnumerable<ProfessionalLocalDto>>(profLocals));
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
var localUser = await _unitOfWork.ProfessionalLocalUsers.GetAsync(id);
if (localUser == null)
{
return NotFound();
}
return Ok(_mapper.Map<ProfessionalLocalDto>(localUser));
}
[HttpGet("{searchText}", Name = "Search")]
public async Task<IActionResult> Get(string searchText)
{
var localUsers = await _unitOfWork.ProfessionalLocalUsers.FindAsync(temp => temp.UserID.ToString().Contains(searchText));
return Ok(_mapper.Map<IEnumerable<ProfessionalLocalDto>>(localUsers));
}
}
Routes need to be unique when mapped to controller actions.
In this case the routes [HttpGet("{id}")] and [HttpGet("{searchText}", Name = "Search")] conflict because to both map to the same URI.
api/v1/ProfessionalLocal/{some value here}
If you want to keep the same URI as before, and allow for the the two action to be distinct, use a route constraint.
//GET api/v1/ProfessionalLocal/1234
[HttpGet("{id:int}")] //<-- note the constraint on the id
public async Task<IActionResult> Get(int id) {
//...
}
So that the above is called only when a valid value is provided. An integer in this case
And consider changing the route template of the search to be more descriptive of what it actually does.
//GET api/v1/ProfessionalLocal/search/{search text here}
[HttpGet("search/{searchText}", Name = "Search")]
public async Task<IActionResult> Get(string searchText) {
//...
}
Reference Routing to controller actions in ASP.NET Core
Reference Routing in ASP.NET Core
You can try changing it like below:
[HttpGet]
[Route("users")]
public async Task<IActionResult> Get()
{
var profLocals = await _unitOfWork.ProfessionalLocalUsers.GetAllAsync();
return Ok(_mapper.Map<IEnumerable<ProfessionalLocalDto>>(profLocals));
}
[HttpGet("{id}")]
[Route("users/{id}")]
public async Task<IActionResult> Get(int id)
{
var localUser = await _unitOfWork.ProfessionalLocalUsers.GetAsync(id);
if (localUser == null)
{
return NotFound();
}
return Ok(_mapper.Map<ProfessionalLocalDto>(localUser));
}
[HttpGet("{searchText}", Name = "Search")]
[Route("usersbytext/{id}")]
public async Task<IActionResult> Get(string searchText)
{
var localUsers = await _unitOfWork.ProfessionalLocalUsers.FindAsync(temp => temp.UserID.ToString().Contains(searchText));
return Ok(_mapper.Map<IEnumerable<ProfessionalLocalDto>>(localUsers));
}
Add a custom route for third one.
The default routing is unable to distinguish [Route("users/{id}")] from [Route("users/{id}")] making it ambigous.
I ran into a similar problem recently. My solution was to use the same method for GetAll and Search. What I did was:
Check for the query parameter, if the query parameter is available, return search results else return all. Please refer to the snippet below.
[HttpGet]
public async Task<IActionResult> Get([FromQuery]string search)
{
if(!String.IsNullOrEmpty(search))
// return search result
//return all
}
I use this
[HttpGet("[action]")]
public async Task<IActionResult> GetAll()
{
var profLocals = await _unitOfWork.ProfessionalLocalUsers.GetAllAsync();
return Ok(_mapper.Map<IEnumerable<ProfessionalLocalDto>>(profLocals));
}
I'm trying to get a REST service up and running (I followed this tutorial), and was trying to extend it with a simple method to mark one of the ToDoItem as "Complete"; literally to pass an ID into a method which should mark it as "Complete".
However, I'm struggling to understand how the routing works.
This is the method provided by default, which works correctly via https://localhost:44388/api/values
If I add another GET operation, even with different [Route] attribute, then I end up with "AmbiguousActionException: Multiple actions matched"
[Route("api/values")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
I tried to specify a route prefix using the method below, so that I could add doesn't work; I get a 404 on https://localhost:44388/api/values and https://localhost:44388/api/values/getbyname
[RoutePrefix("api/values")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[Route("getbyname")]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
I might be trying the wrong method, so I'm happy to take any advice. I just want to be able to create new REST calls and have them the appropriate actions. Do I need to create other controllers? Am I limited to one GET/POST/PUT etc per controller?
Edit: didn't provide enough info, here's more code:
[Route("api/ToDo")]
[ApiController]
public class ToDoController : ControllerBase
{
private readonly ToDoContext _context;
public ToDoController(ToDoContext toDoContext)
{
_context = toDoContext;
if (_context.ToDoItems.Count() == 0)
{
//collection is empty, so add a new item
ToDoItem item1 = new ToDoItem(1, "example 1");
ToDoItem item2 = new ToDoItem(2, "example 2");
_context.ToDoItems.Add(item1);
_context.ToDoItems.Add(item2);
_context.SaveChanges();
}
}
//GET: api/todo
[HttpGet]
public async Task<ActionResult<IEnumerable<ToDoItem>>> GetToDoItems()
{
return await _context.ToDoItems.ToListAsync();
}
//GET: api/todo/5
//[HttpGet(Name = "Get a ToDoItem")]
//[Route("get{id}")]
[HttpGet("{id}")]
public async Task<ActionResult<ToDoItem>> GetToDoItem(long id)
{
var todoitem = await _context.ToDoItems.FindAsync(id);
if (todoitem == null)
{
return NotFound();
}
return todoitem;
}
//POST: api/Todo
[HttpPost]
public async Task<ActionResult<ToDoItem>> PostToDoItem(ToDoItem todoItem)
{
_context.ToDoItems.Add(todoItem);
await _context.SaveChangesAsync();
//calls the "GetToDoItem" method above!
return CreatedAtAction("GetToDoItem", new { id = todoItem.ID }, todoItem);
}
//DELETE: api/todo/5
[HttpDelete("{id}")]
public async Task<ActionResult<ToDoItem>> DeleteToDoItem(long id)
{
var todoItem = await _context.ToDoItems.FindAsync(id);
if(todoItem == null)
{
return NotFound();
}
_context.ToDoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return todoItem;
}
//* -. space to create a "MarkAsComplete" method
//GET: api/todo/5
[HttpGet(Name = "{name}")]
public async Task<ActionResult<ToDoItem>> MarkAsComplete(long id)
{
var todoitem = await _context.ToDoItems.FindAsync(id);
if (todoitem == null)
{
return NotFound();
}
else
{
todoitem.IsComplete = true;
}
return todoitem;
}
//*/
}
Mixing up different versions of the attributes. RoutePrefix is from a previous version.
Routes need to be unique per action to avoid route conflicts.
For example.
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase {
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get() {
return new string[] { "value1", "value2" };
}
// GET api/values/some_name
[HttpGet("{name}")]
public IActionResult GetByName(string name) {
return Ok();
}
}
Reference Routing to controller actions in ASP.NET Core
When building a REST API, it's rare that you will want to use [Route(...)] on an action method. It's better to use the more specific Http*Verb*Attributes to be precise about what your API supports. Clients of REST APIs are expected to know what paths and HTTP verbs map to specific logical operations.
Based on the additional details provided, that MarkAsComplete action should use HTTP PUT so signify that the model is being edited/updated.
For example
//* -. space to create a "MarkAsComplete" method
//PUT: api/todo/5
[HttpPut("{id:long}")]
public async Task<ActionResult<ToDoItem>> MarkAsComplete(long id) {
var todoitem = await _context.ToDoItems.FindAsync(id);
if (todoitem == null) {
return NotFound();
} else {
todoitem.IsComplete = true;
}
return todoitem;
}
//*/
I'm getting an array returned from an API call. The array looks something like this https://i.imgur.com/Rq8GfBI.png.
I get the array, then call my controller method using this
this.http.post<Maps[]>(this.baseUrl + "api/Map/InsertMap/", beatmaps[0]).subscribe();
Maps being an interface
interface Maps {
Id: number;
Name: string;
Artist: string;
Creator: string;
}
Now I just have a basic insert controller method
[Route("api/[controller]/InsertMap/")]
[HttpPost("[action]")]
public async Task<IActionResult> AsyncCreateMap(MapModel model)
{
await _mapService.AsyncInsert(model);
return Ok();
}
It takes in the Model as a parameter and then inserts it using Entity Framework. It doesn't work. I have no idea how to actually transfer the array I get to an object I can use in my controller.
Here is my whole controller class
[Route("api/[controller]")]
public class MapController : Controller
{
private readonly MapService _mapService;
public MapController(MapService mapService)
{
_mapService = mapService;
}
[Route("api/[controller]/Maps")]
[HttpGet("[action]")]
public async Task<IActionResult> AsyncMaps()
{
var data = await _mapService.AsyncGetMaps(0, 10);
return Ok(data);
}
[HttpPost]
public async Task<IActionResult> AsyncCreateMap([FromBody]MapModel model)
{
await _mapService.AsyncInsert(model);
return Ok();
}
}
you should try use [FromBody].
Example:
public class ModelDTO
{
public string Id{get; set;}
public List<string> Childs {get; set;}
}
[HttpPost]
[Route("api/nice/Save")]
public bool Save([FromBody] ModelDTO model)
{ ...
in the angular side, you should use httpClient.post..
save(data: IData): Observable<ISaveCompleted> {
const options = this.createPostOptions();
const saveCompleted = this.http
.post(options.url, data, options)
.map((res: Response) => <ISaveCompleted>res.json());
return saveCompleted;
}
Error is on line #5 of the code below:
public class FirstController : Controller{
public async Task<ActionResult> Index()
{
OtherController OtherController = new OtherController();
return OtherController.Index();
}
}
Then the OtherController:
public class OtherController : Controller{
public async Task<ActionResult> Index()
{
//Tasks...
//await...
return View();
}
}
I tried this and get the same error on line #5:
public class FirstController : Controller{
public ActionResult Index()
{
OtherController OtherController = new OtherController();
return OtherController.Index();
}
}
How do I get this to work?
You need to await the result of calling OtherController.Index(); as this method is marked as async. Because you are awaiting that call, your FirstController.Index method also needs to be marked as async.
In your FirstController.Index method you would therefore have:
return await OtherController.Index();