This project offers templates for OpenFaaS that make use of ASP.NET. The templates allow more control over the request and a better handling of the response by using the ASP.NET framework. Both C# and F# templates are available.
NOTICE: I've stopped using OpenFaaS and as such, I'm no longer maintaining this project. I've moved all my workloads to Knative and JustFaaS, which don't require the usage of templates.
Learn more about OpenFaaS
Just pull the templates with the faas CLI.
faas-cli template pull https://github.com/goncalo-oliveira/faas-aspnet-template
If you are upgrading, use the flag --overwrite
to write over the existing templates.
If you want to install a previous version, you can still do it by referencing the release tag.
faas-cli template pull https://github.com/goncalo-oliveira/faas-aspnet-template#v1.5.2
After installing, create a new function with the aspnet
template. This will generate a C# template. If you want to create an F# template instead, use the aspnet-fsharp
template.
faas-cli new --lang aspnet <function-name>
A single Program.cs
file is generated when you create a new function with this template. This is because by default, a minimal API structure is used. Let's have a look at the contents:
Runner.Run( args, builder =>
{
// add your services to the container
}, app =>
{
// configure the HTTP request pipeline
app.MapPost( "/", () =>
{
return new
{
Message = "Hello"
};
} );
} );
If what you are building is a micro-api or if you rather use a Startup.cs
and the Generic Host model, you can update Program.cs
with the following code
Runner.Run( args, typeof( OpenFaaS.Startup ) );
Then, you can either add a minimal Startup.cs
file, which looks like this
namespace OpenFaaS
{
public class Startup
{
public void Configure( WebApplicationBuilder builder )
{
// configure the application builder
}
public void Configure( WebApplication app )
{
// configure the HTTP request pipeline
}
}
}
Or you can add a standard Startup.cs
file
namespace OpenFaaS
{
public class Startup
{
public Startup( IConfiguration configuration )
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices( IServiceCollection services )
{
// add your services to the container
}
public void Configure( IApplicationBuilder app, bool isDevelopmentEnv )
{
// configure the HTTP request pipeline
}
}
}
Since you're not using a minimal API anymore, you'll also need to add at least one controller class.
Secrets that the function has access to are loaded into the configuration model. They are prepended with the prefix _secret_
. For example, a secret named my-secret-key
can be accessed with the configuration key _secret_my-secret-key
.
NOTE: The value of the secret is read as a byte array and then stored as a base64 string.
You can also use the extension methods GetSecret
and GetSecretAsString
.
IServiceCollection services = ...
services.AddMyService( options =>
{
options.ApiKey = Configuration.GetSecretAsString( "my-api-key" );
} );
The JSON serializer from Microsoft is used by default. This means that there is limited support for polymorphic serialization and deserialization is not supported at all. You can find more in this article where you will also find a few workarounds, including writing a custom converter.
If you rather use Newtonsoft's Json.NET, you still can. Add the package OpenFaaS.Runner.NewtonsoftJson
and use the function builder to extend the functionality
IServiceCollection services = ...
services.ConfigureFunction()
.AddNewtonsoftJson();
If you need to customize the function's behaviour, such as Json serialization options or routing options for example, you can use the function builder to extend functionality
IServiceCollection services = ...
services.ConfigureFunction() // returns an IFunctionBuilder
.ConfigureJsonOptions( options =>
{
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
} )
.ConfigureRouteOptions( options =>
{
options.LowercaseUrls = true;
} );
The templates are using BuildKit syntax since v2.x, so you'll need to make sure you are using it. This can be done with an environment variable
# for the current terminal session
export DOCKER_BUILDKIT=1
# for the current terminal command
DOCKER_BUILDKIT=1 faas-cli build -f hello.yml
To enable BuildKit by default, we can set the feature to true on the daemon configuration (usually /etc/docker/daemon.json) and restart the daemon.
{
"features":
{
"buildkit": true
}
}
If your function has packages from private repositories, you'll need to provide a nuget configuration file to the image build process. On previous versions this could be done with build arguments, but that is considered insecure. Since version 2.x this can only be done with secrets (and BuildKit).
The OpenFaaS CLI doesn't seem yet to support this, therefore, this can only be done with the Docker CLI (or BuildKit standalone).
You'll need a NuGet.Config
file. Let's consider we have one at /home/user/.nuget/NuGet/NuGet.Config
. We just need to pass the file as a secret with the docker build; the name of the secret has to be nuget.config
.
Since faas-cli
doesn't yet support this functionality, we have to shrinkwrap our function.
faas-cli build -f hello.yml --shrinkwrap
The above prepares our function in the ./build/
folder. Now we can use Docker CLI.
docker build -t hello --secret id=nuget.config,src=/home/user/.nuget/NuGet/NuGet.Config build/hello
Currently, the passwords on the configuration file need to be stored in clear text. If you are on Windows, this won't be the case for the
NuGet.Config
on your computer.
Version 2.x brings better performance and less friction with dependencies by dropping the usage of faas-run
. Unless there's a particular reason for not doing so, it is recommended to upgrade your functions to the newer templates.
The templates from v1.x included two C# projects; one that derives from IHttpFunction
, designed to execute a single action (pure functions) and another one that derives from ControllerBase
, designed to support micro-apis. In total, there were three templates
- aspnet (IHttpFunction)
- aspnet-controller (ControllerBase)
- aspnet-fsharp (IHttpFunction)
The IHttpFunction
interface disappeared in version 2.x. Functions are either directly mapped using a minimal API (great for pure functions) or they implement one or more controllers, derived from ControllerBase
(better choice for micro-apis). This applies to both C# and F# templates; so now, there are only two templates (aspnet and aspnet-fsharp).
If your current function derives from IHttpFunction
, the recommended course of action is to migrate to a minimal API function (which is what the template generates by default).
The project file also suffered some changes; it is now an executable and not a library, and replaced OpenFaaS.Functions
with OpenFaaS.Runner
.
The easiest way is to create a separate hello project to serve as a reference as you make the changes. Nonetheless, here's what the function project file looks like
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>OpenFaaS</RootNamespace>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="OpenFaaS.Runner" Version="2.0.2" />
</ItemGroup>
</Project>
If you're going for a minimal API (your current function derives from IHttpFunction
), the Startup.cs
and the Function.cs
files disappear. Instead, you'll have a Program.cs
that looks like this
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using OpenFaaS.Hosting;
Runner.Run( args, builder =>
{
// your Startup.cs > ConfigureServices( IServiceCollection ) goes in here
}, app =>
{
// your Startup.cs > Configure( IApplicationBuilder, bool ) goes in here
// your Function.cs is implemented here
app.MapPost( "/", () =>
{
return new
{
Message = "Hello"
};
} );
} );
If your current function is using the "old" aspnet-controller
template, there are less changes to be made. The Startup.cs
file can be maintained as it is. The same goes for the controller(s). You'll just need to add a Program.cs
file similar to this
using OpenFaaS;
using OpenFaaS.Hosting;
Runner.Run( args, typeof( Startup ) );