Different Web API 2 controllers when using attribute routing - c#

I'm building a prototype for a RESTful API using ASP.NET Web API 2. Fo simplictiy let's assume I have three entities: customers, licences and users. Each customer has a set of licences and users. Semantically it seems to me the resource URIs should look like this:
myurl/api/customers for accessing all customers
myurl/api/customers/{cid} for accessing the customer {cid}
myurl/api/customers/{cid}/licences for accessing all licences of customer {cid}
myurl/api/customers/{cid}/licences/{lid} for accessing the licence {lid} of customer {cid}
The same goes for the users. The intended semantics allow for example two users to have the same id if they belong to separate customers. Apart from maybe the licences entities (decision not final yet) each customer will have a dedicated database, so there is no overlapping in this domain and resource paths like
myurl/api/users
make only sense in the way "join all user tables from all customers' databases.
Using attribute routing this setup is quite easily achieved. However, all methods have to be implemented in the same controller since methods from different controllers cannot share the same prefix AFAIK.
The actual application will contain many more entities than just three, so I expect the controller's implementation to get quite huge. My question now is, how can I split the method into different controllers? I thought about using one main controller which just dispatches the work to be done to another controller. For example
[Route("{customer:int}/licences/{licence:int}")]
public HttpResponseMessage GetLicence(int customer, int licence)
{
// pretend the called method is static
return LicenceController.GetLicence(customer, licence);
}
However, I do not know how to implement this properly: Should I create a new LicenceController for each call? Or have a property of this type and call it's method? Actually implement some static methods?
Another downside is that this introduces hard-coded dependencies between the selector and the implementing controller classes which I feel is not a clean solution.
I came up with a workaround which uses resource paths like this:
myurl/api/licences/customer-{cid} for accessing all licences of customer {cid}
myurl/api/licences/customer-{cid}/{lid} for accessing the licence {lid} of customer {cid}
This works quite well but messes up the homogeneous semantics IMO. I know I can write a custom selector class but that seems to be quite some work to get it right.
So my question is, what is the best (perhaps most efficient) way to split the code which deals with incoming HTTP messages into separate controllers so that there is loose coupling and the resource semantics are coherent?

You would have two controllers. One to return the customers and one to return the licences. For the Customer there is no need to use attributes as the defaults are fine:
public class CustomersController : ApiController
{
// GET: api/Customers
public IEnumerable<Customer> Get()
{
return new List<Customer>
{
new Customer { Id = 1, Name = "Wayne" },
new Customer { Id = 2, Name = "John" }
};
}
// GET: api/Customers/5
public Customer Get(int id)
{
return new Customer { Id = 1, Name = "Wayne" };
}
}
Then you can you RoutePrefix attribute on the controller to add for api/Customers/1/licences and the rest can be handled by Route on the actions. I named the Controller CustomerLicencesController as you probably want to have a Licences controller to fetch a particular licence or all licences such as api/licences or api/licences/1.
[RoutePrefix("api/customers/{customer}/licences")]
public class CustomerLicencesController : ApiController
{
// GET: api/Customers/1/licences
[Route("")]
public IEnumerable<Licence> Get(int customer)
{
return new List<Licence>
{
new Licence { Id = 1, Name = "Test" },
new Licence { Id = 2, Name = "Test2" }
};
}
// GET: api/Customers/1/licences/1
[Route("{id}")]
public Licence Get(int customer, int id)
{
return new Licence { Id = 1, Name = "Test" };
}
}
For more information about Route attributes take a look at this.

Related

How to convert an IHttpActionResult (with JSON inside) to an object I can process with LINQ

