GET and PUT methods for same route don't work if in separate classes - asp.net-web-api-routing

We're using specific route attributes for all our web api routes.
We want to split up a controller class, so we're moving some update functions out to a separate controller class.
We've subsequently got an odd situation with routing.
The original controller class looked like this:
[RoutePrefix("activities")]
public class ActivitiesController : ApiController
{
...
[Route("{activityId:int:min(1)}"), HttpPut]
public void Put(int activityId, [FromBody] NewActivity value) {}
[Route("{activityId:int:min(1)}"), HttpGet]
public dynamic Get(int activityId) {}
...
}
And we then split out the updates, so we end up with this:
[RoutePrefix("activities")]
public class ActivitiesUpdateController : ApiController
{
...
[Route("{activityId:int:min(1)}"), HttpPut]
public void Put(int activityId, [FromBody] NewActivity value) {}
...
}
[RoutePrefix("activities")]
public class ActivitiesController : ApiController
{
...
[Route("{activityId:int:min(1)}"), HttpGet]
public dynamic Get(int activityId) {}
...
}
If we then try to call either endpoint:
- PUT activities/42
- GET activities/42
We get the error:
Multiple controller types were found that match the URL. This can happen if attribute routes on multiple controllers match the requested URL.
The request has found the following matching controller types:
API.Controllers.ActivitiesUpdateController
API.Controllers.ActivitiesController
If both endpoints are in the same class (either the new one or the old), everything works correctly.
Different classes raises the error.
If I dump out the endpoints using ApiExplorer, both end points are where they should be and are not repeated in the other class, so there's no obvious duplication.
I've tried renaming functions, explicit routes (i.e. ~/activities/:id), adding HttpGet & HttpPut doesn't seem to make a difference.
Is this some weird "feature" where the same endpoint with different methods have to be in the same class?
It makes no sense.

Not to worry, I came across this after I posted - don't know how I missed it before:
Multiple controllers with same URL routes but different HTTP methods

Related

Web Api 2 Inheritance No route providing a controller name was found to match request URI

Basically I cannot get my Web Api 2 application to work.
First of all here are my requirements.
In my application I am creating a dozen of controllers ( ProductController, ItemController, SalesController...etc). There are 2 actions which are absolutely common in all my controllers:
FetchData, PostData
(Each controller then may implement a number of other methods which are sepcific to its business domain )
Instead of repeating these actions in every controllers like:
public class ProductController:ApiController{
[HttpPost]
public MyReturnJson FetchData( MyJsonInput Input){
....
return myJsonResult;
}
}
public class SalesController:ApiController{
[HttpPost]
public MyReturnJson FetchData( MyJsonInput Input){
....
return myJsonResult;
}
}
I decided to create a base controller MyBaseController:
public class MyBaseController : ApiController{
[HttpPost]
public MyReturnJson FetchData( MyJsonInput Input){
....
return myJsonResult;
}
}
with the 2 methods so every other controller would inherit them (It saves me from repeating them in every controller). The common base class has been defined and implemented in a separate assembly which is then referenced in my web project.
Then in my javascript client (using breeze) I call a specific controller like
breeze.EntityQuery.from('FetchData')
where my serviceName is 'my_api/product/'
(in the WebApiConfig, the routing table has been defined like:
config.Routes.MapHttpRoute(
name: "my_api",
routeTemplate: "my_api/{controller}/{action}"
);
But when the javascript code is executed I get the error message:
No route providing a controller name was found to match request URI
http://localhost:xxxxx/my_api/product/FetchData
If I don't use a common base class but instead repeat this method (FetchData) in every class (basically ProductController inherits directly from ApiController and not from MyBaseController) every thing works fine and my method is hit. I thing there is a problem with the inheritance scheme. Maybe there is something I don't get (first time using Web Api 2) or some constraints (routing, configuration...) I do not respect. Right now I am stuck and I would appreciate any suggestion which might point me to the right direction. Is inheritance allowed in Web Api 2?
I am not sure why your code is not working. But in the next link (http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-22#ARI) you can see an example of inheritance using attribute routing.
This is the code example:
public class BaseController : ApiController
{
[Route("{id:int}")]
public string Get(int id)
{
return "Success:" + id;
}
}
[RoutePrefix("api/values")]
public class ValuesController : BaseController
{
}
config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());
public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList<IDirectRouteFactory>
GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
{
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>
(inherit: true);
}
}
I hope that it helps.

How to add multiple Get Post and Delete methods in RESTful Service

