.NET 6.0 ServiceStack 6.1.1 JsonServiceClient doesn't use /json/reply - servicestack

I have two application one is an API and is using SS v5.10.4 the other one is a service that was using SS v5.8.0 and was upgraded to v6.1.1
The service is referencing the API's DTOs using the ServiceStackVS plugin which creates a single *.dto file. Some of the DTOs in the API have custom routes others don't. Prior to upgrading the service to SS v.6.1.1 the calls using the DTOs without custom routes were going to /json/reply/{requestDTO} and the ones with custom routes were going to /{Route}. After the upgrade all calls are now going to /{requestDTO}. Any idea why that is and is there an easy way to default the JsonServiceClient to use the /json/reply/{requestDTO} if there isn't a custom route defined in the {myAPI}.dtos file for the specific DTOs ?

So seems like it's working as expected if I set the baseUri in the constructor of the JsonServiceClient instead of setting the BaseUri property.
Doesn't work:
_client = new JsonServiceClient
{
BaseUri = _configuration.ClientUri,
Timeout = TimeSpan.FromMinutes(_configuration.RequestTimeOut)
};
Works:
_client= new JsonServiceClient(_configuration.ClientUri)
{
Timeout = TimeSpan.FromMinutes(_configuration.RequestTimeOut)
};

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

DocuSign - Is there a better way to instantiate the APIclient using the C# sdk in .net core?

I am using E-signature C# SDK to integrate with DocuSign Api's from my .NET6 based API. As per the documentation ApiClient is instantiated like var client = new ApiClient("base address") .
Is there way to use the middleware to create an instance of the ApiClient so that I can only instantiate it once and inject it in my class, to call the SDK methods.
There are multiple constructors you can use.
You can just do new ApiClient() which defaults the basePath to be https://www.docusign.net/restapi
You can also use a Configuration object and pass that to the constructor instead of a basePath.
The most advanced constructor is this:
public ApiClient(string basePath, string oAuthBasePath, WebProxy proxy = null)
This one allows you to specify a different oAuthBasePath and even a WebProxy should you need one.
However, I assume you're actually asking about using dependency injection so that you can have your controllers pass along the singleton.
As it stands right now ApiClient is not implementing any Interface, so you cannot do it directly.
You can create your own class that extends ApiClient, implements some new interface you provide and then you'll be able to get this capability.
You can also take the code for the C# SDK and modify it to be an interface (IApiClient would probably be the name you want to use)

Servicestack Multitenancy dynamic plugins

