I created a new ASP.NET MVC4 Web Api Project. In addition to the default ValuesController, I added another controller, ScenarioController. It has the exact same methods as ValuesController. But for some reason, it behaves differently.
/api/values/ => "value1","value2"
/api/values/1 => "value"
/api/scenario/ => "value1","value2"
/api/scenario/1 => "value1","value2"
^^^^^^^^^^^^^^^^^
should return "value"!
Using breakpoints, I know that /api/scenario/1 actually gets sent to the public IEnumerable<string> Get(), not the expected public string Get(int id). Why?
For reference, here are the relevant files (these are pristine default mvc4-webapi classes, haven't modified anything):
Global.asax.cs
namespace RoutingTest
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
WebApiConfig.cs
namespace RoutingTest
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
//config.EnableQuerySupport();
// To disable tracing in your application, please comment out or remove the following line of code
// For more information, refer to: http://www.asp.net/web-api
config.EnableSystemDiagnosticsTracing();
}
}
}
ValuesController.cs
namespace RoutingTest.Controllers
{
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
}
}
ScenarioController.cs (yes, it's in the Controllers folder)
namespace RoutingTest.Controllers
{
public class ScenarioController : ApiController
{
// GET api/scenario
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/scenario/5
public string Get(int id)
{
return "value";
}
}
}
Gremlins. Thanks to #Pete Klien for verifying that the code does work outside my machine. Here's what I did.
Experienced problem of Controller only using 1 method for Get in original project.
Created new Web Api project, with code that I posted in the question. Same symptom.
Clean Project, Rebuild All, still no dice.
Reboot machine, clean, rebuild, try again, no dice.
Create new Web Api project in new solution, success!
I tried your code just now and got the expected result:
> curl http://localhost:53803/api/values
["value1","value2"]
> curl http://localhost:53803/api/values/1
"value"
> curl http://localhost:53803/api/scenario
["value1","value2"]
> curl http://localhost:53803/api/scenario/1
"value"
>
(By the way, there is no requirement that it be in the Controllers folder. HttpConfiguration.Routes.MapHttpRoute simply finds all your classes that inherit from ApiController.)
I am not being sarcastic when I suggest that you Rebuild All and try again.
I was having this issue and could not get anything to work. Finally I changed the port on the IIS Express Project Url setting and all is back to normal. It was localhost:57846. I just made it localhost:57847 and all is back to normal.
Related
I am starting a new web api, where i added a controller SRDSController, where i have a Get method, returns a string. But unfourtunately, it is not not returning it, the boilerplate asp.net web api code is returning it similarly. Please help
After a lot of google search, i have added
GlobalConfiguration.Configuration.EnsureInitialized(); in the Global.asax. But still not solved the issue.
The Global.asax :
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configuration.EnsureInitialized();
The SRDSController
[RoutePrefix("srds-data")]
public class SRDSController : ApiController
{
private ISRDSService srdsService = null;
public SRDSController()
{
srdsService = new SRDSService();
}
[Route("srds-test")]
[HttpGet]
public string GetTestData()
{
return "SRDS is working";
}
[Route("get-org-detail")]
[HttpPost]
public IHttpActionResult GetOrgDetails(SRDSRequest srdsRequest)
{
var data = srdsService.GetOrgDetails(srdsRequest.OrgNumbers, srdsRequest.UserName);
return Ok(data);
}
}`
As of now, whenever i am running the application, it is starting the application with http://localhost:59691/Help/, and it comes up with all the list of Get & Post Methods.
Feel free to ask any question
Thanks in advance
It seems to me that you may be missing this call:
config.MapHttpAttributeRoutes();
This must be done in the Register method of the class WebApiConfig. See Here for more details. Im saying this because you are using Attribute Routing in your action methods.
The reason why the call to help url works its because is being registered when you call RegisterAllAreas.
I need to build project, that implement REST API predefined by vendor application(which will consume it) - there is about thousand of REST-resources with some actions defined by different HTTP-Verb's(POST, GET, PUT, DELETE, etc..).
So, ideally, for each resource i should have single class like this:
public class SomethingController
{
public Something Post(string name, DateTime time)
{
// ...
}
public int PostStrange(string text)
{
// ...
}
public Something Put([FromBody]Something item)
{
// ...
}
public void Delete(int id)
{
// ...
}
}
In previous versions i can just call MapHttpRoute while registering routes, inherit classes like this from ApiController - and ASP.NET Web Api will do as i need... But in .NET Core i can't find anything like MapHttpRoute/ApiController.. Now there is routing and http-verb attributes, and i need to define everything explicitly for each class/method:
[Route("api/[controller]")]
public class SomethingController : Controller
{
[HttpPost]
public Something Post(string name, DateTime time)
{
// ...
}
[HttpPost("api/[controller]/strange")]
public int PostStrange(string text)
{
// ...
}
[HttpPut]
public Something Put([FromBody]Something item)
{
// ...
}
[HttpDelete]
public void Delete(int id)
{
// ...
}
}
Writing this attributes for each of thousands REST-resources is very boring and error prone...
Do i miss something here? Why in pretty new and modern ASP.NET Core that very common and important thing as building REST-Api made so over-complicated, compared to old ASP.NET?
There is nuget package Microsoft.AspNetCore.Mvc.WebApiCompatShim which main goal is to make migration from web api to core easier. It also provides a way to perform convention-based routing to actions you need. So, first install that package, then in startup:
public void ConfigureServices(IServiceCollection services) {
// add conventions here
services.AddMvc().AddWebApiConventions();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
app.UseMvc(routes => {
// map one global route
routes.MapWebApiRoute("WebApi", "api/{controller}");
});
}
After this small configuration you can inherit your controllers either from ApiController, which is added in package above for convenience of migration from web api, or native asp.net core Controller. Example of ApiController:
public class SomeController : ApiController {
// maps to GET /api/Some
// note - no routing attributes anywhere
public HttpResponseMessage Get() {
return new HttpResponseMessage(HttpStatusCode.OK);
}
// maps to POST /api/Some
public HttpResponseMessage Post() {
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
Native asp.net core controller:
// mark with these attributes for it to work
[UseWebApiRoutes]
[UseWebApiActionConventions]
public class TestController : Controller {
// maps to GET /api/Test
// no routing attributes, but two "conventions" attributes
public IActionResult Get(string p) {
return new ObjectResult(new { Test = p });
}
}
You can also mark your base controller with these attributes:
[UseWebApiRoutes]
[UseWebApiActionConventions]
public class BaseController : Controller {
}
public class TestController : BaseController {
// maps to GET /api/Test
// no attributes
public IActionResult Get(string p) {
return new ObjectResult(new { Test = p });
}
}
If you are not migrating from web api - I'd suggest to use native Controller. ApiController has different structure (similar to asp.net web api ApiController), so there is not much reason to use it for anything other than its intended goal (migration from web api).
MapRoute is still there https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing
Attribute routing compliments MapRoute, not replaces it.
Apparently there are quite a few examples which drop the piece about Routing in order to simplify example. So just dig dipper.
I followed this tutorial to create a WebAPI REST service.
After that, I could load the list of all contacts by pointing at http://baseaddress/api/Contacts.
Then I added the following code in the Register method in WebApiConfig.cs in order to enable an OData endpoint:
config.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Contact>("Contacts");
config.MapODataServiceRoute(
routeName: "OData",
routePrefix: "odata",
model: builder.GetEdmModel());
And also added the [EnableQuery] parameter on the Contact.GetContacts() method. That way, I am able to query for particular contacts like this:
http://baseaddress/odata/Contacts?$filter=startswith(Name,'A')
and it works like charm.
Unfortunately, when I put the [EnableQuery], the WebAPI endpoint stops working, showing instead the following error:
No non-OData HTTP route registered.
in System.Web.OData.Extensions.HttpConfigurationExtensions.GetNonODataRootContainer(HttpConfiguration configuration)
in System.Web.OData.Extensions.HttpRequestMessageExtensions.GetRootContainer(HttpRequestMessage request, String routeName)
in System.Web.OData.Extensions.HttpRequestMessageExtensions.CreateRequestScope(HttpRequestMessage request, String routeName)
in System.Web.OData.Extensions.HttpRequestMessageExtensions.CreateRequestContainer(HttpRequestMessage request, String routeName)
...
What should I do to fix this?
I encountered this problem, and since I'm working with dependency injections I managed to solve this by adding GlobalConfiguration.Configuration.EnableDependencyInjection() to my startup.cs
ex.
using System.Web.OData.Extensions;
public class Startup
{
public void Configuration(IAppBuilder app)
{
GlobalConfiguration.Configuration.EnableDependencyInjection();
}
}
Adding config.EnableDependencyInjection() in Startup.cs worked for me.
var config = new HttpConfiguration();
config.EnableDependencyInjection();
The key with this issue is to use .EnableDependencyInjection() on Configure method in Startup.cs
If you are using ASP.net Core endpoint routing (recommended if you
have at least .net core 3.0 and Microsoft.AspNetCore.OData v7.4.0)
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.Select().Filter().OrderBy().Count().MaxTop(10);
endpoints.EnableDependencyInjection();//This guy solves the problem
endpoints.MapODataRoute("odata", "odata", GetEdmModel());
});
Otherwise if you are using MVC routing (only way available prior .net
core 3.0 and Microsoft.AspNetCore.OData v7.4.0)
app.UseMvc(routeBuilder =>
{
routeBuilder.Select().Filter().OrderBy().Count().MaxTop(10);
routeBuilder.EnableDependencyInjection();//This guy solves the problem
routeBuilder.MapODataServiceRoute("odata", "odata", GetEdmModel());
});
Further reading: https://devblogs.microsoft.com/odata/enabling-endpoint-routing-in-odata/
I encountered this problem before and by the adding the line below it worked for me
protected void Application_Start()
{
GlobalConfiguration.Configuration.EnableDependencyInjection();
.... }
I got this error after updating my WebApi project dependencies (NuGet) to:
Microsoft.AspNet.OData, version="7.0.1"
Microsoft.OData.Core, version="7.5.0"
Microsoft.OData.Edm, version="7.5.0"
Microsoft.Spatial, version="7.5.0"
After downgrading to the versions I used before, the error was gone again:
Microsoft.AspNet.OData, version="5.8.0"
Microsoft.OData.Core, version="6.19.0"
Microsoft.OData.Edm, version="6.19.0"
Microsoft.Spatial, version="6.19.0"
that problem I solved this way:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
.... existing code...
//added:
//(OData need this)
config.EnableDependencyInjection();
config.Expand().Select().OrderBy().Filter();
}
}
In my case, I got the above error even though I had a separate project, which had no Odata code at all. So this was a very strange message to get.
My solution was to delete all the packages from the packages folder. After that it worked again. Some Odata packages were still in the this folder.
In my case, I had a web site with ODATA routes (and controllers), and other API routes (and controllers). What happened was my other routes were simply conflicting with ODATA ones, even with different C# namespaces and classes, etc.
Initially my controller was like this for example:
public class UserController : ApiController
{
[HttpPost]
public void Create([FromBody] string email)
{
}
}
I also had a "User" route in ODATA somewhere (different namespace, different urls, etc.). So I had to add explicitely the Route attribute, like this:
public class UserController : ApiController
{
[HttpPost]
[Route("api/user/create")]
public void Create([FromBody] string email)
{
}
}
I also got this error, and in my case it was an issue with case-sensitivity. I had called
https://www.example.com/odata/MyEntities instead of
https://www.example.com/odata/myentities, as it was registered.
Be sure to check your route configuration and calling url.
the controller name has to match the name you put in builder.EntitySet<SomeType>()
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
var config = new HttpConfiguration();
config.Count().Filter().OrderBy().Expand().Select();
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Order>("Order");// now the controller must be named OrderController
config.MapODataServiceRoute(routeName: "odata", routePrefix: null, model: builder.GetEdmModel());
appBuilder.UseWebApi(config);
}
}
public class OrderController : ODataController
{
// test data source
public List<Order> OrdersList { get; set; } = new List<Order>()
{
// put some test data
};
[HttpGet]
[EnableQuery]
public IQueryable<Order> Get() //Get: http://localhost:port/Order
{
return Orders.AsQueryable();
}
[HttpGet]
[EnableQuery]
public Order Get(int key) //Get: http://localhost:port/Order(key)
{
return Orders.FirstOrDefault(x => x.ID == key);
}
[HttpPost]
public bool Post(Order entity) //Post: http://localhost:port/Order
{
Orders.Add(entity);
return true;
}
[HttpPut]
public bool Put(int key, Order entity) //Put: http://localhost:port/Order(key)
{
var idx = Orders.FindIndex(x => x.ID == key);
Orders[idx] = entity;
return true;
}
[HttpPatch]
public bool Patch(int key, Order entity) //Patch: http://localhost:port/Order(key)
{
var idx = Orders.FindIndex(x => x.ID == key);
Orders[idx] = entity;
return true;
}
[HttpDelete]
public bool Delete(int key) //Delete: http://localhost:port/Order(key)
{
var idx = Orders.FindIndex(x => x.ID == key);
Orders.RemoveAt(idx);
return true;
}
}
PS: am using Microsoft.AspNet.OData 7.5.5
I just migrate from core 2.1 to .net core 3.1, and have the same issue. Here is how I fix:
app.UseMvc(routeBuilder =>
{
routeBuilder.EnableDependencyInjection(); //..another config
});
I am New in Asp.Net and tried to develop a small Web API in learning process.
WebApiConfig.cs
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/v1/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
TopicsController.cs
namespace MessageBoard.Controllers
{
public class TopicsController : ApiController
{
private IMessageBoardRepository _repo;
public TopicsController(IMessageBoardRepository repo)
{
_repo = repo;
}
public IEnumerable<Topic> Get()
{
var topics = _repo.GetTopics()
.OrderByDescending(t => t.Created)
.Take(25)
.ToList();
return topics;
}
}
}
Actually i am watching PluralSight tutorials.
http://localhost:50031/api/v1/topics
this Url is not working in Browser not in Fiddler 4.
all references are added. i have also done Build Solution but its not working and their is no error showing in the code.
One last step to enable Web Api which looks like you're missing is enabling Web API in the Global.asax file by adding the following line of code to the Application_Start() method:
WebApiConfig.Register(GlobalConfiguration.Configuration);
Also, please don't use the port number from the PluralSight tutorial.You need to run the web application project from your instance of Visual Studio and when it opens up in the browser you will see which port is assigned to YOUR api service.So if you see that it assigned port 12345 for example you would call the following URL to access the service action:
http://localhost:12345/api/v1/topics
Add attribute routing to controller
[Route("api/v1/topics")]
public IEnumerable<Topic> Get()
{
var topics = _repo.GetTopics()
.OrderByDescending(t => t.Created)
.Take(25)
.ToList();
return topics;
}
I've recently asked a few questions about the best way to create a web api which utilises the same url as my main mvc site. I deduced the best way was to make the necessary changes to my MVC site to include web api and the necessary routing.
I have mainly followed How to add Web API to an existing ASP.NET MVC 4 Web Application project? but I have run into problems. The code compiles fine and it is clearly looking for the route but I get the error:
No HTTP resource was found that matches the request URI 'http://localhost:2242/api/value'.
No type was found that matches the controller named 'value'.
My WebApiConfig:
class WebApiConfig
{
public static void Register(HttpConfiguration configuration)
{
configuration.Routes.MapHttpRoute("API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional });
}
}
my global.asax:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
Database.SetInitializer<ApplicationDbContext>(null);
}
}
my api controller:
public class ValuesController1 : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<controller>/5
public string Get(int id)
{
return "value";
}
// POST api/<controller>
public void Post([FromBody]string value)
{
}
// PUT api/<controller>/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
}
Other posts have corroborated that this is a correct and working setup...I created a separate webapi project to compare and this is all correct routing wise apparently. It would be far preferable to build this into my MVC website, does anyone have any ideas? This poster No type was found that matches controller had the same problem and the solution he found was to copy everything into a new project....that really isn't something I want to do/see why I should need to do.
I think it is because of your Controller's name : ValuesController1
A controller has to be suffixed by "Controller", the 1 may be the cause of your issue.
The name of the controller ValuesController1 doesn't match convention - in order for the default route to match /api/value based on the default convention set in your call to configuration.Routes.MapHttpRoute(...), the controller should be called ValueController:
public class ValueController : ApiController
{
public IEnumerable<string> Get()
// ...
However, if you intend to deviate from the configured convention, you can apply RouteAttribute and RoutePrefixAttribute in conjunction with the Http* verb attributes to customise controller and method routes, e.g.
[RoutePrefix("api/Foo")]
public class ValuesController : ApiController
{
// get api/Foo/value
[HttpGet]
[Route("value")]
public IEnumerable<string> NameDoesntMatter()
{
return new string[] { "value1", "value2" };
}
// get api/Foo/value/123
[HttpGet]
[Route("value/{id}")]
public string AnotherRandomName(int id)
{
return "value";
}
Before using the RouteAttribute you will need to add the following to your WebApiConfig.Register(HttpConfiguration config):
config.MapHttpAttributeRoutes();
Even with the routing attributes, note however that the controller class name still needs to end with the suffix Controller, i.e. cannot end in the suffix 1. It is surprisingly difficult to alter this convention.