How to use Custom Routes with Auto Query - servicestack

Using the first example in the ServiceStack Auto Query documentation in a project structured similar to the EmailContacts sample project (i.e. separate projects for the ServiceModel and ServiceInterface), how would one register the custom route "/movies" defined by the Route attribute?
[Route("/movies")]
public class FindMovies : QueryBase<Movie>
{
public string[] Ratings { get; set; }
}
Normally, custom routes such as these can be register by passing the ServiceInterface assembly when instantiating AppHostBase:
public AppHost() : base("Email Contact Services", typeof(ContactsServices).Assembly) {}
However, the FindMovies request DTO does not have an associated service and therefore won't be included. No routes are registered.
If I pass typeof(FindMovies).Assembly instead of or in addition to typeof(ContactsServices).Assembly, then the pre-defined route will be registered (i.e. shows up in the metadata, postman, etc.) but the custom route is still not registered (i.e. does not show up in the metadata, postman, etc.).
What is the best way to register the custom route using attributes when there is no service and the ServiceModel and ServiceInterface are in separate projects?

These issues should be resolved in v4.0.24+ that's now available on MyGet.
There's a new AutoQueryFeature.LoadFromAssemblies property to specify an additional list of assemblies to scan for IQuery Request DTO's. This automatically looks in the assemblies where your other Request DTO's are defined so in most cases nothing needs to be done as it will automatically be able to find your query services.
The routes for Query DTO's should now appear on the metadata pages as well as Swagger and Postman metadata API's.

Related

Binding new service dynamically in application.ts or in controller

we have the following scenario with REST custom connectors.
I have followed the lb4 guidelines to create REST based custom connectors, let's say i have two connectors i.e secrets and hello-world.
I also have the loopback application which has data sources, models, repositories and services for 2 integrations.
But I want to maintain a single controller which has service injection in constructor like below
#inject(ConnectorServiceBindings.CONNECTOR_SERVICE) public externalDataService: ConnectorRepositoryService,
here ConnectorRepositoryService is an interface which has contract for method called getSomething(), so i have 2 services which implements interface for calling respective connector.
So in my application.ts i have this.bind(ConnectorServiceBindings.CONNECTOR_SERVICE).toClass( SecretsService, ); this to bind the injection with respective service.
But i needed to have a condition here to select the binding like below
if (type === "secrets") { // how to pass this type during runtime
this.bind(ConnectorServiceBindings.CONNECTOR_SERVICE).toClass(
SecretsManagerServiceService,
);
} else {
this.bind(ConnectorServiceBindings.CONNECTOR_SERVICE).toClass(
HelloWorldService,
);
}
is there a way we can do this binding whenever we get new API request?????
FYI : controller has single POST end point which internally calls interface method using this.externalDataService.getData?.()!;
Question 1 : Why do you want to bind at application.ts?
Ans : In future we want to add more connectors, so doesn't want to touch the controller code every time.

Is there a way to remove the "/json/reply/" section of the url?

I would like the URL for a request to be /AmazingRequest (or even /AmazingService) instead of /json/reply/AmazingRequest.
I've tried the Route attribute, but it seems to have no effect. Is it possible within ServiceStack, or would I have to resort to URL rewriting?
This is what I've tried. It compiles, but the attribute has no effect.
public class MyServiceEndpoints : IService
{
[Route("/AmazingService")]
public AmazingResponse Post(AmazingRequest request)
{
return new Amazing(request).GetResponse();
}
}
I realize I would need to tell ServiceStack that it is a json request, but I'm fine with adding the Accept and Content-Type headers or maybe even a ?format=json to the query string.
P.S. I'm using the BSD version of ServiceStack
In ServiceStack Routes are defined on the Request DTO as it's part of your Service Contract, e.g:
[Route("/AmazingService")]
public class AmazingRequest { ... }
The pre-defined Route you're using is because ServiceStack doesn't think there's any custom route defined for your Service and just uses the default one.
The alternative way for declaring your Routes is to use the Fluent Registration API in your AppHost, e.g:
public void Configure(Container container)
{
Routes
.Add<AmazingRequest>("/AmazingService");
}
But the benefit of defining them on the Request DTO's is that your .NET Service Clients will also have access to them and will be able to use your custom routes instead of falling back to the pre-defined routes.

Servicestack - Grouping like services together

