ServiceStack Request DTO with variable number of properties - servicestack

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) {
...
}
}
}

Related

Adding Request Headers to a manually instantiated ServiceStack object?

using(var service = new PlacementService())
{
service.Request.Headers.Add("Impersonated", "1"); //NULL Exception thrown
}
I need to be able to set a header on a manually instantiated ServiceStack class, but the request object is NULL so it's not working. It's being called inside a HttpPost Mvc Controller method.
The service was created to check a header to see if the request was coming from an impersonated user, it's just usually called directly from the frontend, I need to access a couple methods from it in backend code... this is the only thing tripping me up.
You can invoke Services with the Service Gateway or by using ResolveService from the ServiceStackController base class, e.g:
using var service = ResolveService<PlacementService>();
But the HttpRequestBase.Headers collection is immutable where you wouldn't be able to modify the HTTP Request collections. Instead you would typically populate the Items dictionary to pass additional info to the Service.
Otherwise you could populate the service with a custom IRequest which will let you construct your own Request context, e.g:
using var service = new PlacementService {
Request = new BasicRequest(requestDto)
}

ServiceStack: business logic that depends on the database itself

I'm exploring ServiceStack and I'm not sure what is the best way to implement some business logic.
Using the "Bookings CRUD" example I would like to enforce the following rule:
a given Booking can only be saved (either created or updated) if the hotel has enough free rooms for the particular dates of that booking
Please note that I'm not asking how to calculate "free rooms".
What I'm asking is, from the architectural point of view, how should this be done.
For example, one way would be:
create a request DTO to query the number of configured rooms (lets call it "QueryRooms")
use the existing "QueryBookings" to query current bookings present in database
create a " : Service" class to customize the Booking Service, in order to intercept the "CreateBooking" and "UpdateBooking" requests
inside the custom methods for "CreateBooking" and "UpdateBooking", somehow get the results of "QueryRooms" and "QueryBookings", check if there are enough free rooms for the current request, and proceed only if so
This doesn't look very clean, because the service "CreateBooking" and "UpdateBooking" would depend of "QueryRooms" and "QueryBookings".
What would be an elegant and effcient solution, using ServiceStatck?
You can override AutoQuery CRUD operations with your own Service implementation using the AutoQuery DTO.
Where you can use the Service Gateway to call existing Services which you can use to perform any additional validation & modify the request DTO before executing the AutoQuery operation to implement the API, e.g:
public class MyCrudServices : Service
{
public IAutoQueryDb AutoQuery { get; set; }
public object Post(CreateBooking request)
{
var response = Gateway.Send(new QueryRooms
{
From = request.BookingStartDate,
To = request.BookingEndDate,
});
if (response.Results.Count == 0)
throw new Exception("No rooms available during those dates");
request.RoomNumber = response.Results[0].Id;
return AutoQuery.Create(request, base.Request);
}
}
Note: calling in-process Services with the Service Gateway is efficient as it calls the C# method implementation directly, i.e. without incurring any HTTP overhead.

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.

How to use Custom Routes with Auto Query

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.

Global request/response interceptor

What would be the easiest way to setup a request/response interceptor in ServiceStack that would execute for a particular service?
A request filter (IHasRequestFilter) works fine but a response filter (IHasResponseFilter) is not triggered if the service returns non 2xx status code. I need to retrieve the status code returned by the method as well as the response DTO (if any).
A custom ServiceRunner and overriding the OnBeforeExecute and OnAfterExecute methods seems to work fine but I find it pretty intrusive as the service runner need to be replaced for the entire application and I couldn't find a way clean way to isolate per functionality the tasks that need to be executed in those methods.
Is there some extension point in ServiceStack that I am missing that would allow me to execute some code before each service method and after each service method? A plugin would be ideal but how can I subscribe to some fictitious BeforeExecute and AfterExecute methods that would allow me to run some custom code?
UPDATE:
Just after posting the question I found out that global response filters are executed no matter what status code is returned by the service which is exactly what I needed. So one last question: Is it possible to retrieve the service type that will handle the request in a request filter? I need to check whether this service is decorated by some custom marker attribute.
I have found out a solution to my question about how to retrieve the service type in a custom request/response filter:
appHost.RequestFilters.Add((req, res, requestDto) =>
{
var metadata = EndpointHost.Metadata;
Type serviceType = metadata.GetServiceTypeByRequest(requestDto.GetType());
...
}
A custom ServiceRunner and overriding the OnBeforeExecute and OnAfterExecute methods seems to work fine but I find it pretty intrusive as the service runner need to be replaced for the entire application
Quick note, you can opt-in and choose only what requests should use a custom service runner, e.g:
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(
ActionContext actionContext)
{
return useCustomRunner(actionContext.RequestType)
? new MyServiceRunner<TRequest>(this, actionContext)
: base.CreateServiceRunner<TRequest>(actionContext);
}
IHttpRequest has OperationName. I think thats what you are after.

Resources