I'm completely stuck on this - after commenting out almost everything to do with startup configuration leaving only the following, I still can't get the WebApi Attribute routing to work correctly.
I have the following Global.asax.cs:
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
The following controller - located in an area called "v2":
[RoutePrefix("v2/businesses")]
public class BusinessesController : ApiController
{
// GET: v2/businesses/{id}
[HttpGet]
[Route("{id:int}")]
public ApiResponse<ApiBusiness> Index(int id)
{
return new ApiResponse<ApiBusiness>(new ApiBusiness());
}
}
The following area registration:
public class V2AreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "v2";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.Routes.MapMvcAttributeRoutes();
}
}
And the following WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
}
}
When I try to access the endpoint URL businesses/1 or v2/businesses/1 or v2/v2/businesses/1 all three return a 404, I was sure one of those would be correct.
Note that a different controller outside of the "v2" area works perfectly fine:
[RoutePrefix("abc")]
public class TestingController : ApiController
{
[HttpGet]
[Route("123")]
public int Test()
{
return 2;
}
}
Sure enough, 2 is returned when I access that endpoint (abc/123).
Can anyone explain why this isn't working?
In this situation you are saying that "v2" is both and mvc route AND a web api route; they can't coexist.
I mean, they can, but what happens here is that the requests to the web API will be mistakenly handled as if they were an MVC action. That's why you get a 404.
In less words, Web API doesn't really support areas. What you can do here is try to register the web api routing before the mvc routing or explicitly handle the web api rounting using MapHttpRoute. For example:
context.Routes.MapHttpRoute("AreaName_WebApiRoute",
"AreaName/Api/{controller}/{id}",
new { id = RouteParameter.Optional });
Check this SO answer for more details!
Related
In Visual Studio 2019, I created an ASP.NET Web API project. I created a Web API 2 controller. I followed this link 1 and link 2 to implement dependency injection using Unity.
I have shared the code according to link 1 but it is not working. I get an error in method GetService(Type serviceType) of UnityResolver (which I copied from first link).
Error is:
Exception thrown: 'Unity.ResolutionFailedException' in Unity.Container.dll
I checked this using breakpoints. It is returning null although container is not null.
I have been searching this so long. I don't understand where it is wrong or I did something wrong.
I am new to .NET Framework and also to programming. Any help or advice appreciated.
public object GetService(Type serviceType)
{
try
{
return container.Resolve<Type>();
//return container.Resolve(serviceType);
}
catch (ResolutionFailedException)
{
return null;
}
}
MyAPIController.cs
public class MyAPIController : ApiController
{
private IAgent agent;
public MyAPIController (IAgent _agent)
{
this.agent= _agent;
}
[HttpPost]
[Route("AgentService")]
public string GetAgentServices([FromBody] InputData inputData)
{
return "nothing";
}
}
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var container = new UnityContainer();
container.RegisterType<IAgent, Agent>();
config.DependencyResolver = new UnityResolver(container);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Global.asax.cs
public class WebApiApplication : 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);
}
}
I want to have a Controller Hierarchy assembly at webapi 2 website to handle common CRUD service operation.
I Started with an abstract webapi class like this (I removed unnecesary code and injection for simplicity):
public abstract class EntityController<T> : ApiController {
[Route("GetAll")]
[HttpGet]
public IHttpActionResult GetAll()
{
//code
}
[Route("Add")]
[HttpPost]
public IHttpActionResult Add(T entity)
{
//code
}
[Route("Delete")]
[HttpDelete]
public IHttpActionResult Delete(T entity)
{
//code
}
[Route("Edit")]
[HttpPost]
public IHttpActionResult Edit(T entity)
{
//code
}
}
First, I noticed that if we have controllers in external assemblies, the routes are not resolved properly. I found this post:
https://www.strathweb.com/2012/06/using-controllers-from-an-external-assembly-in-asp-net-web-api/
and when I apply the proposed changes, the routes dont use the "route" attribute. I.E. if I have the following derived class:
[RoutePrefix("api/thebooks")]
public class BookController : EntityController<Book>
{
public BookController() : base()
{
}
}
The controller prefix is api/book instead api/thebooks and if I try to call "Add" I have the exception:
Multiple actions were found that match the request:
Add on type [...]
Edit on type [...]
Find on type [...]
How can I indicate that I will use the Route and RoutePrefix attribute on inherited Controllers?
Thanks in advance!!!
Both Route and RoutePrefix are not inherited because Inherited is set to false as shown below:
[Inherited=false)]
public class RoutePrefixAttribute : Attribute
You can read more about Inherited here.
You will need to decorate your subclasses again unfortunately.
It is possible to override the inherited false of the Route and RoutePrefix with a bit of jiggery pokery.
Firstly inherit from DefaultDirectRouteProvider - notice the inherit true inside GetCustomAttributes
public class InheritanceDirectRouteProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList<IDirectRouteFactory> GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
{
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>(true);
}
}
Then in you application startup class (WebApiConfig or Startup) register it against your HttpConfiguration as an extension, then change the line in the startup from config.MapHttpAttributeRoutes(); to config.MapInheritedAttributeRoutes();
public static class HttpConfigurationExtensions
{
public static void MapInheritedAttributeRoutes(this HttpConfiguration config)
{
config.MapHttpAttributeRoutes(new InheritanceDirectRouteProvider());
}
}
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
config.MapInheritedAttributeRoutes();
app.UseWebApi(config);
}
}
Taken from this article https://www.strathweb.com/2016/06/inheriting-route-attributes-in-asp-net-web-api/
Was coaching a colleague on Web API. Just trying to run the default Web API template out of the Visual Studio box, as it were...not even doing anything fancy. A default (Get) call to my API (api/{controller}/{id}) keeps returning the MVC home view instead of the contents of the Get method in my API controller.
namespace WebAPI.Controllers
{
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
...
namespace WebAPI
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
namespace WebAPI
{
public class WebApiApplication : 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);
}
}
}
I've looked far and wide across the 'Net but can't find anything address my specific problem.
I expected the default value1, value2, etc. Instead, the MVC home view keeps returning instead.
I am using VS 2017 community. I have been building web api s for years. But something must have changed as I cannot get the simplest example to work.
I have a simple controller in the controller folder
public class TestApi : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
I have the necessary code in application start:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
But when I try and test the web api with a get like:
http://localhost:54014/api/testapi
I always get an xml message
Error
Message
No HTTP resource was found that matches the request URI
'http://localhost:54014/api/testapi'.
/Message
MessageDetail
No type was found that matches the controller named 'testapi'.
/MessageDetail
/Error
Here is the WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
I am a couple of hours into head scratching on this. As I say I have built many MS web api implementations and this one has me baffled.
You should add Controller suffix to your class name.
public class TestApiController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
When the app starts, the asp.net mvc frameworks looks for classes (which are inherited from ApiController or Controller) with this suffix and register them when building the route table. When a request comes to your app, the framework again look into the route table and direct the request to the corresponding controller and action method.
Make this change, rebuild your project and try again.
In addition to the already provided answer (which is correct by the way) you can also consider using attribute routing over convention-based routing, where in this case it would not matter what the name of the controller is.
You already have it enabled Based on the WebApiConfig.cs and
config.MapHttpAttributeRoutes();
So now it is just a matter of putting the attributes where needed
[RoutePrefix("api/testapi")]
public class TestApi : ApiController {
[HttpGet]
[Route("")] //Matches GET api/testapi
public IEnumerable<string> Get() {
return new string[] { "value1", "value2" };
}
}
Reference: Attribute Routing in ASP.NET Web API 2
I have applied attribute routing on my controller and it'srouting to wrong action. I don't know where I am getting it wrong.
Here is my controller:
using System.Collections.Generic;
using System.Web.Http;
using System.Web.Http.Description;
using System.Linq;
using System;
namespace Iboo.API.Controllers
{
public class ClientsController : ApiController
{
private readonly IClientRepository _repository;
public ClientsController(IClientRepository repository)
{
_repository = repository;
}
// GET: api/Clients
[Route("api/v1/clients")]
public IEnumerable<Client> Get()
{
//code
}
// GET: api/Clients/5
[HttpGet]
[ResponseType(typeof(Client))]
[Route("api/v1/clients/get/{id}")]
public IHttpActionResult GetClientById(int id)
{
//code
}
// GET: api/Clients/5
[HttpGet]
[ResponseType(typeof(string))]
[Route("api/v1/clients/{id}/emailid")]
public IHttpActionResult GetClientEmailId(int id)
{
//code
}
}
}
I am specifically interested in the GetClientEmailId method. Below is my WebApiConfig
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var container = new UnityContainer();
container.RegisterType<IClientRepository, ClientRepository>(new
HierarchicalLifetimeManager());
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/v1/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
My Global.asax.cs is as follows
public class WebApiApplication : 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);
}
}
In the browser If I type http://localhost:54919/api/v1/clients/?id=1/getemailid it's taking me to http://localhost:54919/api/v1/clients which is not what I want.
If I try http://localhost:54919/api/v1/clients/1/getemailid I am getting a 404 error.
I am not sure as to what I'm getting wrong.
You are calling the wrong URLs according to routes on the actions. you get 404 because the URL you call does not match to any of the route templates you have on your actions
[RoutePrefix("api/v1/clients")]
public class ClientsController : ApiController {
//...other code removed for brevity
[HttpGet]
[Route("")] //Matches GET api/v1/Clients
public IHttpActionResult Get() {
//code
}
[HttpGet]
[ResponseType(typeof(Client))]
[Route("{id:int}")] //Matches GET api/v1/Clients/5
public IHttpActionResult GetClientById(int id) {
//code
}
[HttpGet]
[ResponseType(typeof(string))]
[Route("{id:int}/emailid")] //Matches GET api/v1/Clients/5/emailid
public IHttpActionResult GetClientEmailId(int id) {
//code
}
}
Take note of the expected URLs in the comments
You should also read up on Attribute Routing in ASP.NET Web API 2 to get a better understanding of how to do attribute-routing.
You can try using the route prefix on the controller.
[RoutePrefix("api/v1/clients")]
public class ClientsController : ApiController
{
// GET: api/Clients/5
[ResponseType(typeof(string))]
[Route("{id:int}/emailid"),HttpGet]
public IHttpActionResult GetClientEmailId(int id)
{
//code
}
}
You said:
In the browser If I type http://localhost:54919/api/v1/clients/?id=1/getemailid it's taking me to http://localhost:54919/api/v1/clients which is not what I want.
From the way your routes are set up, it looks like you need to go to http://localhost:54919/api/v1/client/1/emailid to get to the route you want
To explain the difference, when you call http://localhost:54919/api/v1/clients/?id=1/getemailid the route that would match that is something like:
[Route("api/v1/clients")]
public IHttpActionResult GetClientEmailId(string id)
{
//code
}
because you've added the id parameter as a querystring parameter. In this case, the id argument would have a value of 1/getemailid which doesn't make much sense.
by using the route parameters (by replacing ?id=1/getemailid with 1/emailid) you will actually match the route you want to