I want to access the calling Service from inside the ServiceRunner OnBeforeRequest()method in order to get to an object in the calling service class. In MVC, I can create a class BaseController that overrides OnActionExecuting() and I can get to Data easily. However, using ServiceRunner, since it's not derived from Service, I don't see a way to get to the Service object.
Sample service:
public class ProductsService : Service
{
private MyData _data = new MyData();
public MyData Data
{
get { return _data; }
}
public object Get(GetProduct request)
{
// ...
return product;
}
}
In my custom ServiceRunner, how do I retrieve the ProductsService object from OnBeforeRequest() so I can get to Data?
public class MyServiceRunner<T> : ServiceRunner<T>
{
public override void OnBeforeExecute(IRequestContext requestContext, T request)
{
// var productService = ?
base.OnBeforeExecute(requestContext, request);
}
}
After much digging, it looks like this cannot be done. The Service action is available in the ServiceRunner as an unnamed lamdba delegate. There is no reference to the Service.
I have instead found a workaround. I first registered MyData in AppHost.Configure() using
container.RegisterAutoWired<MyData>();
I moved the MyData declaration to a filter attribute like this:
public class UseMyDataAttribute : RequestFilterAttribute
{
public MyData Data { get; set; } // injected by Funq IoC.
public override void Execute(IHttpRequest req, IHttpResponse res, object responseDto)
{
Data.SessionID = req.GetSessionId();
}
}
This way I can apply [UseMyData] to the ProductsService class and be able to set the Session ID to Data.
Related
I would like to be able to leverage ServiceStacks routing and built in documentation functionality to expose service operations based on the message type annotation with route and verb. A generic Service will simply delegate the received request to another Request Handling service.
eg.
Give I have the following type and configured routes with verb for each
public class Request{}
[Route("/order", "POST")]
public class PlaceOrder : Request { }
[Route("/order/{id}", "GET")]
public class OrderDetailsCriteria : Request {
Guid OrderId { get; set; }
}
public class OrderDetails { }
[Route("/inventoryItem/{id}", "PATCH")]
public class ReduceInventory : Request {
Guid InventoryItemId { get; set; }
}
I would like to create a simple service that will deal with all these but at the same time only allow the verbs as configured in the types attribute
something like:
public class MyService : Service {
public void Post(Request request) {
RequestService.Send(request);
}
public void Patch(Request request) {
RequestService.Send(request);
}
public object Get(Request request) {
return RequestService.Send(request);
}
}
Essentially I do not want to have to create a service or each set of Request types because each method will effectively be doing the same thing.
You can define a single implementation to handle all Verbs for a request by using the Any() method, e.g:
public object Any(MyRequest request)
{
var verb = base.Request.Verb; //if needed
...
RequestService.Send(request);
}
PFB my code.
namespace ManualCSharpe
{
public class MyServices : Service
{
[Route("/L/hello/")] //RequestDTO one
public class HelloL
{
public string Name { get; set; }
}
[Route("/H/hello/")] //RequestDTO two
public class HelloH
{
public string Name1 { get; set; }
}
public class HelloResponse //ResponseDTO
{
public string Result { get; set; }
}
public class HelloServiceL : Service //Service One
{
public object Get(HelloL request)
{
return new HelloResponse { Result = "Low" };
}
}
public class HelloServiceH : Service //Service
{
public object Get(HelloH request)
{
return new HelloResponse { Result = "High" };
}
}
//Define the Web Services AppHost
public class AppHost : AppSelfHostBase
{
public AppHost()
: base("HttpListener Self-Host",new Assembly[] {typeof(HelloServiceL).Assembly, typeof(HelloServiceH).Assembly}) { }
public override void Configure(Funq.Container container) { }
}
//Run it!
static void Main(string[] args)
{
var listeningOn = args.Length == 0 ? "http://*:133/" : args[0];
var appHost = new AppHost()
.Init()
.Start(listeningOn);
Console.WriteLine("AppHost Created at {0}, listening on {1}",
DateTime.Now, listeningOn);
Console.ReadKey();
}
}
}
When I am tring to added two service then it is show below exception.
An unhandled exception of type 'System.Reflection.AmbiguousMatchException' occurred in ServiceStack.dll
Additional information: Could not register Request 'ManualCSharpe.MyServices+HelloL' with service 'ManualCSharpe.MyServices+HelloServiceL' as it has already been assigned to another service.
Each Request DTO can only be handled by 1 service.
I have below douts.
Here I have created two different DTO for Two Service. Why it is showing error like Each Request DTO can only be handled by 1 service. In simple word, Two route mapped with two DTO with two Service.
Can I create one route for multiple RequestDTO with multiple service? In Simple word, One Route/L/hello/ can be mapped with two DTO HelloL and HelloH.
You can't have Service class implementations nested inside another outer MyServices class:
public class MyServices : Service
{
[Route("/L/hello/")] //RequestDTO one
public class HelloL
{
public string Name { get; set; }
}
[Route("/H/hello/")] //RequestDTO two
public class HelloH
{
public string Name1 { get; set; }
}
public class HelloResponse //ResponseDTO
{
public string Result { get; set; }
}
public class HelloServiceL : Service //Service One
{
public object Get(HelloL request)
{
return new HelloResponse { Result = "Low" };
}
}
public class HelloServiceH : Service //Service
{
public object Get(HelloH request)
{
return new HelloResponse { Result = "High" };
}
}
}
Remove the outer MyServices class completely and just have the DTO's and Service classes directly under a C# namespace.
Also routes shouldn't end with a / suffix, so I'd change:
[Route("/L/hello/")]
to:
[Route("/L/hello")]
#mythz answer is correct for OP but I came here looking for an answer for a different situation which the cause was not particularly obvious - you will get this exception if you attempt to register the same assembly twice, for example, if you move a service implementation into the same assembly and were pulling it in like so:
public AppHost() : base("App", typeof(AdminService).GetAssembly(), typeof(InboundService).GetAssembly(),typeof(ProductService).GetAssembly())
For those of you who come here from a google search, a AmbiguousMatchException exception in ServiceStack can sometimes be triggered within ServiceStack but handled internally.
You can change your exception setting so it doesn't break on this exception.
I had changed my exception setting to break on all exceptions and this had me stuck for a while.
I have a base Controller on my ASP.NET MVC4 website that have a Constructor simple as this:
public class BaseController : Controller
{
protected MyClass Foo { get; set; }
public BaseController()
{
if (User.Identity.IsAuthenticated))
{
Foo = new MyClass();
}
}
}
However I cannot access User here. It's null. But on my inherited Controllers it's fine.
Thanks
Controller instantiation will occur before authorisation takes place. Even if your MVC application calls RenderAction() several times and you end up creating say, five different controllers, those five controllers will be created before any OnAuthorization takes place.
The best approach to deal with these situations is to use Action Filters. The Authorize Attribute is fired early and may well be suited to your situation.
First, let's create an AuthorizationFilter.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MyClassAuthorizationAttribute : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Controller.ViewData["MyClassInstance"] = new MyClass();
}
}
}
Now let's update our Controller
[MyClassAuthorization]
public class BaseController : Controller
{
protected MyClass Foo
{
get { return (MyClass)ViewData["MyClassInstance"]; }
}
}
In this case I would override Controller Initialize method:
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
// User.Identity is accessible here
}
I have generic Result<T> generic class which I use often in methods to return result like this
public Result<User> ValidateUser(string email, string password)
There is ILoggingService interface in Result class for logging service injection but I do not find a way to inject actual implementation.
I tried to execute the code below but TestLoggingService intance is not injected into LoggingService property. It always return null. Any ideas how to solve it?
using (var kernel = new StandardKernel())
{
kernel.Bind<ILoggingService>().To<TestLoggingService>();
var resultClass = new ResultClass();
var exception = new Exception("Test exception");
var testResult = new Result<ResultClass>(exception, "Testing exception", true);
}
public class Result<T>
{
[Inject]
public ILoggingService LoggingService{ private get; set; } //Always get null
protected T result = default(T);
//Code skipped
private void WriteToLog(string messageToLog, object resultToLog, Exception exceptionToLog)
{
LoggingService.Log(....); //Exception here, reference is null
}
You are creating the instance manually using new. Ninject will only inject objects created by kernel.Get(). Furthermore it seems you try to inject something into a DTO which is not recommended. Better do the the logging in the class that created the result:
public class MyService
{
public MyService(ILoggingService loggingService) { ... }
public Result<T> CalculateResult<T>()
{
Result<T> result = ...
_loggingService.Log( ... );
return result;
}
}
I am trying to create a Custom Attribute for a ServiceStack Service with which I can control each method in the service Class.
This is the attribute class that I am implementing.
[AttributeUsage(AttributeTargets.Method|AttributeTargets.Class , Inherited = false, AllowMultiple = false)]
public class MyRequestFilterAttribute:RequestFilterAttribute
{
public string Provider { get; set; }
public MyRequestFilterAttribute(ApplyTo applyTo): base(applyTo)
{
this.Priority = (int) RequestFilterPriority.Authenticate;
}
public MyRequestFilterAttribute():this(ApplyTo.All)
{
}
public MyRequestFilterAttribute(ApplyTo applyTo, string provider): this(applyTo)
{
this.Provider = provider;
}
public MyRequestFilterAttribute(string provider): this(ApplyTo.All)
{
this.Provider = provider;
}
public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
{}
}
This is the Service Class
[MyRequestFilter(ApplyTo.All)]
public class TodoService : RestServiceBase<Todo>
{
public TodoRepository Repository { get; set; }
public override object OnGet(Todo request)
{
if (request.Id == default(long))
return Repository.GetAll();
return Repository.GetById(request.Id);
}
public override object OnPost(Todo todo)
{
return Repository.Store(todo);
}
public override object OnPut(Todo todo)
{
return Repository.Store(todo);
}
[MyRequestFilter("Admin")]
public override object OnDelete(Todo request)
{
Repository.DeleteById(request.Id);
return null;
}
public object GetDetailsofALL()
{
return null;
}
}
I am able to get control on the Methods when I place the attribute on the Class level.
i.e
[MyRequestFilter(ApplyTo.All)]
public class TodoService : RestServiceBase<Todo>{}
What I require is to place an attribute on the method level as well and do some authentication such that only an admin has the right to perform this method of the service.
But when I place this attribute in the Delete method it is not working and the custom filter attribute is not getting hit. Only the class level attribute call works.
[MyRequestFilter("Admin")]
public override object OnDelete(Todo request){}
Is it possible to set the permission/filter attribute from the method level? If yes, how can I implement this?
No it's not possible to add it on the method. You can add it on the service class or the Request DTO and use the ApplyTo method filter to tell servicestack which methods it should apply to, e.g:
[MyRequestFilter(ApplyTo.All)]
[MyRequestFilter(ApplyTo.Delete, "Admin")]
public class TodoService : RestServiceBase<Todo> { ... }
You can also refer to the implementation of the [RequiredRole] and [RequestPermission] attributes for examples of Request Filter attributes that support this.