PageTemplate, Service and default route

What is the best way to configure a .net core 2.0 web app that uses PageTemplates to assign a Model to the index.html page / default route via a Service.

See the FallbackRoute in the new .NET Core 2.0 SPA Templates.

[FallbackRoute("/{PathInfo*}")]
public class FallbackForClientRoutes
{
    public string PathInfo { get; set; }
}

public class MyServices : Service
{
    //Return index.html for unmatched requests so routing is handled on client
    public object Any(FallbackForClientRoutes request) => 
        new PageResult(Request.GetPage("/"));
}

Also your index.html Template can call Services, other filters, etc.

Thanks! But it seems to catch all requests, even wrong includes to for example a javascript (that also returns the default index page)…

The purpose of the Fallback Route is to catch all non-matching requests and in this case evaluate the index.html as that’s how SPA’s are able to handle routing on the client (which are unknown on the server).

Requests for static files return the files.

FYI I’ve added a new MatchRoute feature (available from latest v5 on MyGet) that will now let you specify a Custom Rule that routes need to match, so you can now specify that the [FallbackRoute] should only handle routes from clients which explicitly accepts HTML with:

[FallbackRoute("/{PathInfo*}", Matches="AcceptsHtml")]
public class FallbackForClientRoutes
{
    public string PathInfo { get; set; }
}

The AcceptsHtml rule matches a built-in RequestRule below:

SetConfig(new HostConfig {
  RequestRules = {
    {"AcceptsHtml", req => req.Accept?.IndexOf(MimeTypes.Html,) >= 0 },
    {"AcceptsJson", req => req.Accept?.IndexOf(MimeTypes.Json) >= 0 },
    {"IsMobile", req => Instance.IsMobileRegex.IsMatch(req.UserAgent) },
    {"**/{int}", req => int.TryParse(req.PathInfo.LastRightPart('/'), out _) },
    {"{int}/**", req => int.TryParse(req.PathInfo.Substring(1).LeftPart('/'),out _) },
    {"path/{int}/**", req => {
        var afterFirst = req.PathInfo.Substring(1).RightPart('/');
        return afterFirst?.Length > 0 && int.TryParse(afterFirst.LeftPart('/'),out _);
    }},
  }
});

Which only matches when clients specifically request the text/html Content Type, so it wan’t be called for unknown image requests.

The other built-in rules lets you have the same Route call different Service depending on whether the Path Segment is an integer or not, e.g

// matches /users/1
[Route("/users/{Id}", Matches = "**/{int}")]
public class GetUser
{
    public int Id { get; set; }
}

// matches /users/username
[Route("/users/{Slug}")]
public class FindUser
{
    public string Slug { get; set; }
}

It also lets you create rules using Regex with the format {IHttpRequest.Field} =~ {pattern} which you can now use instead of the above:

// matches /users/1
[Route("/users/{Id}", Matches = @"PathInfo =~ \/[0-9]+$$")]
public class GetUser
{
    public int Id { get; set; }
}

Nice! Thanks.
What is the optimal way to combine servicestack html template with asp.net core (without using anything from the mvc/routing from asp.net)?
Currently i’ve got this:

  public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime) {
  	loggerFactory.AddSerilog();
  	app.UseStaticFiles(new StaticFileOptions {
  		ServeUnknownFileTypes = true
  	});
  	app.UseServiceStack(new AppHost(Modules.FindPlugins()));
  	if (env.IsDevelopment()) {
  		Debug = true;
  		app.UseDeveloperExceptionPage(); ????
  	} else {
  		//app.UseExceptionHandler("/Error"); ????
  	}
  	app.Use(new RequestInfoHandler().Middleware); ???
  	// Ensure any buffered events are sent at shutdown
  	appLifetime.ApplicationStopped.Register(Serilog.Log.CloseAndFlush);
  }

Have a look at the .NET Core 2.0 Templates Bootstarter project.

So you’re advice is to let servicestack do the handling of every request, also for requests like css, img and javascript files. (even when servicestack is hosted in IIS).

It’s most optimal to serve static files from a CDN, followed by natively from a webserver (e.g. nginx/IIS), then by your .NET App. It requires less configuration to let ServiceStack handle static files, so it’s my preference.