Documentation

Getting started

Add the dependency:

.NET Core CLI
dotnet add package Bemol

or copy to csproj:

XML
<ItemGroup>
    <PackageReference Include="Bemol" Version="1.0.0-alpha" />
</ItemGroup>

Start coding:

C#
using Bemol;

class HelloWorld {
    static void Main() {
        App app = new App().Start(7000);
        app.Get("/", (ctx) => ctx.Result("Hello World!"));
    }
}

You can run the development server with:

.NET Core CLI
dotnet watch run

Routing

A route is made up of three simple pieces:

C#
app.Get("/", ctx => {
    // Show something
});

app.Post("/", ctx => {
    // Create something
});

app.Put("/", ctx => {
    // Update something
});

app.Delete("/", ctx => {
    // Annihilate something
});

app.Head("/", ctx => {
    // Show header
});

Methods

There exists two special methods, before and after.

A before method is matched before every request.

C#
app.Before(ctx => {
    // runs before all requests
});
app.Before("/path/*", ctx => {
    // runs before request to /path/*
});

An after method is matched after every request (even if an exception occurred).

C#
app.After(ctx => {
    // run after all requests
});

app.After("/path/*", ctx => {
    // runs after request to /path/*
});

You can also add your own custom methods:

C#
app.Custom("TRACE", "/", ctx => {
    // Trace something
});

Paths

Paths are matched in the order they are defined.

Paths can include named parameters, accessible via ctx.PathParam("key"):

C#
app.Get("/hello/:name", ctx => {
    ctx.Result("Hello: " + ctx.PathParam("name"));
});

Paths can also include wildcard parameters, which are like unnamed path-parameters:

C#
app.Get("/hello/*", ctx => {
    // capture all request to sub-paths of /hello/
});

Context (ctx)

The Context object provides you with everything you need to handle a http-request.

C#
// Request methods
ctx.Body()                              // get body as string
ctx.Body<T>()                           // get body as type
ctx.BodyAsBytes()                       // get body as bytes
ctx.UploadedFile(name)                  // get uploaded file by name
ctx.FormParam(key)                      // get form parameter
ctx.FormParam<T>(key)                   // get form parameter as type
ctx.FormParam(key, default)             // get form parameter (or default value)
ctx.FormParam()                         // get form parameter collection
ctx.PathParam(key)                      // get path parameter
ctx.PathParam<T>(key)                   // get path parameter as type
ctx.ContentLength()                     // get request content length
ctx.ContentType()                       // get request content type
ctx.Cookie(name)                        // get request cookie
ctx.Header(header)                      // get request header
ctx.Header()                            // get request header collection
ctx.Ip()                                // get request ip address
ctx.Method()                            // get request method
ctx.Path()                              // get request path
ctx.QueryParam()                        // get request query parameter
ctx.QueryParam<T>()                     // get request query parameter as type
ctx.QueryMap()                          // get request query paramaeter as collection
ctx.QueryString()                       // get query string
ctx.UserAgent()                         // get user agent

// Response methods
ctx.Result(resultString)                // set a string result that will be sent to the client
ctx.ResultString()                      // get the string result that will be sent to the client
ctx.Result(byteArray)                   // set a byte[] result that will be sent to the client
ctx.ResultBytes()                       // get the byte[] result that will be sent to the client
ctx.ResultStream()                      // get the stream that will be sent to the client
ctx.ContentType(contentType)            // set the response content type
ctx.Header(name, value)                 // set a response header
ctx.Status(statusCode)                  // set response status
ctx.Status()                            // get response status
ctx.Redirect(path)                      // redirect to path
ctx.Redirect(path, statusCode)          // redirect to path with specific HTTP 3XX status code
ctx.Cookie(name, value)                 // set cookie by name and value
ctx.Cookie(cookie)                      // set cookie
ctx.RemoveCookie(name)                  // remove cookie
ctx.Html(html)                          // call Result(string).ContentType("text/html")
ctx.Json(obj)                           // call Result(JsonSerializer.Serialize(obj)).ContentType("application/json")
ctx.Render(filePath, model)             // call Html(BemolRenderer.Render(filePath, model))

Error Mapping

All handlers (before, path, after) can throw HttpException (and any subclass of HttpException):

C#
using Bemol.Http.Exception;

app.Get("/error", ctx => {
    throw new NotFoundException();
});

The app.Error() method gives you a way to handle those:

C#
app.Error(404, ctx => {
    ctx.Result("Generic 404 message");
});

Configuration

You can pass a config object when creating a new instance of App. The below snippets shows all the available config options:

C#
App(config => {
    config.PublicFolder = publicFolder              // public folder used to serve static files (default is "/public")
    config.ContextPath = contextPath                // context path for the http server (default is "/")
    condig.DefaultContentType = contentType         // content type to use if no content type is set (default is "text/plain")
    config.IgnoreTrailingSlashes = true/false       // default is true
    condig.EnableCorsForAllOrigins = true/false     // enable cors for all origins

    // DotLiquid
    config.ResourcesFolder = resourcesFolder        // resources folder for DotLiquid (default is "/resources")
    config.PartialsFolder = partialsFolder          // partials folder for DotLiquid (default is "/partials")
})

Static Files

To include static files in your build:

XML
<ItemGroup> 
  <Watch Include="public/**" />
  <None Include="public/**"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
  </None> 
</ItemGroup>

Static resource handling is done after endpoint matching, meaning your self-defined endpoints have higher priority. The process looks like this:

before handlers
endpoint handler
if no endpoint handler found
    static file handler
    if no static file found
        response is 404
after handlers

If you do config.PublicFolder = "/public". Your index.html file at /public/index.html will be available at http://{host}:{port}/index.html and http://{host}:{port}/.