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();
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);
}
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.
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> { .. }
I just start to learn REST and ServiceStack and there's something about Route that I just can't quite understand. For example if we take the very basic HelloWorld example from GitHub tutorial and re-write it to return collection of User objects. Here is example:
public User
{
public string Name;
public string Address;
public int Age;
}
// Hello - request object without [Route] attribute
public class Hello
{
public string Name { get; set; }
}
public class HelloResponse
{
public IEnumerable<User> Result {get;set;}
}
public class HelloService : Service
{
public object Any(Hello request)
{
return new HelloResponse { // Collection of User object };
}
}
now everything working right and no problems here. But now I want to add another routing url like: /Hello/{name}/Address
Actually this call (GET) to this url will return a single User selected by Age parameter. How I can do this ? Should I add another Service ? And if the url will be:
/Hello/{name}/{age}/Address
It seems I don't understand something.....
See this earlier answer for details about Routing in ServiceStack. The Smart Routing section in ServiceStack's New API explains further options and different precedence.
There are a few problems with your example. First ServiceStack text serializers only support public properties so you need to change your User Model to use public properties instead of fields, e.g:
public User
{
public string Name { get; set; }
public string Address { get; set; }
public int Age { get; set; }
}
Next, Interfaces on DTOs are a bad idea as there's no good reason for it. They're still supported but you can end up with undesirable results. Use a concrete collection like a List<T> which provides more utility, e.g:
public class HelloResponse
{
public List<User> Results { get; set; }
}
Also the routes should match the property names on your DTO exactly, they are case-insensitive when matching against the Request Path, but they need to map to an exact property name, e.g:
/Hello/{Name}/{Age}/Address
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.