I need to access request context, specifically the Items inside my custom class and I don't want to do have it either inheriting from ServiceStack Service or having the set it up inside the my Service.
So if I have a class like below which the implementer class (ContextItemsGetter) also implements IRequiresRequest, I would expect the Request property to be populated.
public interface IGetContextItems
{
string Get(string key);
}
public class ContextItemsGetter : IGetContextItems, IRequiresRequest
{
public string Get(string key)
{
//someway to access http context items
//im RequestContext.Instance.Items[key] e.g. Prop1 Prop2
//or Request.blah but Request is always null
}
public IRequest Request { get; set; }
}
https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Web/IRequiresRequest.cs
However the Request is always null for both when SessionIdGetter is called from a genuine HTTP request or a redis message request. Am I doing something wrong? The purpose is to decouple and use Items to pass information between http request and redis message request.
I've also tried to use RequestContext.Instance.Items, where this worked for HTTP request, but during redis message request, the items are not there, the keys where I populated just before calling ExecuteMessage are not there.
var req = new BasicRequest { Verb = HttpMethods.Get };
req.Items.Add("Prop1", m.GetBody().Prop1);
req.Items.Add("Prop2", m.GetBody().Prop2);
var result = HostContext.ServiceController.ExecuteMessage(m, req);
I'm using version 4.0.50.
Also, this page Access HTTP specific features in services where mentions
Note: ServiceStack's Service base class already implements IRequiresRequestContext which allows you to access the IRequestContext with base.RequestContext and the HTTP Request and Response with base.Request and base.Response.
I believe IRequiresRequestContext is now called IRequiresRequest, so I think the doc should be updated.
Updated: full code to demo my original intention:
[Route("/test", Verbs = "GET")]
public class Dto : IReturnVoid
{ }
public class DtoService : Service
{
//So that IGetContextItems is taken care of by IDependencyThatUsesIGetContextItems
public IDependencyThatUsesIGetContextItems DependencyThatUsesIGetContextItems { get; set; }
public void Get(Dto req)
{
DependencyThatUsesIGetContextItems.SomeMethod();
}
}
public interface IGetContextItems
{
string Get(string key);
}
//since ContextItemsGetter implmeents IRequiresRequest
//I can still easily test any service that uses IGetContextItems by mocking IGetContextItems
public class ContextItemsGetter : IGetContextItems, IRequiresRequest
{
public IRequest Request { get; set; }
public string Get(string key)
{
//either through injection
//return Request.Items[key].ToString();
//or some static class
//return RequestContext.RequestItems.Items[key].ToString();
return RequestContext.Instance.Items[key].ToString();
}
}
public interface IDependencyThatUsesIGetContextItems
{
string SomeMethod();
}
public class DependencyThatUsesIGetContextItems : IDependencyThatUsesIGetContextItems
{
//this will be inejcted
public IGetContextItems ContextItemsGetter { get; set; }
public string SomeMethod()
{
var a = ContextItemsGetter.Get("SomeKey");
return "blah";
}
}
IRequiresRequest only injects the current IRequest to your Service classes and Validation Filters, it doesn't inject the IRequest into your dependencies which are resolved directly from the IOC and who doesn't have access to current IRequest to be able to inject.
Also ServiceStack's convenient Service and AbstractValidator<T> base classes already implement IRequiresRequest so in most cases the places where IRequiresRequest applies has already been implemented so you shouldn't need to implement it yourself.
The recommended approach to passing the IRequest into your dependencies is to pass them as a parameter from your Service, e.g:
public class MyServices : Service
{
public IGetContextItems ContextItems { get; set; }
public object Get(Request request)
{
return ContextItems.Get(base.Request, request.Id);
}
}
You do have an opportunity to inspect and modify your Service instance before it executes your Service by overriding OnPreExecuteServiceFilter() in your AppHost to go through and inject the IRequest in each of your Services dependencies that implement IRequiresRequest with:
public override object OnPreExecuteServiceFilter(IService service,
object request, IRequest req, IResponse res)
{
service.InjectRequestIntoDependencies(req);
return request;
}
Which calls the below extension method will recursively populate your Services dependency graph as long as each parent implements IRequiresRequest:
public static class ServiceExtensions
{
public static void InjectRequestIntoDependencies(this object instance, IRequest req)
{
foreach (var pi in instance.GetType().GetPublicProperties())
{
var mi = pi.GetGetMethod();
if (mi == null)
continue;
var dep = mi.Invoke(instance, new object[0]);
var requiresRequest = dep as IRequiresRequest;
if (requiresRequest != null)
{
requiresRequest.Request = req;
requiresRequest.InjectRequestIntoDependencies(req);
}
}
}
}
But you need to be careful to not implement IRequiresRequest on any of your Singleton dependencies (the default scope) as it's not ThreadSafe whereas passing IRequest as a parameter would be.
Also to avoid coupling your logic classes to ServiceStack I'd consider only passing in what your dependencies needs from IRequest instead of the IRequest instance itself which will also make it easier to test.
Related
I would like to register a transient service that will be injected and used to instantiate controllers and other services.
The transient service's connection string depends up on different values coming from the request header. For example, the custom header will have two value UserId and UserName. Based on these values, I want to appropriately configure the service and register it.
My question is how can I pass the request header values to the class constructor ?
public class ServiceA: IServiceA
{
public ServiceA(string userId, string userName)
{
// instantiate the service based on userId and userName
}
}
I know I can create a middleware or filter but that is not where I need these values. I also dont want to pass these values as function argument so that i dont clutter the code base with usernames and userIds.
I saw IHttpContextAccessor can be used but MSFT cautions about performance issues when using this class.
What is the best approach to access request headers anywhere in the application other than the controllers ?
You can safely create a scoped RequestMetadataService and in combination with ControllerExtensions class use IHttpContextAccessor
public static class ControllerExtensions
{
public static string GetMyHeader(this HttpContext httpContext)
{
const string MyHeaderKey = "Key";
if(! httpContext.Request.Headers.TryGetValue(MyHeaderKey, out var myValue))
return null;
return myValue;
}
}
public class RequestMetadataService : IRequestMetadataService
{
public HttpContext HttpContext { get; set; }
public string MyValue
{
get => HttpContext?.GetMyHeader();
}
public RequestMetadataService(
IHttpContextAccessor httpContextAccessor
)
{
HttpContext = httpContextAccessor?.HttpContext;
}
}
public class ServiceA: IServiceA
{
public ServiceA(string userId, string userName)
{
var myValue = RequestMetadataService.MyValue;
}
}
while using in Startup
services.AddHttpContextAccessor();
I have an ASP.NET MVC 5 Application with a SignalR 2 hub and using autofac for the DI.
The entire business logic is encapsulated in manager classes in their own layer. Some manager methods need informations about the current logged in user (UserId, TenantId, ..).
I solved this problem by injecting an AuthorizationProvider into each manager class that needs the user information.
public interface IAuthorizationProvider
{
long? GetUserId();
long? GteTenantId();
}
public class MyManager : IMyManager
{
private IAuthorizationProvider _authorizationProvider;
public MyManager(IAuthorizationProvider authorizationProvider)
{
_authorizationProvider = authorizationProvider;
}
public void MyMethod()
{
// Getting the User information here is pretty simple
long userId = _authorizationProvider.GetUserId();
}
}
Normally I can get the user information from the HttpContext and from the session. So I wrote a SessionAuthorizationProvider:
public class SessionAuthorizationProvider{
public long? GetUserId()
{
HttpContext.Current?.Session?[SessionKeys.User]?.Id;
}
public long? GteTenantId() { ... }
}
But now I have a new method in the SignalR hub that use the same mechanism.
[HubName("myHub")]
public class MyHub : Hub
{
private IMyManager _myManager;
public MyHub(IMyManager myManager)
{
_myManager = myManager;
}
[HubMethodName("myHubMethod")]
public void MyHubMethod(long userId, long tenantId)
{
_myManager.MyMethod();
}
}
The problem is that a SignalR request doesn't have a session. Therefore I have also set the required user information in the hub method as parameters postet from the client.
So I thought it is the best solution for this problem to write a new AuthorizationProvider for SignalR and adapt the depdendency resolver. But I can't get the current user in the new SignalrAuthorizationProvider.
public class SignalrAuthorizationProvider{
public long? GetUserId()
{
// How to get the user information here???
}
public long? GteTenantId() { /* and here??? */ }
}
Is there a recommended solution to this problem?
Of course, I can extend MyMethod to accept the user information as a parameter. But MyMethod calls another method from another manager and that manager also calls another method. The user information is only needed for the last method call. So I had to change at least 3 methods and many more in the future.
Here is a sketch of the problem
This is a potential solution. But it's very bad
Session is not supported by SignalR by default and you should avoid using it. See No access to the Session information through SignalR Hub. Is my design is wrong?. But you still can use cookie or querystring to get the desired value.
In both case you need to have access to the HubCallerContext of the underlying hub, the one that is accessible through the Context property of the Hub.
In a ideal word you should just have to had the dependency to the SignalAuthorizationProvider
ie :
public class SignalrAuthorizationProvider {
public SignalrAuthorizationProvider(HubCallerContext context){
this._context = context;
}
private readonly HubCallerContext _context;
public long? GetUserId() {
return this._context.Request.QueryString["UserId"]
}
}
But due to SignalR design it is not possible. Context property is assigned after construction of the Hub and AFAIK there is no way to change it.
Source code here : HubDispatcher.cs
One possible solution would be to inject a mutable dependency inside the Hub and alter the object in the OnConnected, OnReconnected methods.
public class SignalrAuthorizationProvider : IAuthorizationProvider
{
private Boolean _isInitialized;
private String _userId;
public String UserId
{
get
{
if (!_isInitialized)
{
throw new Exception("SignalR hack not initialized");
}
return this._userId;
}
}
public void OnConnected(HubCallerContext context)
{
this.Initialize(context);
}
public void OnReconnected(HubCallerContext context)
{
this.Initialize(context);
}
private void Initialize(HubCallerContext context) {
this._userId = context.QueryString["UserId"];
this._isInitialized = true;
}
}
and the Hub
public abstract class CustomHub : Hub
{
public CustomHub(IAuthorizationProvider authorizationProvider)
{
this._authorizationProvider = authorizationProvider;
}
private readonly IAuthorizationProvider _authorizationProvider;
public override Task OnConnected()
{
this._authorizationProvider.OnConnected(this.Context);
return base.OnConnected();
}
public override Task OnReconnected()
{
this._authorizationProvider.OnReconnected(this.Context);
return base.OnReconnected();
}
}
Having a mutable dependency is not the best design but I can't see any other way to have access to IRequest or HubCallerContext.
Instead of having an abstract Hub class which is not a perfect solution. You can change the RegisterHubs autofac method to use AOP with Castle.Core and let the interceptor calls the methods for you.
I am using Ninject 3 in an MVC5-based website, and trying to work out how to get DI to work with a type that tests properties of a Uri.Host value passed into its constructor. I'd like the binding to somehow provide the current URL. The minimal structure I've tried initially is:
public class StructuredUrlTester : IStructuredUrlTester
{
// Expose public getters for parts of the uri.Host value
bool MyBooleanProperty { get; private set; }
public StructuredUrlTester(Uri uri)
{
// Test the value of uri.Host and extract parts via regex
}
}
// In Global.asax.cs
public class MvcApplication : NinjectHttpApplication
{
protected override IKernel CreateKernel()
{
kernel.Bind<IStructuredUrlTester>()
.To<StructuredUrlTester>()
.InTransientScope();
.WithConstructorArgument("uri", Request.Url);
}
}
// In MyController.cs
public class MyController : Controller
{
private readonly IStructuredUrlTester _tester;
public ContentPageController(IStructuredUrlTester tester)
{
this._tester = tester;
}
public ActionResult Index()
{
string viewName = "DefaultView";
if (this._tester.MyBooleanProperty)
{
viewName = "CustomView";
}
return View(viewName);
}
}
As the CreateKernel() call happens before the Request object is available, the .WithConstructorArgument() part throws an exception ("System.Web.HttpException: Request is not available in this context").
How can I provide the binding of interface to concrete type, whilst also being able to provide the e.g. HttpContext.Current.Request.Url value (available within the Controller) to the constructor of the concrete type, at run-time when it's available?
Wrap the desired functionality in an abstraction:
public interface IUriProvider {
Uri Current { get; }
}
Refactor the tester class:
public class StructuredUrlTester : IStructuredUrlTester {
// Expose public getters for parts of the uri.Host value
bool MyBooleanProperty { get; private set; }
public StructuredUrlTester(IUriProvider provider) {
Uri uri = provider.Current;
// Test the value of uri.Host and extract parts via regex
}
}
The provider implementation should wrap the Request.Url:
public class UriProvider : IUriProvider {
public Uri Current { get { return HttpContext.Current.Request.Url; } }
}
And note that the Current property should actually be called within the action of a controller where HttpContext and its request are available.
public class ActionFilterVersionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.Request.Headers.Any(x => x.Key == "SetInternalVersion"))
{
// determine somehow that the **InternalSystem implementation** should be resolved when the controller class is instantiated with the **ISystem constructor** parameter
}
else
{
// determine somehow that the **ExternalSystem implementation** should be resolved when the controller class is instantiated with the **ISystem constructor** parameter
}
base.OnActionExecuting(actionContext);
}
}
I have ExternalSystem/InternalSystem with the ISystem interface.
How can I tell autofac to inject the ExternalSystem or InternalSystem into the instantiated controller as ISystem instance depending on the string value I pass in the ActionFilter or maybe message handler.
I know I can do stuff like:
builder.RegisterType<InternalSystem>().As<ISystem>().Keyed<ISystem>("Internal");
where I can use a func<string,ISystem> factory to resolve the class during runtime but this is not what I want to do.
Actually I need to register the ISystem within the the action filter, but then I would need somehow to pass the container into the filter, but that is not what I want...and prolly its also not possible.
// Action: returns external or internal value
public string Get()
{
return resolvedISystem.Get();
}
Of course I could resolve the ISystem depending on the func factory within each single action or put behavior into a base controller where I check for the header, but I really would prefer the action filter as it can be just globally registerd ONE time, but for each new controller I have to subclass the base controller.
Base controller sample with pseudo code , because the base.Request is null which needs another workaround/fix...
public class BaseController : ApiController
{
public BaseController(Func<string, ISystem> dataServiceFactory)
{
string system = base.Request.Headers.Any(x => x.Key == "SetInternalVersion") ? "internal" : "external";
System = dataServiceFactory(system);
}
public ISystem System { get; set; }
}
UPDATING the container is also marked as OBSOLETE by the Autofac author.
Thus I do not want to add registrations in my filter/handler and update/build the container again.
I think you should not use ActionFilter at all. You have a controller dependency which should be resolved properly based on the information coming from request. Here is a possible solution. You can use a static HttpContext.Current property in order to extract request header.
System classes:
public interface ISystem { }
public class ExternalSystem : ISystem { }
public class InternalSystem : ISystem { }
SystemKeyProvider:
public enum SystemKey
{
External,
Internal
}
public interface ISystemKeyProvider
{
SystemKey GetSystemKey();
}
public class SystemKeyProvider : ISystemKeyProvider
{
private const string HeaderKey = "SetInternalVersion";
private readonly HttpRequest _request;
public SystemKeyProvider(HttpRequest request)
{
_request = request;
}
public SystemKey GetSystemKey()
{
return (_request.Headers[HeaderKey] != null) ?
SystemKey.Internal :
SystemKey.External;
}
}
Controller constructor: ValuesController(ISystem system)
Autofac container registration:
var builder = new ContainerBuilder();
builder.Register(c => HttpContext.Current.Request).As<HttpRequest>().InstancePerRequest();
builder.RegisterType<SystemKeyProvider>().AsImplementedInterfaces();
// service registration
builder.RegisterType<ExternalSystem>().Keyed<ISystem>(SystemKey.External);
builder.RegisterType<InternalSystem>().Keyed<ISystem>(SystemKey.Internal);
builder.Register(c =>
c.ResolveKeyed<ISystem>(c.Resolve<ISystemKeyProvider>().GetSystemKey()))
.As<ISystem>();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
GlobalConfiguration.Configuration.DependencyResolver =
new AutofacWebApiDependencyResolver(builder.Build());
In this solution I created a SystemKeyProvider wrapper class which is responsible for providing appropriate key in order to resolve ISystem.
Demo:
When no SetInternalSystem header is present.
Then the dependency is resolved as ExternalSystem.
When SetInternalSystem header is present.
Then the dependency is resolved as InternalSystem.
Currently I'm writing a bot for a browsergame and there are several responses.
They are unencrypted and the data is provided by a normal http response.
So because there are severaly slightly different response-types and structures I thought I could use my (low) oop knowledge to handle this - but my concept does not work.
I thought I can make for each request (login request, logout request, attack request, harvest request, ..) a own class which is a child from the basic request class.
And the same concept I thought I can do for the responses.
So here is a small demo of my code:
public class BasicRequest
{
public BasicRequest(string serverId) { }
public virtual BasicResponse DoRequest(string[] requestData)
{
// request is handled here
}
}
public class LoginRequest : BasicRequest
{
public LoginRequest(string serverId) : base(serverId) { }
}
public class BasicResponse
{
public BasicResponse(string[] responseData) { }
public virtual void DoSomeStuffWithTheResponse() { }
}
public class LoginResponse : BasicResponse
{
public LoginResponse(string[] responseData) : base(responseData) { }
public override void DoSomeStuffWithTheResponse() { }
}
This is my basic structure (ofc I have some more request and response classes).
Now I tried to use it like this:
LoginResponse response = new LoginRequest("serverXX").DoRequest(new string[] { "data" }) as LoginResponse;
But then 'response' is just null.
The important thing is, that it should perform the actions that the constructor of the base class do (this is some basic stuff that every request and response need) and then the constructor and the override of the specific class (loginrequest/response in this case) should be called.
Hope you can help me out, thanks in advice.
Since you haven't overriden DoRequest in your LoginRequest class, when you invoke DoRequest the base classes implementation is being called.
In order for this to work, LoginRequest needs to override the base implementation:
public class LoginResponse : BasicResponse
{
public LoginResponse(string[] responseData) : base(responseData) { }
public override void DoSomeStuffWithTheResponse()
{
}
public override BasicResponse DoRequest(string[] requestData)
{
// Do stuff
return new LoginResponse();
}
}