Because the HandlerFactoryPath
is only meant for ASP.NET hosts to be able to properly calculate the BaseUrl for when ServiceStack is hosted at a /custom
path in an ASP.NET Web App. ServiceStack’s hosted from the BaseUrl which is its root for everything, services, razor views, static files, etc. ServiceStack doesn’t see any request that comes before the BaseUrl so it can’t be configured to listen on http://localhost:8080/api/
and serve any requests from http://localhost:8080/
.
ServiceStack also doesn’t distinguish between a Service that returns JSON or when it’s used to populate the View Model of a Razor View when requested by a browser, they’re just different JSON or HTML formats of a Service Response.
If you want to ServiceStack to serve requests from both http://localhost:8080/
and http://localhost:8080/api/
it needs to be hosted from http://localhost:8080/
then use Custom Routes to specify which Services should be prefixed with /api
.
If you also want to remap your Assign Roles Services you’ll need to specify the new routes for them as well, e.g:
Plugins.Add(new AuthFeature {
//...
ServiceRoutes = new Dictionary<Type, string[]> {
{typeof(AuthenticateService), new[] { "/api/auth", "/api/auth/{provider}" }},
{typeof(AssignRolesService), new[] { "/api/assignroles" }},
{typeof(UnAssignRolesService), new[] { "/api/unassignroles" }},
}
}
You can’t change the built-in Pre-defined Routes because they need to be predictable from external clients from the BaseUrl independent of any Server configuration.
Personally I wouldn’t prefix and segregate my JSON API’s with /api
, places I’ve seen this convention used is due to an artificial limitation imposed by Web API and MVC or ServiceStack and MVC being different implementations where hosting at different paths is the only way to make both work within the same Web Application. In a REST-ful Service you’d typically wouldn’t have different routes for /customers
to return a Web Page for Browsers and a different Route to return a JSON Response to JSON clients, the client would just use the Accept Header to specify which format it wants returned.
But if for some reason I had to segregate Services between /
and /api
, I would be manually adding the prefix in each Route definition e.g, [Route("/api/customers")]
to make it clear which explicit route it’s available from. You can use GetRouteAttributes()
to save some typing, but the external route mapping is the declarative endpoint for your Service and not something I’d want to save typing and hide behind a Startup script.