I am trying to setup a modular ServiceStack implementation but I can't seem to figure out how to address my plug-in.
Here is my ASP.Net MVC 4 Global.asax.cs:
public class MvcApplication : System.Web.HttpApplication
{
[Route("/heartbeat")]
public class HeartBeat
{
}
public class HeartBeatResponse
{
public bool IsAlive { get; set; }
}
public class ApiService : Service
{
public object Any(HeartBeat request)
{
var settings = new AppSettings();
return new HeartBeatResponse { IsAlive = true };
}
}
public class AppHost : AppHostBase
{
public AppHost() : base("Api Services", typeof(ApiService).Assembly) { }
public override void Configure(Funq.Container container)
{
Plugins.Add(new ValidationFeature());
Plugins.Add(new StoreServices());
}
}
protected void Application_Start()
{
new AppHost().Init();
}
This loads fine and I'm able to see the available "HeartBeat" Service. The service loaded by the plug-in is not found though.
Here is the plug-in code:
public class StoreServices: IPlugin
{
private IAppHost _appHost;
public void Register(IAppHost appHost)
{
if(null==appHost)
throw new ArgumentNullException("appHost");
_appHost = appHost;
_appHost.RegisterService<StoreService>("/stores");
}
}
and the corresponding service that it loads:
public class StoreService:Service
{
public Messages.StoreResponse Get(Messages.Store request)
{
var store = new Messages.Store {Name = "My Store", City = "Somewhere In", State = "NY"};
return new Messages.StoreResponse {Store = store};
}
}
[Route("/{State}/{City}/{Name*}")]
[Route("/{id}")]
public class Store : IReturn<StoreResponse>
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string State { get; set; }
}
public class StoreResponse
{
public Store Store { get; set; }
}
The url to run heartbeat is from localhost}/heartbeat and the meta can be found at from localhost}/metadata.
When I try to call {from localhost}/stores/1234 though I get a unresolved route?, but if you see the route attribute on the service call it should resolve?
The following is the response I get for the stores request:
Handler for Request not found:
Request.ApplicationPath: /
Request.CurrentExecutionFilePath: /stores/123
Request.FilePath: /stores/123
Request.HttpMethod: GET
Request.MapPath('~'): C:\Source Code\White Rabbit\SpiritShop\SpiritShop.Api\
Request.Path: /stores/123
Request.PathInfo:
Request.ResolvedPathInfo: /stores/123
Request.PhysicalPath: C:\Source Code\White Rabbit\SpiritShop\SpiritShop.Api\stores\123
Request.PhysicalApplicationPath: C:\Source Code\White Rabbit\SpiritShop\SpiritShop.Api\
Request.QueryString:
Request.RawUrl: /stores/123
Request.Url.AbsoluteUri: http://localhost:55810/stores/123
Request.Url.AbsolutePath: /stores/123
Request.Url.Fragment:
Request.Url.Host: localhost
Request.Url.LocalPath: /stores/123
Request.Url.Port: 55810
Request.Url.Query:
Request.Url.Scheme: http
Request.Url.Segments: System.String[]
App.IsIntegratedPipeline: True
App.WebHostPhysicalPath: C:\Source Code\White Rabbit\SpiritShop\SpiritShop.Api
App.WebHostRootFileNames: [global.asax,global.asax.cs,packages.config,spiritshop.api.csproj,spiritshop.api.csproj.user,spiritshop.api.csproj.vspscc,web.config,web.debug.config,web.release.config,api,app_data,bin,obj,properties]
App.DefaultHandler: metadata
App.DebugLastHandlerArgs: GET|/stores/123|C:\Source Code\White Rabbit\SpiritShop\SpiritShop.Api\stores\123
This code doesn't does not give your service a url prefix like you're assuming:
_appHost.RegisterService<StoreService>("/stores");
Instead the optional params string[] atRestPaths only specifies routes for the DefaultRequest route of that Service. You can specify which operation is the default using the [DeafultRequest] attribute, e.g:
[DefaultRequest(typeof(Store))]
public class StoreService : Service { ... }
Which allows you to specify the routes in-line instead of on the request DTO, i.e:
_appHost.RegisterService<StoreService>(
"/stores/{State}/{City}/{Name*}",
"/stores/{Id}");
But as you've already got the routes on the Request DTO you can ignore them here, i.e:
_appHost.RegisterService<StoreService>();
But you'll need to include the missing /stores url prefix, e.g:
[Route("/stores/{State}/{City}/{Name*}")]
[Route("/stores/{Id}")]
public class Store : IReturn<StoreResponse> { .. }
Related
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 am getting very confused with the Funq container.
I have the following:
public interface IConnectionString
{
string ConnectionString { get; set; }
}
public class FoundationConnection : IConnectionString
{
public FoundationConnection(string connectionString)
{
ConnectionString = connectionString;
}
public string ConnectionString { get; set; }
}
Now in my AppHost, I would like to register
container.Register<IConnectionString>(c=> new FoundationConnection(AppSettings.Get(
"FoundationConnectionString", "").MapHostAbsolutePath()));
In my ServiceInterface I want to call this injected method somehow:
public class ServiceInterface : Service
{
public IConnectionString foundationConnection { get; set; }
public object Any(SomeRequest request)
{
string injectedProperty = foundationConnection.ConnectionString;
}
}
}
Issue is that foundationConnection is null and never injected.
I hope this makes sense?
Personally I would use AppSettings to access config settings which would allow you to source configuration from a number of different and cascading configuration sources.
But I've tested this using these types:
public interface IConnectionString
{
string ConnectionString { get; }
}
class FoundationConnectionString : IConnectionString
{
public FoundationConnectionString(string connectionString)
{
ConnectionString = connectionString;
}
public string ConnectionString { get; set; }
}
and it's working correctly after registering it in the AppHost.Configure():
public override void Configure(Container container)
{
container.Register<IConnectionString>(c =>
new FoundationConnectionString("My Connection"));
}
and accessing it from a test service:
[Route("/test")]
public class Test : IReturn<string> { }
public class TestService : Service
{
public IConnectionString Config { get; set; }
public object Any(Test request)
{
return Config.ConnectionString;
}
}
Which returns "My Connection" when called.
Can I retrieve the operation DTO from url route inside a service stack service ?
Example :
public class HelloService : IService
{
public object Any(HelloRequest request)
{
//Here I want to retrieve operation Dto.
//In this case if request.AnotherApiRoute is "/another?Age=33"
//then result could be operation AnotherRequest
return new HelloResponse { Result = "Hello, " + name };
}
}
public class AnotherApiService : IService
{
public object Another(AnotherRequest request)
{
return new AnotherResponse { Result = "Your Age : " + Age };
}
}
//OPERATIONS
[Route("/hello/{Name}")]
public class Hello : IReturn<HelloResponse>
{
public string Name { get; set; }
public string AnotherApiRoute {get; set;}
}
public class HelloResponse
{
public string Result { get; set; }
}
[Route("/another/{Age}")]
public class AnotherRequest : IReturn<AnotherResponse>
{
public string Age { get; set; }
}
public class AnotherResponse
{
public string Result { get; set; }
}
Thanks for your suggests
If you want access to the HTTP Request Context the Service was executed in you should inherit from the convenience Service base class (or have your service also implement IRequiresRequestContext so Request is injected), e.g:
public class HelloService : Service
{
public object Any(Hello request)
{
var pathInfo = base.Request.PathInfo;
return new HelloResponse { Result = "Hello, " + name };
}
}
But what you're after is unclear since the Request DTO is the Operation DTO for that request. If instead you wanted to call another Service from within your Service you can do it with Resolving the Service from the IOC (which also injects the current HTTP Request) with:
public class HelloService : Service
{
public object Any(Hello request)
{
using (var service = base.ResolveService<AnotherService>())
{
var anotherDto = request.ConvertTo<Another>();
return service.Any(anotherDto);
}
}
}
Alternatively you can just execute the Service by passing in the Request DTO, and let ServiceStack call the appropriate Service, e.g:
public class HelloService : Service
{
public object Any(Hello request)
{
var anotherDto = request.ConvertTo<Another>();
return base.ExecuteRequest(anotherDto);
}
}
I must be overlooking something around getting the fluent-validation to fire within basic Service-Stack application I created.
I have been following the example found here. For the life of me I can't seem to get my validators fire????
Crumbs, there must be something stupid that I'm missing....???
I'm issuing a user request against the User-Service (http://my.service/users), the request goes straight through without invoking the appropriate validator registered.
Request is :
{"Name":"","Company":"Co","Age":10,"Count":110,"Address":"123 brown str."}
Response :
"user saved..."
Here is the code :
1.DTO
[Route("/users")]
public class User
{
public string Name { get; set; }
public string Company { get; set; }
public int Age { get; set; }
public int Count { get; set; }
public string Address { get; set; }
}
2.Validator
public class UserValidator : AbstractValidator<User>
{
public UserValidator()
{
RuleFor(r => r.Name).NotEmpty();
RuleFor(r => r.Age).GreaterThan(0);
}
}
3.AppHostBase
public class ValidationAppHost : AppHostBase
{
public ValidationAppHost()
: base("Validation Test", typeof(UserService).Assembly)
{
}
public override void Configure(Funq.Container container)
{
Plugins.Add(new ValidationFeature());
//This method scans the assembly for validators
container.RegisterValidators(typeof(UserValidator).Assembly);
}
}
4.Service
public class UserService : Service
{
public object Any(User user)
{
return "user saved...";
}
}
5.Global.asax.cs
protected void Application_Start(object sender, EventArgs e)
{
new ValidationAppHost().Init();
}
Ok....found the issue....I (in error) installed (via nuget) and referenced within my project the FluentValidation.dll with Service-Stack's FluentValidation implementation (see namespace ServiceStack.FluentValidation).
Once I removed this the sole incorrect FluentValidation reference and ensured that my validator extended from the service-stack implementation of the AbstractValidator the validators fired correctly...
I created a custom RegistrationFeature:
public class CustomRegistrationFeature: IPlugin
{
private string AtRestPath {get; set;}
public CustomRegistrationFeature ()
{
AtRestPath = "/register";
}
public void Register (IAppHost apphost)
{
appHost.RegisterService <CustomRegistrationService>(AtRestPath);
appHost.RegisterAs <CustomRegistrationValidator, IValidator <CustomRegistration>>();
}
}
I configured in AppHost:
Plugins.Add (new CustomRegistrationFeature ());
but in the metadata page there are CustomRegistration and Registration.
Why?
Thanks.
Update
The CustomRegistrationService:
[DefaultRequest(typeof(CustomRegistration))]
public class CustomRegistrationService : RegistrationService
{
public object Post(CustomRegistration request)
{
//base.Post( request);
return new CustomRegistrationResponse();
}
}
The CustomRegistration (Request dto):
[DataContract]
public class CustomRegistration : IReturn<CustomRegistrationResponse>
{
[DataMember]
public string Name{ get; set; }
}
The CustomRegistrationResponse (Response dto):
[DataContract]
public class CustomRegistrationResponse
{
[DataMember]
public string Test { get; set; }
}
The CustomRegistration service should appear although as we can't see the implementation of it, I can't tell if the service has been written correctly or not.
But there's no reason why Registration would appear in the /metadata pages since you haven't registered the RegistrationFeature.