It might be a noob question or an architectural misunderstanding, but I ask it anyhow because I am out of ideas and search terms: The goal is to implement a controller CountriesController() which is supposed to concatenate the (JSONish) results of two endpoints.
Assume I have two endpoints api/allowedCountriesToSell and api/allowedCountriesToBuy which are implemented as CountriesSellController() and CountriesBuyController() respectively. Both of them give back data as JSON which I want to merge and offer as a new endpoint. I am aware that this architecture is not ideal, but I am not allowed to do it architecturally different. Furthermore, I actually have to POST two different files to those endpoints - both existing controllers contain something like
[HttpPost]
public ActionResult FileUpload(HttpPostedFileBase file, string selectBox)
{ // ...
My new endpoint compiles all these two required parameters, let's call them myFileX, and mySelectBox. Here what I have have so far:
var myOtherContoller1 = new CountriesSellController();
var list1 = myOtherContoller1.FileUpload(myFile1,mySelectBox);
var myOtherContoller2 = new CountriesSellController();
var list2 = myOtherContoller1.FileUpload(myFile2,mySelectBox);
my result = list1.asEnumerable().Concat(list2.asEnumerable()); // Pseudocode. Here I am lost.
return Ok(result);
The problem is that both list1 and list2 are of type IHttpActionResult and I am not sure how to extract the data inside that. Ideally, result would be of type IEnumerable<UploadStatusDto> where I define the respective data transfer object as
namespace API.Models
{
public class UploadStatusDto
{
public int UploadId { get; set; } // contained in the response of both controllers
public string FileName { get; set; } // myFileX - parameter for calling the 2 existing controllers
public int UploadStatus { get; set; } // coming back within listX
public int Type { get; set; } // whether it is a buy or a sell, i.e. which controller I called
}
Any guidance is appreciated.
You need to do something line this.
var response = await myOtherContoller1.FileUpload(myFile2,mySelectBox).ExecuteAsync();
This will return HttpResponseMessage and you can get the content from it
You can get your content like this: Getting content/message from HttpResponseMessage.
My suggestion though, would be to extract the logic of your other controllers to a service class, and call both in this and the other two, the logic that is now in the original controllers.

asp.net core WebAPI get subset of Generic Request Object

I am new into asp.net core webAPI.
I have created a Controller method that taken a request object.
[HttpPost]
[Route("/api/DoWork")]
[ValidateModelState]
public virtual IActionResult DoWork(CustomRequest request)
{
//TODO: implement this method
}
Now this CustomRequest calss is generic class, which have multiple properties and depending upon the client/tenant will populate/set values for some of the properties.
Example :
Suppose CustomRequest class structure is,
public partial class CustomRequest
{
public string ReqId { get; set; }
public DateTime? BusinessDate { get; set; }
public DateTime? CurrentDate{ get; set; }
}
So Customer A can only set values (or send values in request) ReqId and BusinessDate and Customer B send values ReqId and CurrentDate. As to proceed with business operation either of the date is required.
It's also previously decided which client will send which of the values, and this number of properties is large.
So I was thinking of creating a relatively small class which will be subset of CustomRequest class and sufficient enough to process business operations. And I can think of two options to proceed with that.
Write code inside my controller action to create small sub set class
and then call another method that expect only the subset class
Use Custom Model binders to create that small subset class and change the method signature.
My question is:
What is the best way to handle this situation option 1 or 2 or any other option? Best way in terms of performance and customization option available (like in option 1, i don't think we can use ModelState)
Any help or guidance...

How to support different clients with single WCF service

I need to return Employee class as a response to my clientA as follows.
[OperationContract]
public Employee GetEmployee(String id)
{
..
..
return emp;
}
public class Employee
{
public string Name;
public string phoneNo;
}
But the problem here is clientB is going to consume my service but the need a SSN of employee. If i add it into Employee class, I will be sending to clientA as well which wont need it. How to overcome this situation. If i have to do anything with custom deserialization, would not it be a problem if i about to handle 1000s of properties?
Which is the better solution? Any wcf architectural help would also be more helpful.
If different clients have different needs, the proper thing is to create different services as well.
You put the business logic in a shared business class (or distributed over multiple shared business classes), and expose a different service per different type of client. That keeps things nice, abstracted and secure, and nobody gets data they don't need or want.
There has been a quite similar discussion on this link. Basically, it refers to conditional hiding the value of a data member.
You could decide if you want to expose a data member based on the client id or credentials (which should be passed as a parameter to the service method call).
[OperationContract]
public Employee GetEmployee(int clientId, String id)
{
var employee = new Employee();
//here you might use a mapping table between the clients and the exposed data members
if (clientId == 1)
{
employee.IsSSNSerializable = true;
}
return employee;
}
The Employee class will expose the SSN based on the value of the IsSSNSerializable property:
[DataContract]
public class Employee
{
public bool IsSSNSerializable = false;
[DataMember]
public string Name;
[DataMember]
public string phoneNo;
public string SSN;
[DataMember(Name = "SSN", EmitDefaultValue = false)]
public string SSNSerializable
{
get
{
if (!IsSSNSerializable)
return null;
return SSN;
}
set
{
SSN = value;
}
}
}
I would suggest you take a read of the versioning strategies of the WCF that might be matches with your scenarios.
for my case, i implemented IExtensibleDataObject on the data contracts and manage in this layer instead of service contracts layer.
the downside would be difficulties to track different versions of contracts, however I'm practicing the semi-strict versioning and works well for me.
I second Roy, but however if this is the only difference between client A and B. It would not hurt to expose a GetEmployee method with parameter IsSSNRequired which can have a default false value.

Suggest REST/Service design for collection in 'DTO' response/request

Just learning REST and ServiceStack and asking for suggestion how to build this example schema:
I have a User object with those fields:
public class User
{
public string ID {get;set;
public string Name {get;set;}
public List<int> OrderID {get;set;} // inner collection
// all other fields
}
I have a Service class:
public class UserService : Service
{
public List<User> Get(User UserRequest)
{
List<User> lstUsers = // Somehow polulate a collection of Users
return lstUsers;
}
// Other functions
}
and a AppHost file (self hosted for now)
public UserHost : AppHostHttpListenerBase
{
public override void Configure(Funq.Container container)
{
Routes
.Add<User>("/UserInfo")
.Add<User>("/User/{ID}");
}
}
Everything working fine: the client (browser) send an empty 'User' request and receive collection of User objects, but what I need to add is to get specific User request. For example I want to receive (and change) all Orders with specific User ID.
For example User with ID = 2 has a collection of 10 OrderID. So I though I can build an URI like this: /MyHost/2/Orders and it will return collection of Order objects.
I already tried to add another DTO object Order and bind it to routes like this:
Route.Add<Order>("/User/{ID}/Oders")
but it does not work. The screen (at least in browser) always stay blank and it seems no response is coming from SS.
Is it a RESFull way of doing things ? How It could be implemented in ServiceStack ? and could you provide a little code example (even schematic) how to do that ?
If you want to return a list of orders for a given user, then binding to the URL "/User/{ID}/Orders" is the RESTful way, yes.
With ServiceStack I normally prefer to define request and response classes, where request class will have all the possible input parameters and response defines the resulting object. Though often these to can be combined like User in the example.
As {ID} in the URL refers to the user ID, it's better to name it {UserID} and also have this property on your request class.
Probably you can just have UserID on the Order class:
public class Order
{
public string ID { get; set; } // order ID
public string UserID { get; set; } // user ID - also request param
// ...
}
Routes.Add<Order>("/User/{UserID}/Oders");
Now, UserID from the URL is mapped to your UserID property and you can use it to query the db, etc.
Each request class normally corresponds to another service class with your app logic. Thus,
for User you have UserService : RestServiceBase<User>,
for Order there is OrderService : RestServiceBase<Order>.
When ServiceStack sees this URL mapping Routes.Add<Order>("/User/{UserID}/Oders") it knows to invoke OrderService for a given URL.
public class OrderService : RestServiceBase<Order>
{
public override object OnGet(Order request)
{
// ...
}
}

Custom security scenario in ASP.NET MVC

I don't have a lot of experience with this and I am really hoping to get a good suggestion from you guys. I need to implement the following security scenario and I would like to know the best way to do it.
Imagine we have Employees, Supervisors and Department managers.
Both Employees and Supervisors have ManagerId assigned based off and pointing to the department manager they belong to.
When a supervisor user logs in I want him to only see records for employees that belong to the same ManagerId as his.
If another supervisor with another ManagerId user logs in and manually punches other employee's information in url (ex: wwww.domain.com/employee/details/{id} ),
because his ManagerId != employee's ManagerId I would like the access to be restricted.
Does it make sense ?
I started typing out checks on all ActionMethods such as:
public ActionResult Details(int id)
{
var employee = employeeRepository.Get(id)
var user = (CustomIdentity)ControllerContext.HttpContext.User.Identity;
if(employee.managerId == user.managerId)
{
Do whatever...
}
else
{
Not allowed
}
}
But typing that out in all ActionMethods seems redundant and just..ehh... I know there must be a better way.
Here is a stab at a solution. It needs a bit of cleanup but should give you everything you need.
Create a custom ActionFilter, and then decorate your methods with it.
[ManagerIdAuthentication]
public ActionResult Details(int id)
{
// Gets executed if the filter allows it to go through.
}
The next class can be created in a separate library so you can include it in all your actions that require this validation.
public class ManagerIdAuthentication : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// the next line needs improvement, only works on an httpGet since retrieves
// the id from the url. Improve this line to obtain the id regardless of
// the method (GET, POST, etc.)
var id = filterContext.HttpContext.Request.QueryString["id"];
var employee = employeeRepository.Get(id);
var user = filterContext.HttpContext.User.Identity;
if (employee.managerId == user.managerId)
{
var res = filterContext.HttpContext.Response;
res.StatusCode = 402;
res.End();
filterContext.Result = new EmptyResult(); //may use content result if want to provide additional info in the error message.
}
else
{
// OK, let it through.
}
}
}
I had a similar issue in the past, what I would consider per-object permissions. What I did was add a member to the object similar to:
public bool CanUserAccess(User user) {
return managerId == user.managerId;
}
Then, at the top of each action providing access to a controlled resource:
public ActionResult Details(int id)
{
var employee = employeeRepository.Get(id)
var user = (CustomIdentity)ControllerContext.HttpContext.User.Identity;
if(!employee.CanUserAccess(user))
return new HttpUnauthorizedResult();
// Normal logic here
}
It's certainly not perfect, but it does centralize the permission handling and allows you to easily increase the complexity in the future (allow access up the chain, special rules for HR, etc.). You could also write another overload/extension to access the User.Identity property for a bit more automation (or at least handle the type conversions).
Since I was dealing with ACL's, I would have additional methods/parameters to specify the basic nature of the action (e.g. Read, Write, Delete, Create, etc.).

Categories

Resources