I am new to REST Services and would like to know how we can add multiple Get / Post / Delete methods.
for e.g.
We are having following Get Methods: -
GetAllUsers()
GetUserByID(int id)
GetUserByName(string name)
Similarly, Delete methods: -
DeleteAllUsers()
DeleteUserByID(int id)
DeleteUserByName(string name)
Post/Put Methods: -
PutCreateDefaultUser()
PutCreateUser(User user)
PutCreateMultipleUsers(User[] users)
So how to define Get/Delete/Post/Put methods in above case. Is that name it self says which is get / delete /put / post
Also How to set the uri template for each?
What will be the URI of each method?
Note: I am using MVC4 .Net Web API project, I am NOT using WCF
Your examples point out to more of an RPC implementation. REST is based on resources. Each resource has its methods. to Get, Update, Insert and Delete. If you are planning to have what you said in your question, you can do it in your ASP.NET API with no problem: (But be sure that this is NOT REST)
Update (2018)
After some time and experience (and after a user comment on this old answer) I realized it was wrong to say that OP endpoints were not Restfull. The routes can be easily done to achieve that, as my examples already have shown. Funny how we learn and change our own ideas/opinions with time. :)
UserController
[RoutePrefix("api/v1")]
public class UserController : ApiController
{
[HttpGet]
[Route("users")]
public HttpResponseMessage GetAllUsers()
{
...
}
[HttpGet]
[Route("users/{id:int}")]
public HttpResponseMessage GetUserByID(int id)
{
...
}
[HttpGet]
[Route("users/{name:string}")]
public HttpResponseMessage GetUserByName(string name)
{
...
}
[HttpDelete]
public HttpResponseMessage DeleteAllUsers()
{
...
}
[HttpDelete]
[Route("users/{id:int}")]
public HttpResponseMessage DeleteUserByID(int id)
{
...
}
}
With the HttpAttributes, you can have as many HttpDeletes you want. Just put the attribute on top of the action and you're good to go. It also enforces that the methods can only be called using that HTTP verb. So in the Delete above, if you do a call with a GET verb, you'll get nothing. (The action will not be found)
You can also explicitly give a custom route to your action if you so desire. For instance, your call to GetUserByID would be:
GET: http://localhost:2020/api/v1/users/1
Most of the information you require can be found here:
http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api
http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-and-action-selection
You can specify the HTTP method with an attribute: AcceptVerbs, HttpDelete, HttpGet, HttpHead, HttpOptions, HttpPatch, HttpPost, HttpPut.
Otherwise, if the name of the controller method starts with "Get", "Post", "Put", "Delete", "Head", "Options", or "Patch", then by convention the action supports that HTTP method.
If none of the above, the method supports POST.
The Uri will depend on the name of the controller:
/api/controller-name/GetAllUsers

JAX-RS #PathParam to inject in class member variable?

I want to do something like this:
#Stateless
#Path("/sensors/{sensorid}/version")
#Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML})
#Produces({MediaType.APPLICATION_XML, MediaType.TEXT_XML})
public class SensorVersionRestView extends VersionRestView{
#PathParam("sensorid")
private String sensorid;
#GET
#Path("count")
// so the complete path is i.e.
// domain.com/rs/sensors/111211/version/count
public void getCount() {
// do something with the sensorId....
}
}
But the only thing I get is null on runtime (I use Glassfish v3 with Jersey). The compiler and eclipse never mentions a problem with the #PathParam at the member class variable.
What's wrong with my construct?
The main problem is, why I doesn't want to use the whole path on each method in this class, that there exists another class which handles some rest operations on the sensor layer (deomain.com/rs/sensors/count i.e.)
I believe you need to change it to this:
#Stateless
#Path("/sensors/{sensorid}/version")
public class SensorVersionRestView extends VersionRestView {
#GET
#Path("count")
#Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML})
#Produces({MediaType.APPLICATION_XML, MediaType.TEXT_XML})
// domain.com/rs/sensors/111211/version/count
public void getCount(#PathParam("sensorid") String sensorid) {
// do something with the sensorId....
}
}
Because injection occurs at object
creation time, use of this annotation
on resource class fields and bean
properties is only supported for the
default per-request resource class
lifecycle. Resource classes using
other lifecycles should only use this
annotation on resource method
parameters. - JSR-311 Javadocs
You should be able to annotate fields with #PathParam as long as the resource class lifecyle is per-request. By default the life-cycle of root resource classes is per-request.
EDIT: I don't think you can achieve this using EJBs. If you remove the #Stateless annotation, it should work.

In asp.net mvc 2 : how to access http post data inside constructor of any controller

My controller has abstract base controller. I want to access the form post data inside abstract base class constructor. How can we do that ?
public abstract class AppController : Controller
{
public AppController()
{
// request post data required here
}
}
public class ProductController : AppController
{
public ProductController() { }
}
Purpose : Updating second dropdown on change of first dropdown. Both are on MASTER page.
Code given above is one of the 2 options to pass data to master page:
Add using ViewData in ALL the action methods.
Do it in only one place using abstract base controller - add the required data using ViewData inside its constructor and make our main controller class implement this abstract base controller class. So that we don't have to add the viewdata for master page in all action methods.
I don't know what is your final goal with this but this is something which is not recommended to be done in MVC. The Request object is not yet initialized in the constructor of the controller. You could try to use the native HttpContext object:
string foo = System.Web.HttpContext.Current.Request["foo"];
but that's something extremely bad and I would never recommend you doing this as now your controller is coupled to the static native HttpContext instance without any chance of unit testing it.
Instead of using the constructor you could override the Initialize method of your controller where you will have access to the request context and you could read posted data:
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
string foo = requestContext.HttpContext.Request["foo"];
}

Custom Logic and Proxy Classes in ADO.NET Data Services

I've just read "Injecting Custom Logic in ADO.NET Data Services" and my next question is, How do you get your [WebGet] method to show up in the client-side proxy classes? Sure, I can call this directly (RESTfully) with, say, WebClient but I thought the strong typing features in ADO.NET Data Services would "hide" this from me auto-magically.
So here we have:
public class MyService : DataService<MyDataSource>
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(IDataServiceConfiguration config)
{
config.SetEntitySetAccessRule("Customers", EntitySetRights.AllRead);
config.SetServiceOperationAccessRule("CustomersInCity", ServiceOperationRights.All);
}
[WebGet]
public IQueryable<MyDataSource.Customers> CustomersInCity(string city)
{
return from c in this.CurrentDataSource.Customers
where c.City == city
select c;
}
}
How can I get CustomersInCity() to show up in my client-side class defintions?
When you see your Odata in browser, you will see link ...
e.g. http://localhost:1234/odataService.svc
just write your method name after the link
for your method it will be something like this...
http://localhost:1234/odataService.svc/CustomersInCity?city="London"