How to map different actions to Web API methods that would have same signature - rest

I am trying to create a REST service using Web Api best practices and I came to a situation I don't know how to handle.
Imagine I have groups and inside of each group I have users.
This is how my GroupController looks like:
[RoutePrefix("api/v1/groups")]
public class GroupsController : ApiController
{
[HttpGet]
[Route("{id:int}")]
public IHttpActionResult Get(int id)
{
return Ok();
}
// I also want to add endpoint which fetches all the users inside of group and I'd like to do it like this:
[HttpGet]
[Route("{id:int}/users")]
public IHttpActionResult Get(int id)
{
return Ok();
}
}
As you can see, my second method won't compile since we can not have two methods with the same signature in the same class.
My route template looks like the standart one:
config.Routes.MapHttpRoute(
name: "DefaultRouting",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I know that I can add a route which maps also action method name but I was wondering if there's a better way for achieving my goal. I'd like to stick to the best principles for writing RESTful services. As I know, one of the suggestions is to avoid custom action names since this is more common for WCF services.

Don't mix MapHttpRoute() style routing configuration and attribute-based routing. It's simpler to use one or the other (I prefer attribute-based as it's explicit and allows for clearer class method names).
You also seem to not be calling the method necessary to enable attribute-based routing on your config - config.MapHttpAttributeRoutes().
Finally, I'd rename your methods as follows to ensure there is no clash in the method signatures: GetGroups(int id)and GetUsersForGroup(int id)

Related

In a REST API client, do you need multiple GET methods for every endpoint?

I have been trying to learn a bit about api clients.
You have an api at www.expamle.com/api and you need to be able to GET all users at www.expamle.com/api/users and also get them by id at www.example.com/api/users/{id} .
In your code do you need to have a separate function to get ALL records and only one record?
What I don't get is how to properly serialize the results since when you get multiple records it returns you an array but it doesn't work with a single record.
Also, would you need more GET functions for other endpoints such as http://www.example.com/api/products/
Yes,you typically implement the two separate calls with two methods like this (C#):
[Route("users")]
public class UsersController : Controller
{
// users resource: /api/users/
[Route("")]
[HttpGet]
public IActionResult GetUsers() { ... }
// single user resource: /api/users/5
[Route("{id:long}")]
[HttpGet]
public IActionResult GetUser(long id) { ... }
}
This way you can explicitly define your two different routes. You you will find it also easier to deal with the different requests: less code, simpler to read etc. Avoid code redundancy though.
The serialization depends on what you require. There are multiple options. I personally stick to JSON these days. As a human I can read the data easily, yet the package sizes are fairly small compared to SOAP or any custom XML. Dealing with JSON is simple, especially in ASP.NET Core. You just return an IEnumerable<...> and the framework takes care of everything else:
[Route("")]
[HttpGet]
public IActionResult GetUsers()
{
return Ok(
new List<Users>
{
new User(1, ...),
new User(2, ...),
new User(3, ...)
});
}
For other resources, e. g. your products resource, use other controllers. I personally follow the RESTful guidance: Wikipedia and Microsoft Docs.

ASP.NET Web Api duplicate verbs

I have created a web api (REST) called Filter that has multiple get methods such as
GetCompany
GetCustomers
GetOrders
Is this correct practise or should I have different web api for different entities? Should I have same http verb (GET) duplicated in the same WEB API.
What about other verbs (POST or PUT)?
In another service we have one case where we want to update a specific field and another case where we can update anything except that specific field in the record. Should one method (POST or PUT) be used for both cases or can I have two separate methods?
I am calling these methods from angularjs $http service.
You should have a different controller for each resource (entity)
Then a Get method on your CustomersController for example
Your urls would then be
/Company
/Customers
/Orders
etc...
Your HTTP verbs are then routed to the corresponding methods in those controllers. So, GET request to /Customers would be routed to your Get() method on that controller
Alternatively, if you really insist on one controller, you could use Attribute Routing, along with verb attributes
Something like
public class FilterController : ApiController
{
[HttpGet]
[Route("orders")]
public IHttpActionResult GetOrders()
{ }
[HttpGet]
[Route("customers")]
public IHttpActionResult GetCustomers()
{ }
[HttpPut]
[Route("customers")]
public IHttpActionResult UpdateOrders()
{ }
}
But this will get pretty big, pretty quick, and I don't generally suggest doing it like this.
One controller per resource is much cleaner.

Workflow: Creating Dependency Chain with Service Locator Pattern

I'm trying to get dependencies set up correctly in my Workflow application. It seems the best way to do this is using the Service Locator pattern that is provided by Workflow's WorkflowExtensions.
My workflow uses two repositories: IAssetRepository and ISenderRepository. Both have implementations using Entity Framework: EFAssetRepository, and EFSenderRepository, but I'd like both to use the same DbContext.
I'm having trouble getting both to use the same DbContext. I'm used to using IoC for dependency injection, so I thought I'd have to inject the DbContext into the EF repositories via their constructor, but this seems like it would be mixing the service locator and IoC pattern, and I couldn't find an easy way to achieve it, so I don't think this is the way forward.
I guess I need to chain the service locator calls? So that the constructor of my EF repositories do something like this:
public class EFAssetRepository
{
private MyEntities entities;
public EFAssetRepository()
{
this.entities = ActivityContext.GetExtension<MyEntities>();
}
}
Obviously the above won't work because the reference to ActivityContext is made up.
How can I achieve some form of dependency chain using the service locator pattern provided for WF?
Thanks,
Nick
EDIT
I've posted a workaround for my issue below, but I'm still not happy with it. I want the code activity to be able to call metadata.Require<>(), because it should be ignorant of how extensions are loaded, it should just expect that they are. As it is, my metadata.Require<> call will stop the workflow because the extension appears to not be loaded.
It seems one way to do this is by implementing IWorkflowInstanceExtension on an extension class, to turn it into a sort of composite extension. Using this method, I can solve my problem thus:
public class UnitOfWorkExtension : IWorkflowInstanceExtension, IUnitOfWork
{
private MyEntities entities = new MyEntities();
IEnumerable<object> IWorkflowInstanceExtension.GetAdditionalExtensions()
{
return new object[] { new EFAssetRepository(this.entities), new EFSenderRepository(this.entities) };
}
void IWorkflowInstanceExtension.SetInstance(WorkflowInstanceProxy instance) { }
public void SaveChanges()
{
this.entities.SaveChanges();
}
}
The biggest downside to doing it this way is that you can't call metadata.RequireExtension<IAssetRepository>() or metadata.RequireExtension<ISenderRepository>() in the CacheMetadata method of a CodeActivity, which is common practice. Instead, you must call metadata.RequireExtension<IUnitOfWork>(), but it is still fine to do context.GetExtension<IAssetRepository>() in the Execute() method of the CodeActivity. I imagine this is because the CacheMetadata method is called before any workflow instances are created, and if no workflow instances are created, the extension factory won't have been called, and therefore the additional extensions won't have been loaded into the WorkflowInstanceExtensionManager, so essentially, it won't know about the additional extensions until a workflow instance is created.

How to Route non-CRUD actions in a RESTful ASP.NET Web API?

I am trying to design a RESTful web API for our service using ASP.NET Web API. I'm running into trouble with figuring out how to route non-CRUD actions to the proper controller action. Let's assume my resource is a door. I can do all of the familiar CRUD things with my door. Let's say that model for my door is:
public class Door
{
public long Id { get; set; }
public string InsideRoomName { get; set; }
public string OutsideRoomName { get; set; }
}
I can do all of my standard CRUD operations via my web api:
POST: http://api.contoso.com/v1/doors
GET: http://api.contoso.com/v1/doors
GET: http://api.contoso.com/v1/doors/1234
GET: http://api.contoso.com/v1/doors?InsideRoomName=Cafeteria
PUT: http://api.contoso.com/v1/doors/1234
DELETE: http://api.contoso.com/v1/doors/1234
and so on. Where I run into trouble is when I need to model the non-CRUD actions against my door. I want to model a Lock and Unlock verb against my resource. Reading through the ASP.NET articles the guidance seems to be to switch to an RPC style call when using custom actions. This gives me a path:
PUT: http://api.contoso.com/v1/doors/1234/lock
PUT: http://api.contoso.com/v1/doors/1234/unlock
This seems to conflict with the spirit of REST which aims for the path to indicate a resource. I suppose I could model the verb as a resource:
POST: http://api.contoso.com/v1/doors/1234/lockrequests
POST: http://api.contoso.com/v1/doors/1234/unlockrequests
In this case I could still use the recommend {controller}/{id}/{action} but it seems like I'm still creating a mixed RPC / REST API. Is it possible, or even recommended as far as REST interfaces go, to put the custom action in the list of parameters?
PUT: http://api.contoso.com/v1/doors/1234?lock
PUT: http://api.contoso.com/v1/doors/1234?unlock
I could foresee a need to have this call supported with query parameters as well, such as:
PUT: http://api.contoso.com/v1/doors?lock&InsideRoomName=Cafeteria
How would I create the route to map this request to my DoorsController?
public class DoorsController : ApiController
{
public IEnumerable<Doord> Get();
public Door Get(long id);
public void Put(long id, Door door);
public void Post(Door door);
public void Delete(long id);
public void Lock(long id);
public void Unlock(long id);
public void Lock(string InsideRoomName);
}
I may be making some false assumptions here regarding what is and is not best practices with respect to REST API design, so any guidance there is appreciated as well.
From RESTful principle, maybe it's best to introduce a 'status' property to manage those non-CURD actions. But I don't think it meets the real production development.
Every answer to this kind of question, looks like you must have to use a work-around to enforce your API design meets RESTful. But my concern is, is that really making convenience to both user and developer?
let's take a look on the API3.0 design of Google bloger: https://developers.google.com/blogger/docs/3.0/reference, it's using lot URL for non-CURD actions.
And this is interesting,
POST /blogs/blogId/posts/postId/comments/commentId/spam
and the description is
Marks a comment as spam. This will set the status of the comment to spam, and hide it in the default comment rendering.
You can see, a comment has a status to indicate whether it's a spam or not, but it was not designed like the answer mentioned above by JoannaTurban.
I think from user point of view, it's more convenient. Don't need to care the structure and the enum value of the "status". And actually you can put lot of attributes into the definition of "status", like "isItSpam", "isItReplied", "isItPublic" etc. The design will becomes unfriendly if the status has many things.
On some business logic requirement, to use an easy to understand verb, instead of trying to make it completely a "real" RESTful, it's more productive, for both user and developer. This is my opinion.
To handle the lock/unlock scenario you could consider adding a State property to the Door object:
public State State { get; set; }
where State is an enum of available values, e.g.
{
LockedFromOutsideRoom,
LockedFromInsideRoom,
Open
}
To clarify: That you're adding a state to the object is not against restful principles as the state is passed over the api every time you make a call to do something with the Door.
Then via the api you would send a PUT/POST request to change the state of the Door on each lock/unlock. Post would probably be better as it's only one property that gets updated:
POST: http://api.contoso.com/v1/doors/1234/state
body: {"State":"LockedFromInsideRoom"}
From a REST perspective you probably want to be treating the lock as a resource in and of itself. This way you create and delete the lock independently of the door (although presumably locate the lock endpoint from the door representation). The URL of the resource is probably going to be related to the URL of the door, however from a RESTful perspective this is irrelevant. REST is about relationships between resources, so the important part is that the url of the lock is discoverable from the representation of the door.

REST route for alternative Get query type of calls

I am using .NET Web API and need a strategy for route creation of queries. For example, say from the page the code is at a decision point and needs direction from business logic on the server about how to proceed.
Just for review this route will return a standard user...
http://mysite/api/user/5
However, if I want to know if the user has some characteristic determined by business logic which I would make a UI decision on, what would a good REST call be for something like this?
Maybe...
http://mysite/api/user/5?canbake=true
Or is this better...
http://mysite/api/user/5/canbake
And if the latter, then what does the route definition look like to make that happen?
I believe the general consensus is that the latter is better.
That said, the route definition would look something like:
public static void RegisterApiRoutes(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name:"UserApi",
routeTemplate: "api/{controller}/{id}/{bake}",
defaults: new
{
bake = RouteParameter.Optional
});
}