.NET handler reference
Handler contract for Coderunner .NET functions, background jobs, and scheduled jobs.
Applies to the function, job, and schedule workload types — code you upload as a ZIP and Code Runner executes against a .NET bootstrap image (portal runtime slug dotnet; dotnet-script is a separate, lighter-weight catalog entry). For a service (any container, your own Dockerfile), see Deploy a container image instead — there's no handler contract there, just a container listening on a port.
Project structure
my-coderunner/
├── Handler.cs
└── CodeRunner.Function.csprojCode Runner invokes Handler.Handler.Execute as the entry point — a static Execute method inside namespace Handler, class Handler.
Handler contract
namespace Handler;
public static class Handler
{
public static async Task<object> Execute(
Dictionary<string, object?> context,
Dictionary<string, object?> payload)
{
// ...
}
}| Type | Signature | Return value |
|---|---|---|
function | Task<object> Execute(context, payload) | An object with statusCode, body, headers — required. A function is a request/response HTTP handler; whatever you return becomes the response. |
job / schedule | Task Execute(context, payload) (no <object>) | None required — the process runs to completion and exits. Log via Console.WriteLine; there's no response to return. |
payload is the JSON input supplied by the caller (function) or trigger (job/schedule) as a Dictionary<string, object?>. context is runtime-provided; no properties are documented yet beyond its presence in the signature — don't depend on undocumented context fields.
Minimal example (function)
namespace Handler;
public static class Handler
{
public static async Task<object> Execute(
Dictionary<string, object?> context,
Dictionary<string, object?> payload)
{
if (payload == null || !payload.TryGetValue("name", out var name) || name == null)
{
return new
{
statusCode = 400,
body = new { error = "Name is required" },
headers = new { content_type = "application/json" },
};
}
return new
{
statusCode = 200,
body = new { message = $"Hello {name}" },
headers = new { content_type = "application/json" },
};
}
}A job/schedule handler drops the return type to plain Task — do the work, log, and let the method end:
namespace Handler;
public static class Handler
{
public static async Task Execute(
Dictionary<string, object?> context,
Dictionary<string, object?> payload)
{
try
{
Console.WriteLine("Background job started");
await DoSomething(payload);
Console.WriteLine("Background job completed successfully");
}
catch (Exception err)
{
Console.WriteLine($"Background job failed: {err.Message}");
}
}
}Project file and NuGet packages
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
</ItemGroup>
</Project>Environment variables and secrets
Environment.GetEnvironmentVariable("NAME") reads deploy-time env vars (gf deploy -e KEY=value) and injected secrets (gf deploy --secret KEY, see Secrets). Never hard-code API keys, passwords, or connection strings — and never zip a .env file.
Common status codes (function)
| Code | Usage |
|---|---|
200 | Successful execution |
201 | Resource created |
400 | Invalid or missing input |
401 / 403 | Auth required / denied |
404 | Not found |
500 | Unexpected error — don't leak exception internals in the body |
Best practices
- Structure the entry point exactly as
namespace Handler/class Handler/ staticExecute. - Use
async/await; checkresponse.IsSuccessStatusCodeon outbound HTTP calls. - Validate
payloadbefore using it — it's caller-supplied and untyped. - Catch exceptions inside
Executeso a fault doesn't crash-loop the container. - Keep secrets out of the ZIP — inject them at deploy time.
Related
- Coderunner — workload types and lifecycle
- Deploy a container image — the
servicetype instead - Node.js handler reference · Python handler reference