Was wondering if there's a recommended best-practice way of grouping similar services together in what's becoming a larger and larger project. Say that most of my services can be lumped in either dealing with "Pro" data or "Amateur" data (the data goes way beyond a simple flag in a table, the data itself is totally different, from different tables, on the pro or amateur side.
I know I can add routes to my classes...
/pro/service1
/am/service2
It looks like I can put the DTOs in namespaces....
What about the Service.Interface items (Service and Factory classes). Would you put those into namespaces also?
Finally, is there a way for the metadata page to reflect these groupings? I started to go down this road, but all the services listed out in alphabetical order, and you couldn't see the route or namespace differences between service1 and service2.
thank you
If you want, you can split multiple Service implementations across multiple dlls as described on the Modularizing Services wiki.
You can safely group service implementation classes into any nested folder groupings without having any impact to the external services. But changing the namespaces on DTO's can have an effect if your DTO's make use of object, interfaces or abstract classes which emit type info containing full namespaces.
In ServiceStack v4.09+ (now on MyGet) the MetadataFeature includes the ability to customize the ordering of the metadata page, e.g you can reverse the order of the metadata pages with:
var metadata = (MetadataFeature)Plugins.First(x => x is MetadataFeature);
metadata.IndexPageFilter = page => {
page.OperationNames.Sort((x,y) => y.CompareTo(x));
};
Organising your large project:
For a complex service(s) I setup 4 projects in one solution.
AppHost, This takes care of the configuration of the service. (References Model, Service and Types)
Model, This is the database model (Does not reference other projects)
Service, This is the implementation of the service only, not the interfaces or DTOs (References Model and Types)
Types, This includes my Interfaces, DTOs and routes. (Does not reference other projects)
Having a separate Types library allows the distribution to clients, for example for use with the ServiceStack JsonServiceClient.
Yes you can namespace the Interfaces, DTOs and factory classes, any way you want. They will work as long as they are referenced in your service correctly.
If you are trying to separate more than one service, you should consider separating your service code into logical folders within the Service project. i.e.
/Service/Pro
/Service/Amateur
Wrap the outer code of your Service methods in a public partial static class MyServiceStackApplication, with an appropriate name. Then reference this as the assembly in the AppHost constructor. So for example:
Pro Service (Service Project/Pro/UserActions.cs)
public partial static class MyServiceStackApplication
{
public partial class Pro
{
public class UserActionsService : Service
{
public User Get(GetUserRequest request)
{
}
}
// ...
}
}
Pro Service (Service Project/Pro/OtherActions.cs)
public partial static class MyServiceStackApplication
{
public partial class Pro
{
public class OtherActionsService : Service
{
public Other Get(GetOtherRequest request)
{
}
}
// ...
}
}
Amateur Service (Service Project/Am/UserActions.cs)
public partial static class MyServiceStackApplication
{
public partial class Amateur
{
public class UserActionsService : Service
{
public User Get(GetUserRequest request)
{
}
}
// ...
}
}
etc.
You can see from the above code we can have multiple files, all separated out and organised, but one assembly for ServiceStack to reference in the AppHost:
public AppHost() : base("Pro & Amateur Services", typeof(MyServiceStackApplication).Assembly) {}
Using the reference to the MyServiceStackApplication assembly, and using the partial keyword allows you to organise the code into manageable groupings.
Metadata:
Unfortunately separating the metadata by namespace isn't supported. You could try and customize the MetaDataFeature yourself, but it does seem like a useful feature, being able to separate multiple services where they are hosted in the one ServiceStack application. I would suggest you raise a feature request.
Mythz is bringing out features faster than lightning. :) Seems like he has that covered in the next release and you should be able to apply a custom filter to HostContext.Metadata.OperationNamesMap.

Servicestack Razor Markdown View Resolution

I want to display a view after facebook authentication in my application but I'm getting the json response instead the html view, according to the documentation the view resolution is the following:
The resolution order ServiceStack's uses to resolve the appropriate Markdown template to use for rendering HTML output is:
If the Web Service specifies a template (via a customized IHttpResult.TemplateName response) - then a View with that name.
A view with the same name as the Response DTO, looking first in /Views then in /Views/Shared
A view with the same name as the Request DTO, looking first in /Views then in /Views/Shared
these are my DTOs
public class FacebookRequest{}
public class FacebookResponse{}
this is my route configuration
Routes.Add<FacebookRequest>("/User/facebook");
my view has the folloing code:
#inherits ViewPage<MyProject.Services.Dto.FacebookResponse>
this is a facebook response
Service method:
[ClientCanSwapTemplates]
public FacebookResponse Get(FacebookRequest userRequest)
{
return new FacebookResponse();
}
As far as I understand if I have a view located in the "views" directory with the same responseDTO it should wire the view with the response from the service.
Adding comment as answer
SS uses the exact name of the response DTO class and looks for a view file with that exact name on the filesystem, unless you provide a DefaultView attribute. So, if your view (in your example) wasn't Views/FacebookResponse.cshtml exactly, it won't work. Using the inherits viewpage statement doesn't help SS resolve the view. It's looking for the exact file name in a specific location. I'm not 100% on this - but I seem to recall that it was a case sensitive match too.

ServiceStack Request DTO with variable number of properties

I'd like to create an endpoint that is the front end for a query service and I'd like to support a scenario where any number of arguments can be passed into the service via querystring parameters.
This is the sort of URL I want:
/queries/MyQuery?argument1=value&arg2=value
The DTO object structure itself is fairly straightforward (a property such as Dictionary<string, object>) but I'm stuck on what the route should look like. Is this possible via the RouteAttribute or do I need to go another direction?
Here's a good answer that explains Routing in ServiceStack.
Only the PathInfo is apart of the Route, the Request DTOs on all routes/services are also automatically populated based on the Request Params on the QueryString/FormData etc.
So if you know what the QueryString is going to be you can just add it as properties on the Request DTO and they will get automatically populated.
If you don't know what they are, but you still want access to the QueryString you can get it from the base.Request property in your Service, e.g:
class MyServices : Service
{
public object Get(MyQuery request)
{
foreach (var entry in base.Request.QueryString) {
...
}
}
}

Resources