We are moving from an on premise-like application to a multi tenant cloud application.
for my web application we made a very simple interface based on IPlugin, to create a plugin architecture. (customers can have/install different plugins)
public interface IWebPlugin : IPlugin
{
string ContentBaseUrl { set; get; }
}
We have some plugins that would normally be loaded in on startup. Now i'm migrating the code to load at the beginning of a request (the Register function is called on request start), and scope everything inside this request.
It's not ideal but it would bring the least impact on the plugin system for now.
I could scope the Container by making an AppHost child container which would stick to the request:
Container IHasContainer.Container
{
get
{
if (HasStarted)
return ChildContainer;
return base.Container;
}
}
public Container ChildContainer
{
get { return HttpContext.Current.Items.GetOrAdd<Container>("ChildContainer", c => Container.CreateChildContainer()); }
}
problem case
Now im trying to make plugins work that actually add API services.
appHost.Routes.Add<GetTranslations>("/Localizations/translations", ApplyTo.Get);
But this service is unreachable (and not visible in metadata). How do i make it reachable?
I see you execute the following in ServiceController AfterInit. Re-executing this still wouldnt make it work.
//Copied from servicestack repo
public void AfterInit()
{
//Register any routes configured on Metadata.Routes
foreach (var restPath in appHost.RestPaths)
{
RegisterRestPath(restPath);
//Auto add Route Attributes so they're available in T.ToUrl() extension methods
restPath.RequestType
.AddAttributes(new RouteAttribute(restPath.Path, restPath.AllowedVerbs)
{
Priority = restPath.Priority,
Summary = restPath.Summary,
Notes = restPath.Notes,
});
}
//Sync the RestPaths collections
appHost.RestPaths.Clear();
appHost.RestPaths.AddRange(RestPathMap.Values.SelectMany(x => x));
appHost.Metadata.AfterInit();
}
solution directions
Is there a way i could override the route finding? like extending RestHandler.FindMatchingRestPath(httpMethod, pathInfo, out contentType);
Or could i restart the path compilation/caching? (would be enough for now that the service would be reachable tenant wide )
All configuration in ServiceStack should be contained within AppHost.Configure() and remain immutable thereafter. It's not ThreadSafe to modify ServiceStack's Static Configuration at runtime like trying to modify registered routes or Service Metadata which needs to be registered once at StartUp in AppHost.Configure().
It looks as though you'll need to re-architect your solution so all Routes are registered on Startup. If it helps Plugins can implement IPreInitPlugin and IPostInitPlugin interfaces to execute custom logic before and after Plugins are registered. They can also register a appHost.AfterInitCallbacks to register custom logic after ServiceStack's AppHost has been initialized.
Not sure if it's applicable but at runtime you can "hi-jack Requests" in ServiceStack by registering a RawHttpHandler or a PreRequestFilter, e.g:
appHost.RawHttpHandlers.Add(httpReq =>
MyShouldHandleThisRoute(httpReq.PathInfo)
? new CustomActionHandler((req, res) => {
//Handle Route
});
: null);
Simple answer seems to be, no. The framework wasn't build to be a run-time plugable system.
You will have to make this architecture yourself on top of ServiceStack.
Routing solution
To make it route to these run-time loaded services/routes it is needed to make your own implementation.
The ServiceStack.HttpHandlerFactory checks if a route exist (one that is registered on init). so here is where you will have to start extending. The method GetHandlerForPathInfo checks if it can find the (service)route and otherwise return a NotFoundHandler or StaticFileHandler.
My solution consists of the following code:
string contentType;
var restPath = RestHandler.FindMatchingRestPath(httpMethod, pathInfo, out contentType);
//Added part
if (restPath == null)
restPath = AppHost.Instance.FindPluginServiceForRoute(httpMethod, pathInfo);
//End added part
if (restPath != null)
return new RestHandler { RestPath = restPath, RequestName = restPath.RequestType.GetOperationName(), ResponseContentType = contentType };
technically speaking IAppHost.IServiceRoutes should be the one doing the routing. Probably in the future this will be extensible.
Resolving services
The second problem is resolving the services. After the route has been found and the right Message/Dto Type has been resolved. The IAppHost.ServiceController will attempt to find the right service and make it execute the message.
This class also has init functions which are called on startup to reflect all the services in servicestack. I didn't found a work around yet, but ill by working on it to make it possible in ServiceStack coming weeks.
Current version on nuget its not possible to make it work. I added some extensibility in servicestack to make it +- possible.
Ioc Solution out of the box
For ioc ServiceStack.Funq gives us a solution. Funq allows making child containers where you can register your ioc on. On resolve a child container will, if it can't resolve the interface, ask its parent to resolve it.
Container.CreateChildContainer()

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 inject HttpRequestBase and HttpContextBase in Funq (while using ServiceStack)

I have been happily using AutoFaq for a couple of years and take advantage of its ability to easily inject HttpRequestBase and HttpContextBase in the MVC pipeline. This makes mocking and decoupling a lot easier.
I am in the process of changing my data layer to ServiceStack and as part of wiring the default Funq DI mechanism to my different layers I can't figure out how to inject HttpRequestBase and HttpContextBase.
Is there a way to do this? I am looking for the container.Register() analog inside of AppHost.Configure(Func.Container container).
Thanks
ServiceStack doesn't allow registering runtime dependencies with its IOC, although as ServiceStack Services and Request pipeline only binds to the IRequest interface which can just inject a mocked IRequest directly on the service when its required, e.g:
var service = new MyService {
Request = new MockHttpRequest()
};
var response = service.Get(new MyRequest { Id = 1 });
The Testing wiki shows other ways of testing ServiceStack services.
ServiceStack has it's own abstraction of the HttpContext and Request/Response. In v3.x, these are IRequestContext, IHttpRequest, IHttpResponse. This is to be implementation-independent of ASP.NET (console or Mono). It is recommended you use the abstractions instead of trying to use the underlying ASP.NET objects.
In your Service code, you may access them this way:
var httpReq = base.RequestContext.Get<IHttpRequest>();
var httpResp = base.RequestContext.Get<IHttpResponse>();
If you really need the real ASP.NET HttpContext, apparently you should be able to access it at IRequest.OriginalRequest. But if you are trying it the ServiceStack way, "don't do that".
More explanation of the Funq usage in v3 is here:
https://github.com/ServiceStackV3/ServiceStackV3/wiki/The-IoC-container

Resources