Merge routes in servicestack - c#

I have a Dto like this:
[Route("/clients/", HttpMethods.Post)]
[Route("/clients/{Dummy}", HttpMethods.Post)]
public class ClientPostDto : IReturnVoid
{
public string Dummy { get; set; }
}
And the post method in my service:
public class ClientService : Service
{
public void Post(ClientPostDto request)
{
// do some stuff
}
}
Is it possible to merge these two routings?
So the following two POST request are handled by the same Method in my Service:
localhost:12345/clients/
localhost:12345/clients/CLIENT_IDENTIFIER
I won't need the Dummy property at all, because the clients are stored in a list with the full path.

You can use the magic {ignore} literal instead, i.e. if you want to specify a wildcard without needing to specify an existing property, e.g:
[Route("/clients/{ignore}", "POST")]
public class ClientPostDto : IReturnVoid {}

That should just work. But you should keep the dummy variable so you know what has been requested perhaps retitled to ClientIdentifier.

Related

Web API dynamic method parameter - Resolve T for generic from dynamic parameter

We are wrapping a Viewmodel inside a ApiRequestModel and pass it as a parameter to the DoAuditModel web Api method.DoAuditModel calls the DoAudit generic method.
Since there are multiple Viewmodels we had to create a ApiRequestModel for each Viewmodel type.
But we don't want to create a Api method/endpoint for each model.
We want to create a single Api method that can do this DoAuditModel task.
Below is our simplified model structure for API Method call.
It has some common data like Token AppCode and a Model (ViewModel).
These models (ModelA,ModelB etc) don't have a base class or implements an interface.
// Api Request Base
public class ApiRequest : IApiRequest
{
public string Token { get; set; }
public string AppCode { get; set; }
...
}
// Api Request For ModelA
public class ApiRequestForModelA : ApiRequest
{
public ModelA MyModel { get; set; }
...
}
// Api Request For ModelB
public class ApiRequestForModelB : ApiRequest
{
public ModelB MyModel { get; set; }
...
}
API Controller has multiple methods for each model type that calls a generic method.
public class MyAuditController : ApiController
{
[HttpPost]
public Task DoAuditModelA(ApiRequestForModelA modelReq)
{
// Generic Method
DoAudit<ApiRequestForModelA>(modelReq.MyModel);
...
}
[HttpPost]
public Task DoAuditModelB(ApiRequestForModelB modelReq)
{
// Generic Method
DoAudit<ApiRequestForModelB>(modelReq.MyModel);
...
}
}
I want to avoid this web method duplication (DoAuditModelA) and creating ApiRequest models (ApiRequestForModelA) for each model type since all I want is to call that generic DoAudit<T>(T model){...}
I want to create a single Api method as AuditModel.
So I created a generic Api Request as below.
// Generic API Request
public class ApiRequest<T> : ApiRequest
{
public ApiRequest(T model)
{
this.MyModel = model;
}
public T MyModel { get; set; }
...
}
Now the problem is that Web Api method doesn't know the model type and how to bind/deserialize data.
I ended up using ApiRequest<dynamic>.
Below is my new Api Method.
[HttpPost]
public Task DoAuditModel(ApiRequest<dynamic> modelReq)
{
var myModelObj = modelReq.GetType().GetProperty("MyModel").GetValue(modelReq);
// Get model Type
var typeData = auditReq.ModelAssemblyQualifiedName;
Type t = Type.GetType(typeData);
// how to use this t to create model instance or convert/deserialize data
var myModel = //myModelObj should cast to the original model type.(I'm stuck here)
DoAudit(myModel);
...
}
// DoAudit Generic method
private void DoAudit<T>(T myModel)
{
...
}
So this is my approach to pass these ViewModels to DoAudit generic method in the web api and avoid multiple web api endpoints. I'm also concern about what type of overhead that Api will have to handle is this approach. Generic controller will not work for me here.
Summery
I want to create a single API Endpint for DoAuditModel task which calls DoAudit generic method

Calling the controller method depending on the incoming parameters

I want to implement a certain functionality, but I do not know where to start. I will describe what I have.
Backend
public enum SourceType { Database, Folder }
public class DatabaseSource
{
public string ServerName { get; set; }
public string DatabaseName { get; set; }
}
public class FolderSource
{
public string FolderName { get; set; }
}
public class TestController : ApiController
{
[HttpPost]
[Route("source")]
public void Post([FromBody]DatabaseSource source) //method one
{
}
[HttpPost]
[Route("source")]
public void Post([FromBody]FolderSource source) //method two
{
}
}
Frontend
export enum SourceType {
Database,
Folder
}
export class DatabaseSource {
public ServerName: string;
public DatabaseName: string;
}
export class FolderSource {
public FolderName: string;
}
var source = new DatabaseSource();
source.ServerName = "serverName";
source.DatabaseName = "dbName";
var obj = {
sourceType: SourceType.Database,
source: source
};
Now imagine that I will send obj to the server. I want that specific controller method to be called depending on the enum. How can I do this?
P.S. The example is greatly simplified.
Your implementation is inconsistent for what you've specified in code.
On the front-end you are describing an object which has a sourceType field and a source object property, while on the backend you're overloading the ApiController method and mapping different REST object resources to a single HTTP method and endpoint (which I believe will not work).
There is no magic way for the ApiController to use your enum property to differentiate between the object types automatically.
A simpler (and better) implementation would be to have separate ApiController classes for your Database and Folder source object POST calls. This follows the principle of REST API design where you are essentially mapping basic CRUD operations to the HTTP methods with object types.
If your intention is to perform an operation based on these parameter objects, then clarify the intention via the API routing for the endpoint as below:
public class TestController : ApiController
{
[HttpPost]
[Route("ETLLoad/Database/source")]
public void Post([FromBody]DatabaseSource source) //method one
{
}
[HttpPost]
[Route("ETLLoad/Folder/source")]
public void Post([FromBody]FolderSource source) //method two
{
}
}

