Servicestack routing problems - servicestack

Hi i am new to servicestack
have a problem, with the the routing
i have mate a route
[Route("/Person/{ID}", "GET")]
public class GetPersonByID : IReturn<PersonResponse>
{
public decimal ObjectId { get; set; }
}
[Route("/Organization/{ID}/Person", "GET")]
public class GetPersonByOrganizationId : List<PersonResponse>
{
public decimal ObjectId { get; set; }
}
but then i am trying /Organization/281478302400588/Persons, I am getting a error saying
Unable to bind request
Stacktrace: at ServiceStack.Host.RestHandler.CreateRequest(IRequest httpReq,
IRestPath restPath) at
ServiceStack.Host.RestHandler.ProcessRequestAsync(IRequest httpReq,
IResponse httpRes, String operationName)

You need to ensure that the segment name in the route matches a property in the DTO. So {ID} should be {ObjectId}
In the second request, you should be using IReturn<List<PersonResponse>> rather than inheriting from List<PersonResponse> in your request
[Route("/Person/{ObjectId}", "GET")]
public class GetPersonByID : IReturn<PersonResponse>
{
public decimal ObjectId { get; set; }
}
[Route("/Organization/{ObjectId}/Person", "GET")]
public class GetPersonByOrganizationId : IReturn<List<PersonResponse>>
{
public decimal ObjectId { get; set; }
}
You also note you are trying /Organization/281478302400588/Persons You have used Persons in the request, but the route is Person so either change the request or the route accordingly. (Probably best on the route. i.e. [Route("/Organization/{ObjectId}/Persons", "GET")].
Then ensure in your service you are setting it up similar to this:
public class PersonService : Service
{
public PersonResponse Get(GetPersonByID request)
{
// return new PersonResponse();
}
public List<PersonResponse> Get(GetPersonByOrganizationId request)
{
// return new List<PersonResponse>();
}
}
I hope that helps.

Related

Custom RazorPage get access to IRequest and Route Info

Using the Razor implementation for ServiceStack and AspNetCore how can I get access to the IRequest to get the route info for a custom razor page? Ultimately I want to get to the Name attribute on the route if possible.
public abstract class CustomView : RazorPage
{
public IRequest Req { get; set; } // always null
protected Breadcrumb Breadcrumb
{
get
{
return new Breadcrumb(this.Req); // need to pass IRequest to breadcrumbs so it can produce them
}
}
}
Routes are defined with a custom attribute which inherits from Route.
[CustomRoute("/message/{id}", View = "MessageDetailView", Name = "GetById")]
Custom Route:
public class CustomRoute : RouteAttribute
{
public string Name { get; set; }
public string View { get; set; }
public CustomRoute(string path) : base(path) { }
public CustomRoute(string path, string verbs) : base(path, verbs) { }
}
It seems that at least in web applications (as opposed to self hosted) the following works:
IRequest req = HostContext.TryGetCurrentRequest();

ServiceStack "Handler for request not found" when it is working for dozens of similar DTOs

I have been using ServiceStack for months now. It has been working great for awhile and I've used many advanced approaches and Redis integration. I have a license, so my issue is not regarding a license issue, but I wonder if it is related. It almost looks like I have hit a maximum of DTO or paths, but I do not get any such error, simply the "Handler for request not found". So here is my question: how can you debug and isolate this error? I have read all the posts I can find on proper formats for DTO and DTO filters and I have been doing this long enough that I can see nothing wrong in this regard. Identically styled DTO's and paths work, but new ones fail, or so it seems. Even if I find there is something I am doing wrong in my DTO setup, the question remains, is there a way to debug this? Of course, finding what I'm doing wrong, if that is the case, is the first question.
Here is my code, AppHost first:
.Add<UsersCredentials>("/userscredentials", "GET")
.Add<UserCredential>("/userscredentials", "DELETE")
.Add<UserCredential>("/userscredentials/{UserName}", "POST PUT DELETE")
.Add<UserCredential("/userscredentials/{UserName}/(Permissions}/{System}/{ParamSet}/{Instrument}/{Interval}", "POST PUT DELETE")
DTO:
[Route("/userscredentials", "GET")]
public class UsersCredentials : IReturn<UsersCredentials>
{
public string UserName { get; set; }
public string Permissions { get; set; }
public string System { get; set; }
public uint ParamSet { get; set; }
public string Instrument { get; set; }
public uint Interval { get; set; }
} //Request DTO
[Route("/userscredentials", "DELETE")]
[Route("/userscredentials/{UserName}", "POST PUT DELETE")]
[Route("/userscredentials/{UserName}/(Permissions}/{System}/{ParamSet}/{Instrument}/{Interval}", "POST PUT DELETE")]
public class UserCredential : IReturn<UserCredential>
{
public string UserName { get; set; }
public string Permissions { get; set; }
public string System { get; set; }
public uint ParamSet { get; set; }
public string Instrument { get; set; }
public uint Interval { get; set; }
} //Request DTO
And Service:
// UsersCredentials
public class UsersCredentialsResponse
{
public string Result { get; set; }
public ResponseStatus ResponseStatus { get; set; } //Where Exceptions get auto-serialized
}
public class UsersCredentialsService : Service
{
private bool init = false;
public object Get(UsersCredentials request)
{
return (request);
}
public object Post(UserCredential request)
{
return request;
}
public object Put(UserCredential request)
{
return request;
}
public void Delete(UserCredential request)
{
}
}
I use "POSTMAN" for debug and send this as a POST:
http://sun:1300/userscredentials/a?format=json
It works. Then I send as POST:
http://sun:1300/userscredentials/a/b/c/1/d/2?format=json
and get, "Handler for Request not found: Request.HttpMethod: POST Request.PathInfo: /userscredentials/a/b/c/1/d/2 Request.QueryString: format=json Request.RawUrl: /userscredentials/a/b/c/1/d/2?format=json"
Routing:
You shouldn't be defining the routes in the AppHost using the .Add<T> method as well as using [Route("/route", "METHOD")] on the DTO.
You only need to use one method. So this may cause conflict, and certainly extra maintenance. I recommend using just the latter, of the Route attribute. So remove the Add rules from your AppHost as they are covered by the DTO routes.
You should also read the routing documentation here, and this post about routing also.
Typo:
You have a typo in your route code. You have an incorrect bracket ( instead of {:
(Permissions}
Should be:
{Permissions}
Metadata
An excellent place to check the service is defined properly is by checking the applications Metadata feature. This is enabled by default, so you can do this by adding /metadata to your server url. i.e.
http://localhost:{port}/metadata
You can see an example metadata page here
Hope that helps.

ServiceStack and FluentValidation NOT firing

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...

ServiceStack registration

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.

ServiceStack not receiving values on OnDelete

On OnDelete of ServiceStack, it is called but the values are empty.
I tried to check the value, e.g.
ProductRequestResponse rx = Client.Send<ProductRequestResponse>(
"DELETE", "http://localhost:2012/api/product_request",
new ProductRequest { Id = 7 });
On the ServiceStack side, I only receive an Id of 0. Here's my StackService OnDelete method.
public override object OnDelete(ProductRequest request)
{
throw new Exception("Id: " + request.Id.ToString());
}
Here's my objects use for communication
public class ProductRequest
{
public int Id { get; set; }
public ProductDto ProductDto { get; set; }
}
public class ProductRequestResponse
{
public ProductDto ProductDto { get; set; }
public IEnumerable<ProductDto> ProductDtos { get; set; }
public ServiceStack.ServiceInterface.ServiceModel.ResponseStatus ResponseStatus { get; set; } //Where Exceptions get auto-serialized
}
What am I missing, why StackService is not receiving any value from OnDelete method?
Firstly, you should be using the Delete method as the Send only does POST's:
So it looks something like:
restClient.Delete<TransactionLogResponse>("/transactionlog");
The reason why Delete doesn't expect a Request DTO is because the DELETE Http verb does not accept a request body.
If you want to add paramaters you should add this on the route path or query string, e.g:
restClient.Delete<TransactionLogResponse>("/transactionlog/1?Arg1=a&Arg2=b");

Resources