I'm migrating a midsize application built with asp.net web api 2 to .net 5 web api. The problem is that I have now refactored the controllers and found that the way that the parameters passed by the query string do not have the same behavior in the .net core for routes.
So my problem is that to be able to switch to .net 5 I need to keep these apis working as they are today, I am not able to change routes and parameters at this time. I have routes as in the example below and that return the error AmbiguousMatchException: The request matched multiple endpoints:
// GET: api/<ValuesController>
[HttpGet]
public IEnumerable<string> GetAll()
{
return new string[] { "value1", "value2" };
}
// GET: api/<ValuesController>?message=world
[HttpGet]
public string GetMessage([FromQuery] string message)
{
return $"Hello {message}";
}
This controller works with web api 2. I saw that there is a compatibility pack for migration but it was removed in .NET 5 (Microsoft.AspNet.Mvc.WebApiCompatShim). I even tried to use it but runtime errors occur when registering its use.
I would like to know if someone has been through this and managed to solve it successfully, I already researched a lot and the most I found was some people talking to use IActionConstraint but the examples I found were to define mandatory parameters, it didn't seem to fit my problem.
If anyone knows something that can put me in the direction of a solution I would appreciate it.
Not sure if you can do it automatically, but you could do this:
private IActionResult GetAll()
{
return new string[] { "value1", "value2" };
}
[HttpGet]
public IActionResult GetMessage([FromQuery] string message)
{
if(message == null)
return GetAll();
return $"Hello {message}";
}
Related
I am trying ASP.NET Core 3.1, MVC.
I am confused with the return type.
According to many links, such as https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-web-api?view=aspnetcore-3.1&tabs=visual-studio we set our signature to include the return type
EG
public async Task<ActionResult<TodoItem>> GetTodoItem()
{
return new ToDoItem();
}
Historically I'd return a status code and the return object, such as
public async Task<ActionResult> GetToDoItem()
{
return Ok(new ToDoItem());
}
It's really unclear why we don't return the status code in this manner any more. It's possible I'm getting confused by WebApi and MVC controllers, but as I've read more into this, I believe there is no real difference (in that it's simply an exposed end point)
This choice would be a good choice for mistakes
public IActionResult Get()
if(username == password )
{
return Ok(username);
}else
{
return BadRequest("False");
}
In Asp.net WebApi2
when api/values/9b858599-7639-45da-acd6-a1323fb019b5 is called get Action is invoked.
Action with optional parameters.
When api/values/9b858599-7639-45da-acd6-a1323fb019b5?maxRecords=100 or
api/values/?maxRecords=100 GetProducts Action is invoked.
In Asp.net Core
But in asp.net core when api/values/9b858599-7639-45da-acd6-a1323fb019b5 is called GetProducts Action is getting invoked. I wanted to call Get action without changing existing url's.
How to fix this issue in Asp.net core 2.0
Contoller
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
//https://localhost:44323/api/values/9b858599-7639-45da-acd6-a1323fb019b5
[HttpGet("{productId:Guid}", Order = 1)]
public ActionResult<string> Get(Guid productId)
{
return "value1";
}
//https://localhost:44323/api/values/9b858599-7639-45da-acd6-a1323fb019b5?maxRecords=100
//https://localhost:44323/api/values/?maxRecords=100
[HttpGet("{startRecordId:Guid?}")]
public ActionResult<IEnumerable<string>> GetProducts(Guid? startRecordId, int maxRecords, DateTimeOffset? minimumChangeDate = null)
{
return new string[] { "value1", "value2" };
}
}
Startup.cs
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute("DefaultApi", "api/{controller}/{id?}");
});
While not ideal, and this is a temporary shoehorn, till you can update the client calls. You could try this, if you can assume that you must have a maxRecords QueryString. Since if it defaults to 0 it is useless, unless you have logic in place in the case that it is 0.
//https://localhost:44323/api/values/9b858599-7639-45da-acd6-a1323fb019b5
[HttpGet("{productId:Guid}", Order = 1)]
public ActionResult<string> Get(Guid productId)
{
return "value1";
}
//https://localhost:44323/api/values/9b858599-7639-45da-acd6-a1323fb019b5?maxRecords=100
//https://localhost:44323/api/values/?maxRecords=100
[HttpGet("{startRecordId:Guid?}")]
public ActionResult<IEnumerable<string>> GetProducts(Guid? startRecordId, [FromQuery] int maxRecords, [FromQuery] DateTimeOffset? minimumChangeDate = null)
{
if (!Request.GetDisplayUrl().Contains(nameof(maxRecords)) &&
startRecordId.HasValue)
{
return Get(startRecordId.Value);
}
return new string[] { "value1", "value2" };
}
Your URIs aren't following RESTful convention. Frankly, I can't see how you ever made this work in the first place, because the same problem should have resulted in ASP.NET Web Api, but it's possible you just got lucky about the way the routing was done in ASP.NET Web Api. ASP.NET Core handles routing completely differently.
Regardless, a route for list of multiple products should not contain an id in the actual URI. In other words:
/api/values - Multiple values
/api/values/{id} - Single value
For things like filtering, ordering, etc. the multiple records, those should be part of the query string. That includes something like startRecordId:
/api/values?startRecordId={id}
Remove the ambiguity in your routes and you'll have no issue. Long and short, you can't have two routes both accepting a Guid in the same path segment, even if it's optional on one. Move the startRecordId into the query string, which is the correct way to do it anyways, and you're good to go.
You could combine your actions and change the behaviour based on which parameters have a supplied value;
//https://localhost:44323/api/values/9b858599-7639-45da-acd6-a1323fb019b5
//https://localhost:44323/api/values/9b858599-7639-45da-acd6-a1323fb019b5?maxRecords=100
//https://localhost:44323/api/values/?maxRecords=100
[HttpGet("{startRecordId:Guid?}")]
public ActionResult<IEnumerable<string>> GetProducts(Guid? startRecordId, int? maxRecords, DateTimeOffset? minimumChangeDate = null)
{
if (startRecordId.HasValue && !maxRecords.HasValue)
return "value";
else
return new string[] { "value1", "value2" };
}
This is a basic question. I am new to ASP.Net Core so I created a .Net Core Web API project using the template in Visual Studio 2017 and I would like to know how to return a Json string from the Get() function.
The Get() function provided.
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
I would like to know how to change so it returns a Json string of int variable like the following.
// GET: api/MOER
[HttpGet]
public <<some return type>> Get()
{
_MOER = 32;
return <<return a Json result/string of _MOER>>;
}
I am have seen the Nuget package Newtonsoft.Json where you serialize/deserialize but I am not sure if its applicable any more with .Net Core.
I have also seen examples where they use JsonResult but when I try to use this approach, the compiler doesn't know what Json() is.
[HttpGet]
public JsonResult Get()
{
_MOER = 32;
return Json(_MOER);
}
Thank you for your help!
Add this attribute to your controller class:
[Produces("application/json")]
So it becomes:
[Produces("application/json")]
public class YourController: Controller {
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
That should be enough, otherwise I believe the default is XML (unless the client explicitly asks for JSON using the Accept HTTP header).
I am trying to add a Get() function in a MVC 6 (Asp .Net 5) Web Api to pass a configuration option as a query string. Here are the two functions that I already have:
[HttpGet]
public IEnumerable<Project> GetAll()
{
//This is called by http://localhost:53700/api/Project
}
[HttpGet("{id}")]
public Project Get(int id)
{
//This is called by http://localhost:53700/api/Project/4
}
[HttpGet()]
public dynamic Get([FromQuery] string withUser)
{
//This doesn't work with http://localhost:53700/api/Project?withUser=true
//Call routes to first function 'public IEnumerable<Project> GetAll()
}
I've tried several different ways to configure the routing, but MVC 6 is light on documentation. What I really need is a way to pass some configuration options to the list of Projects for sorting, custom filtering etc.
You can't have two [HttpGet]s with the same template in a single controller. I'm using asp.net5-beta7 and in my case it even throws the following exception:
Microsoft.AspNet.Mvc.AmbiguousActionException
Multiple actions matched. The following actions matched route data and had all constraints satisfied:
The reason for this is that [From*] attributes are meant for binding, not routing.
The following code should work for you:
[HttpGet]
public dynamic Get([FromQuery] string withUser)
{
if (string.IsNullOrEmpty(withUser))
{
return new string[] { "project1", "project2" };
}
else
{
return "hello " + withUser;
}
}
Also consider using Microsoft.AspNet.Routing.IRouteBuilder.MapRoute() instead of attribute routing. It may give you more freedom defining the routes.
Accessing the query string is very doable by using either the RESTful routing conventions (enforced by ASP.NET 5 / MVC 6 by default) or by defining custom routes, as explained in this answer.
Here's quick example using custom, attribute-based routes:
[HttpGet("GetLatestItems/{num}")]
public IEnumerable<string> GetLatestItems(int num)
{
return new string[] { "test", "test2" };
}
For more info about custom routing, read the following article on my blog.
Hi im learning about REST API and used this link http://www.codeproject.com/Articles/588909/ASP-NET-WEB-API-Basics-MVC-4 . And i have developed a small API . Following is my controller
public class FileController : ApiController
{
[HttpGet]
public List<files> get()
{
DBQuery k = new DBQuery();
List<files> listOfFiles = k.GetAll();
return listOfFiles;
}
[HttpPost]
public int get(int v)
{
return (v);
}
having two methods namely get and upload and by using the below url i can access the get method
http://localhost:53081/api/File
but when i give http://localhost:53081/api/File/1 this i can access upload method . Still im getting the get methods result
what went wrong in my code . Kindly help me . im new to this
update
if i cal the method upload im still getting the get methods result on my screen
what went wrong
Images
and for this http://localhost:53081/api/File/1 also im getting 404 error and i have used REST Client plugin to post the url
You might be best to rename your methods like so
[HttpGet]
public List<File> Get()
{
return myFileClass.MyMethodThatReturnsAListOfFiles;
}
[HttpGet]
public File Get(int id)
{
return myFileClass.MyMethodThatReturnsASpecificFile(id);
}
The upload method you have is a bit confusing as it should really take in a File object and be restricted to only use the HttpPost method. Youd need to use a rest client (plugin for chrome, or use Fiddler) to get this to work, but stick with the pattern above and you'll be ok.
My guess at a method to replace your upload method is
[HttpPost]
public File Post(File uploadedFile)
{
return myFileClass.MyMethodThatSavesAFileAndReturnsItWithANewId(uploadedFile);
}