Making a generic query / getbyspecification in web api controller?

I have a typical API with some CRUD operations. I typically need to get certain objects, based on different parameters.
One way to do it would be to have methods like:
GetProjectsByCustomerId(int customerId);
GetProjectsBySigneeId(int signeeId);
However, in my service layer (ProjectService in this case) I usually use a method such as the following where ProjectSpecification typically has quite a lot of fields and even lists:
public IEnumerable<Project> GetBySpecification(ProjectSpecification projectSpecification)
That means, in my dream world I would like to have endpoints such as:
/api/projects (empty specification, return full list)
/api/projects?customerid=2 (gets projects for customer with id 2)
/api/projects?signeeid=2,3 (get projects with signee id 2 and 3)
My question is - how is this done
My first attempt was adding this in my ProjectController (calling my ProjectService):
public class ProjectsController : ApiController
{
public IEnumerable<Project> GetProjects(ProjectSpecification projectSpecification)
{
var projects = _projectService.GetBySpecification(projectSpecification);
return projects;
}
}
But lets say I open this URL:
/api/Projects?CustomerId=2
This is not parsed into a ProjectSpecification viewmodel. However, if I change my controller signature to:
public IEnumerable<Project> GetProjects(int customerid) { }
It would work, because it's a simple type.
I could of course build some parameter-hell, but I guess there is something super obvious MVC magic I am missing - probably in the routing? :-)
Referencing documentation
Parameter Binding in ASP.NET Web API : [FromUri]
To force Web API to read a complex type from the URI, add the
[FromUri] attribute to the parameter.
For example assuming
public class ProjectSpecification {
public int CustomerId { get; set; }
//...other properties
}
public class ProjectsController : ApiController {
[HttpGet]
public IHttpActinoResult GetProjects([FromUri]ProjectSpecification projectSpecification) {
return Ok(projectSpecification);
}
}
The client can put the CustomerId value in the query string.
For example:
/api/Projects?CustomerId=2
and Web API will use them to construct a ProjectSpecification with the CustomerId set to 2 .

How to avoid identical actions that do different things in RESTful Web API 2?

I'm in the process of designing a RESTful Web API and have bumped into the following problem: I need a controller to retrieve collections (called Sections) of a hierarchical structure as well as to retrieve a single part (a single Section). If I need a collection I have to refer to the ID of the root Section which gives me a subtree of the whole structure. So I went ahead and defined a SectionsController like this:
public class SectionsController : ApiController
{
// GET api/sections/5
// Gets a subtree.
public IEnumerable<Section> Get(int rootId)
{
...
}
// GET api/sections/5
// Gets a single section.
public Section Get(int sectionId)
{
...
}
Which obviously doesn't work as the signatures are identical. What is the recommended way to go about this?
If you want to follow standard REST patterns you should introduce a slightly different API:
public class SectionsController : ApiController
{
// GET api/section
public IEnumerable<Section> GetAll()
{
...
}
// GET api/section/5
public Section Get(int sectionId)
{
...
}
Normally you should use singular resources and provide identifier only for a specific one. You can't have same URLs, even with different controllers.
Reading this post on SO regarding image transfer and following the link provided I realized there is a very simple solution to this problem that respects REST and at the same time doesn't require additional controllers.
Just return a collection of the subtree IDs within the object requested for a particular ID, i.e.
public class Section
{
public int Id { get; set; }
public string Name { get; set; }
public int[] DescendantIds { get; set; }
}
So with a single call to
api/section/5
I get all the details for the section with ID 5 as well as the IDs of the sections below. Yes, there's some overhead involved, so you have to decide for yourself if this solution is for you.

When/Where should i create the following class in my ASP.NET MVC project?

I have a simple class that contains some general information about the current web site :-
public class WebSiteSettings
{
public EnvironmentType Environment { get; set } // Eg. Production
public VersionType Version { get; set; } // Eg. Version2
public string ApiKey { get; set; } // Eg. ABCDE-1234
}
Now, I wish to have the following Route :-
// Api - Search methods.
routes.MapRoute(
"Search Methods",
"{versionDate}/{controller}/{action}"
);
and here's a sample url:
http://localhost.api.mydomain.com:1234/2010-01-23/search/posts?user=JonSkeet&apikey=ABCDE-1234
I currently handle retrieving the apikey via a custom ActionFilter. That's kewl. But i'm not sure how i can extract the versiondate (ie. 2010-01-23) from the url and populate that simple class i made .. which all controllers will have access too.
I was initially thinking of doing this:
public abstract class AbstractController : Controller
{
protected WebSiteSettings WebSiteSettings { get; set; }
protected AbstractController() : base()
{
WebSiteSettings = new WebSiteSettings();
// 1. Read in the version data and figure out the version number.
// 2. Read in the app settings to figure out the environment. (easy to do).
// 3. No api key just yet. this will be handled in the OnAuthorization action filter.
}
}
public class SearchController : AbstractController
{
public ActionResult Posts(..)
{ // ... blah ... }
}
So, i tried this and the RouteData was null, in the abstract controller. In fact, most of the information was null, in the abstract controller.
So i'm not sure when the proper place is to read in those values and create that class.
Anyone have an idea?
You might want to try doing that stuff in the AbstractController.OnActionExecuting method, the constructor is too early.
protected override void OnActionExecuting(ActionExecutingContext context)
{
// context has all the goods
}
http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.onactionexecuting.aspx?ppud=4

Categories