Layouting
Layouts are a basic mechanism that allows you to assemble your web application fromdifferent parts. They define which URLs are made available by the server and whatthe URL structure of your app will look like.
The following example will expose a single file to your clients which will be availablevia the URLhttp://localhost:8080/hello.txt. For any other URL, the server will generatea404 Not Found
response.
var app = Layout.Create() .Add("hello.txt", Content.From(Resource.FromString("Hello World")));await Host.Create() .Handler(app) .RunAsync();
In this example, we create a segment namedhello.txt
and let it be handled by theContent
handler.
Sub Sections
Layouts can contain another layout for a specific path. This way your web application can be structuredas needed. The following example exposeshttp://localhost:8080/static/hello.txt to your clients.
var resources = Layout.Create() .Add("hello.txt", Content.From(Resource.FromString("Hello World")));var app = Layout.Create() .Add("static", resources);await Host.Create() .Handler(app) .RunAsync();
This feature is typically used to describe your project topology on a high level andto glue everything together. The following example will give you an idea on how thiscould be used in a more complex app.
var app = Layout.Create() .Add("api", ...) .Add("static", ...) .Add("admin-area", ...);await Host.Create() .Handler(app) .RunAsync();
Multiple Segments
To create a nested path structure (such as/api/v1/
) you can nest layouts within layouts.To make this easier, there is an extension method that accepts an array of segments and internally creates additional layouts as needed:
var api = Layout.Create() .AddService<...>("...");var app = Layout.Create() .Add(["api","v1"], api);await Host.Create() .Handler(app) .RunAsync();
Alternatively, you can also write.Add("/api/v1/", api)
which is less type safe yet easier to understand.
Imperative Flow
Instead of creating and adding new layouts for each sub section yourself, you can also directly create a new layoutby calling theAddSegment()
method on an existing builder. This flavor feels more imperative and might suit some projectsetup procedures better than the functional one.
var app = Layout.Create();var api = app.AddSegment("api");// or AddSegments([ "api", "v1" ])api.AddService<SomeService>("some");
Fallbacks
When adding handlers without a segment name, they will be called if no other registered handler was ableto generate a response. In the following example,http://localhost:8080/file/will returnFile Contents
whereas any other URL will returnFallback Content
.
var app = Layout.Create() .Add("file", Content.From(Resource.FromString("File Content"))) .Add(Content.From(Resource.FromString("Fallback Content")));await Host.Create() .Handler(app) .RunAsync();
If there are multiple routes set on a layout, the implementation will invoke them one-by-one until itretrieves a non-null response. In the following example, the server will render the “Hello World”response only, if the request file does not exist in the given directory.
var tree = ResourceTree.FromDirectory("...");var files = Resources.From(tree);var app = Layout.Create() .Add(files) .Add(Content.From(Resource.FromString("Hello World")));await Host.Create() .Handler(app) .RunAsync();
Index
While theAdd()
method matches all routes that start with the given name (e.g./hello.txt/appendix
in the first example of this document), theIndex()
method only matches the current root. The following examplewill renderHello World
whenhttp://localhost:8080/ is requested, but returns404 Not Found
for any other URL.
var app = Layout.Create() .Index(Content.From(Resource.FromString("Hello World")));await Host.Create() .Handler(app) .RunAsync();
Extensions
Some modules extend the layout builder to reduce the boilerplate code requiredto add handlers or concerns to your application.
For example the webservice module allows to directly add a webservice handler.
var api = Layout.Create() .AddService<MyServiceClass>("service");
which is a shortcut for
var service = ServiceResource.From<MyServiceClass>();var api = Layout.Create() .Add("service", service);
As those methods are defined in extension methods provided by the source module,this requires you to add an using directive, in this caseusing GenHTTP.Modules.Webservices
.
Non-Root Layouts
While it makes sense to start with a layout on root level to define the structureof your application, it is just an ordinary handler and your server instancejust requires any handler to work with.
This means that you do not need to use a layout as a root handler and can addlayouts anywhere where handlers are supported.
The following example uses the virtual host handler to serve to different kind ofapps to your clients:
var app1 = Layout.Create();var app2 = Layout.Create();var app = VirtualHosts.Create() .Add("domain1.com", app1) .Add("domain2.com", app2);await Host.Create() .Handler(app) .RunAsync();