nanoFramework.WebServer 1.1.17

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package nanoFramework.WebServer --version 1.1.17
                    
NuGet\Install-Package nanoFramework.WebServer -Version 1.1.17
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="nanoFramework.WebServer" Version="1.1.17" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="nanoFramework.WebServer" Version="1.1.17" />
                    
Directory.Packages.props
<PackageReference Include="nanoFramework.WebServer" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add nanoFramework.WebServer --version 1.1.17
                    
#r "nuget: nanoFramework.WebServer, 1.1.17"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package nanoFramework.WebServer@1.1.17
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=nanoFramework.WebServer&version=1.1.17
                    
Install as a Cake Addin
#tool nuget:?package=nanoFramework.WebServer&version=1.1.17
                    
Install as a Cake Tool

Quality Gate Status Reliability Rating License NuGet #yourfirstpr Discord

nanoFramework logo


Welcome to the .NET nanoFramework WebServer repository

Build status

Component Build Status NuGet Package
nanoFramework.WebServer Build Status NuGet

.NET nanoFramework WebServer

This library was coded by Laurent Ellerbach who generously offered it to the .NET nanoFramework project.

This is a simple nanoFramework WebServer. Features:

  • Handle multi-thread requests
  • Serve static files on any storage
  • Handle parameter in URL
  • Possible to have multiple WebServer running at the same time
  • supports GET/PUT and any other word
  • Supports any type of header
  • Supports content in POST
  • Reflection for easy usage of controllers and notion of routes
  • Helpers to return error code directly facilitating REST API
  • HTTPS support
  • URL decode/encode

Limitations:

  • Does not support any zip in the request or response stream

Usage

You just need to specify a port and a timeout for the queries and add an event handler when a request is incoming. With this first way, you will have an event raised every time you'll receive a request.

using (WebServer server = new WebServer(80, HttpProtocol.Http)
{
    // Add a handler for commands that are received by the server.
    server.CommandReceived += ServerCommandReceived;

    // Start the server.
    server.Start();

    Thread.Sleep(Timeout.Infinite);
}

You can as well pass a controller where you can use decoration for the routes and method supported.

using (WebServer server = new WebServer(80, HttpProtocol.Http, new Type[] { typeof(ControllerPerson), typeof(ControllerTest) }))
{
    // Start the server.
    server.Start();

    Thread.Sleep(Timeout.Infinite);
}

In this case, you're passing 2 classes where you have public methods decorated which will be called every time the route is found.

With the previous example, a very simple and straight forward Test controller will look like that:

public class ControllerTest
{
    [Route("test"), Route("Test2"), Route("tEst42"), Route("TEST")]
    [CaseSensitive]
    [Method("GET")]
    public void RoutePostTest(WebServerEventArgs e)
    {
        string route = $"The route asked is {e.Context.Request.RawUrl.TrimStart('/').Split('/')[0]}";
        e.Context.Response.ContentType = "text/plain";
        WebServer.OutPutStream(e.Context.Response, route);
    }

    [Route("test/any")]
    public void RouteAnyTest(WebServerEventArgs e)
    {
        WebServer.OutputHttpCode(e.Context.Response, HttpStatusCode.OK);
    }
}

In this example, the RoutePostTest will be called every time the called url will be test or Test2 or tEst42 or TEST, the url can be with parameters and the method GET. Be aware that Test won't call the function, neither test/.

The RouteAnyTestis called whenever the url is test/any whatever the method is.

There is a more advance example with simple REST API to get a list of Person and add a Person. Check it in the sample.

Important

  • By default the routes are not case sensitive and the attribute must be lowercase
  • If you want to use case sensitive routes like in the previous example, use the attribute CaseSensitive. As in the previous example, you must write the route as you want it to be responded to.

A simple GPIO controller REST API

You will find in simple GPIO controller sample REST API. The controller not case sensitive and is working like this:

  • To open the pin 2 as output: http://yoururl/open/2/output
  • To open pin 4 as input: http://yoururl/open/4/input
  • To write the value high to pin 2: http://yoururl/write/2/high
    • You can use high or 1, it has the same effect and will place the pin in high value
    • You can use low of 0, it has the same effect and will place the pin in low value
  • To read the pin 4: http://yoururl/read/4, you will get as a raw text highor lowdepending on the state

Authentication on controllers

Controllers support authentication. 3 types of authentications are currently implemented on controllers only:

  • Basic: the classic user and password following the HTTP standard. Usage:
    • [Authentication("Basic")] will use the default credential of the webserver
    • [Authentication("Basic:myuser mypassword")] will use myuser as a user and my password as a password. Note: the user cannot contains spaces.
  • APiKey in header: add ApiKey in headers with the API key. Usage:
    • [Authentication("ApiKey")] will use the default credential of the webserver
    • [Authentication("ApiKeyc:akey")] will use akey as ApiKey.
  • None: no authentication required. Usage:
    • [Authentication("None")] will use the default credential of the webserver

The Authentication attribute applies to both public Classes an public Methods.

As for the rest of the controller, you can add attributes to define them, override them. The following example gives an idea of what can be done:

[Authentication("Basic")]
class ControllerAuth
{
    [Route("authbasic")]
    public void Basic(WebServerEventArgs e)
    {
        WebServer.OutputHttpCode(e.Context.Response, HttpStatusCode.OK);
    }

    [Route("authbasicspecial")]
    [Authentication("Basic:user2 password")]
    public void Special(WebServerEventArgs e)
    {
        WebServer.OutputHttpCode(e.Context.Response, HttpStatusCode.OK);
    }

    [Authentication("ApiKey:superKey1234")]
    [Route("authapi")]
    public void Key(WebServerEventArgs e)
    {
        WebServer.OutputHttpCode(e.Context.Response, HttpStatusCode.OK);
    }

    [Route("authnone")]
    [Authentication("None")]
    public void None(WebServerEventArgs e)
    {
        WebServer.OutputHttpCode(e.Context.Response, HttpStatusCode.OK);
    }

    [Authentication("ApiKey")]
    [Route("authdefaultapi")]
    public void DefaultApi(WebServerEventArgs e)
    {
        WebServer.OutputHttpCode(e.Context.Response, HttpStatusCode.OK);
    }
}

And you can pass default credentials to the server:

using (WebServer server = new WebServer(80, HttpProtocol.Http, new Type[] { typeof(ControllerPerson), typeof(ControllerTest), typeof(ControllerAuth) }))
{
    // To test authentication with various scenarios
    server.ApiKey = "ATopSecretAPIKey1234";
    server.Credential = new NetworkCredential("topuser", "topPassword");

    // Start the server.
    server.Start();

    Thread.Sleep(Timeout.Infinite);
}

With the previous example the following happens:

  • All the controller by default, even when nothing is specified will use the controller credentials. In our case, the Basic authentication with the default user (topuser) and password (topPassword) will be used.
    • When calling http://yoururl/authbasic from a browser, you will be prompted for the user and password, use the default one topuser and topPassword to get access
    • When calling http://yoururl/authnone, you won't be prompted because the authentication has been overridden for no authentication
    • When calling http://yoururl/authbasicspecial, the user and password are different from the defautl ones, user2 and password is the right couple here
  • If you would have define in the controller a specific user and password like [Authentication("Basic:myuser mypassword")], then the default one for all the controller would have been myuser and mypassword
  • When calling http://yoururl/authapi, you must pass the header ApiKey (case sensitive) with the value superKey1234 to get authorized, this is overridden the default Basic authentication
  • When calling http://yoururl/authdefaultapi, the default key ATopSecretAPIKey1234 will be used so you have to pass it in the headers of the request

All up, this is an example to show how to use authentication, it's been defined to allow flexibility.

Managing incoming queries thru events

Very basic usage is the following:

private static void ServerCommandReceived(object source, WebServerEventArgs e)
{
    var url = e.Context.Request.RawUrl;
    Debug.WriteLine($"Command received: {url}, Method: {e.Context.Request.HttpMethod}");

    if (url.ToLower() == "/sayhello")
    {
        // This is simple raw text returned
        WebServer.OutPutStream(e.Context.Response, "It's working, url is empty, this is just raw text, /sayhello is just returning a raw text");
    }
    else
    {
        WebServer.OutputHttpCode(e.Context.Response, HttpStatusCode.NotFound);
    }
}

You can do more advance scenario like returning a full HTML page:

WebServer.OutPutStream(e.Context.Response, "<html><head>" +
    "<title>Hi from nanoFramework Server</title></head><body>You want me to say hello in a real HTML page!<br/><a href='/useinternal'>Generate an internal text.txt file</a><br />" +
    "<a href='/Text.txt'>Download the Text.txt file</a><br>" +
    "Try this url with parameters: <a href='/param.htm?param1=42&second=24&NAme=Ellerbach'>/param.htm?param1=42&second=24&NAme=Ellerbach</a></body></html>");

And can get parameters from a URL a an example from the previous link on the param.html page:

if (url.ToLower().IndexOf("/param.htm") == 0)
{
    // Test with parameters
    var parameters = WebServer.decryptParam(url);
    string toOutput = "<html><head>" +
        "<title>Hi from nanoFramework Server</title></head><body>Here are the parameters of this URL: <br />";
    foreach (var par in parameters)
    {
        toOutput += $"Parameter name: {par.Name}, Value: {par.Value}<br />";
    }
    toOutput += "</body></html>";
    WebServer.OutPutStream(e.Context.Response, toOutput);
}

And server static files:

var files = storage.GetFiles();
foreach (var file in files)
{
    if (file.Name == url)
    {
        WebServer.SendFileOverHTTP(e.Context.Response, file);
        return;
    }
}

WebServer.OutputHttpCode(e.Context.Response, HttpStatusCode.NotFound);

And also REST API is supported, here is a comprehensive example:

if (url.ToLower().IndexOf("/api/") == 0)
{
    string ret = $"Your request type is: {e.Context.Request.HttpMethod}\r\n";
    ret += $"The request URL is: {e.Context.Request.RawUrl}\r\n";
    var parameters = WebServer.DecodeParam(e.Context.Request.RawUrl);
    if (parameters != null)
    {
        ret += "List of url parameters:\r\n";
        foreach (var param in parameters)
        {
            ret += $"  Parameter name: {param.Name}, value: {param.Value}\r\n";
        }
    }

    if (e.Context.Request.Headers != null)
    {
        ret += $"Number of headers: {e.Context.Request.Headers.Count}\r\n";
    }
    else
    {
        ret += "There is no header in this request\r\n";
    }

    foreach (var head in e.Context.Request.Headers?.AllKeys)
    {
        ret += $"  Header name: {head}, Values:";
        var vals = e.Context.Request.Headers.GetValues(head);
        foreach (var val in vals)
        {
            ret += $"{val} ";
        }

        ret += "\r\n";
    }

    if (e.Context.Request.ContentLength64 > 0)
    {

        ret += $"Size of content: {e.Context.Request.ContentLength64}\r\n";
        byte[] buff = new byte[e.Context.Request.ContentLength64];
        e.Context.Request.InputStream.Read(buff, 0, buff.Length);
        ret += $"Hex string representation:\r\n";
        for (int i = 0; i < buff.Length; i++)
        {
            ret += buff[i].ToString("X") + " ";
        }

    }

    WebServer.OutPutStream(e.Context.Response, ret);
}

This API example is basic but as you get the method, you can choose what to do.

As you get the url, you can check for a specific controller called. And you have the parameters and the content payload!

Example of a result with call:

result

And more! Check the complete example for more about this WebServer!

Using HTTPS

You will need to generate a certificate and keys:

X509Certificate _myWebServerCertificate509 = new X509Certificate2(_myWebServerCrt, _myWebServerPrivateKey, "1234");

// X509 RSA key PEM format 2048 bytes
        // generate with openssl:
        // > openssl req -newkey rsa:2048 -nodes -keyout selfcert.key -x509 -days 365 -out selfcert.crt
        // and paste selfcert.crt content below:
        private const string _myWebServerCrt =
@"-----BEGIN CERTIFICATE-----
MORETEXT
-----END CERTIFICATE-----";

        // this one is generated with the command below. We need a password.
        // > openssl rsa -des3 -in selfcert.key -out selfcertenc.key
        // the one below was encoded with '1234' as the password.
        private const string _myWebServerPrivateKey =
@"-----BEGIN RSA PRIVATE KEY-----
MORETEXTANDENCRYPTED
-----END RSA PRIVATE KEY-----";

using (WebServer server = new WebServer(443, HttpProtocol.Https)
{
    // Add a handler for commands that are received by the server.
    server.CommandReceived += ServerCommandReceived;
    server.HttpsCert = _myWebServerCertificate509;

    server.SslProtocols = System.Net.Security.SslProtocols.Tls | System.Net.Security.SslProtocols.Tls11 | System.Net.Security.SslProtocols.Tls12;
    // Start the server.
    server.Start();

    Thread.Sleep(Timeout.Infinite);
}

IMPORTANT: because the certificate above is not issued from a Certificate Authority it won't be recognized as a valid certificate. If you want to access the nanoFramework device with your browser, for example, you'll have to add the (CRT file)[WebServer.Sample\webserver-cert.crt] as a trusted one. On Windows, you just have to double click on the CRT file and then click "Install Certificate...".

You can of course use the routes as defined earlier. Both will work, event or route with the notion of controller.

Feedback and documentation

For documentation, providing feedback, issues and finding out how to contribute please refer to the Home repo.

Join our Discord community here.

Credits

The list of contributors to this project can be found at CONTRIBUTORS.

License

The nanoFramework WebServer library is licensed under the MIT license.

Code of Conduct

This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behaviour in our community. For more information see the .NET Foundation Code of Conduct.

.NET Foundation

This project is supported by the .NET Foundation.

Product Compatible and additional computed target framework versions.
.NET Framework net is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories (2)

Showing the top 2 popular GitHub repositories that depend on nanoFramework.WebServer:

Repository Stars
nanoframework/Samples
🍬 Code samples from the nanoFramework team used in testing, proof of concepts and other explorational endeavours
nanoframework/nanoFramework.IoT.Device
📦 This repo includes .NET nanoFramework implementations for various sensors, chips, displays, hats and drivers
Version Downloads Last Updated
1.2.126 356 8/12/2025
1.2.125 265 7/7/2025
1.2.124 162 7/7/2025
1.2.123 173 6/27/2025
1.2.122 273 6/2/2025
1.2.121 324 4/25/2025
1.2.118 337 4/2/2025
1.2.117 197 4/2/2025
1.2.116 541 3/26/2025
1.2.115 515 3/26/2025
1.2.114 532 3/26/2025
1.2.112 250 3/12/2025
1.2.111 260 3/10/2025
1.2.110 207 3/10/2025
1.2.109 219 3/10/2025
1.2.108 232 3/10/2025
1.2.107 273 3/7/2025
1.2.106 203 3/3/2025
1.2.105 290 2/27/2025
1.2.104 162 2/27/2025
1.2.101 193 2/25/2025
1.2.97 165 2/25/2025
1.2.95 162 2/25/2025
1.2.93 338 2/5/2025
1.2.92 167 2/5/2025
1.2.89 192 2/4/2025
1.2.88 187 2/4/2025
1.2.87 182 1/31/2025
1.2.86 174 1/30/2025
1.2.85 164 1/30/2025
1.2.84 155 1/30/2025
1.2.83 134 1/30/2025
1.2.77 171 1/29/2025
1.2.74 284 1/10/2025
1.2.73 182 1/6/2025
1.2.72 185 1/2/2025
1.2.70 321 12/11/2024
1.2.69 144 12/11/2024
1.2.63 567 10/8/2024
1.2.60 239 9/26/2024
1.2.56 293 7/30/2024
1.2.55 233 7/24/2024
1.2.52 342 6/3/2024
1.2.48 246 5/17/2024
1.2.45 160 5/13/2024
1.2.43 250 5/10/2024
1.2.40 297 4/12/2024
1.2.38 186 4/9/2024
1.2.36 174 4/8/2024
1.2.34 200 4/5/2024
1.2.32 178 4/3/2024
1.2.30 180 4/3/2024
1.2.27 402 2/14/2024
1.2.25 172 2/12/2024
1.2.23 286 1/26/2024
1.2.21 175 1/26/2024
1.2.19 169 1/26/2024
1.2.17 214 1/24/2024
1.2.14 517 11/17/2023
1.2.12 226 11/10/2023
1.2.9 195 11/9/2023
1.2.7 230 11/8/2023
1.2.6 170 11/8/2023
1.2.3 322 10/27/2023
1.2.1 245 10/25/2023
1.1.79 270 10/10/2023
1.1.77 252 10/4/2023
1.1.75 503 8/8/2023
1.1.73 357 7/27/2023
1.1.71 225 7/27/2023
1.1.65 779 2/17/2023
1.1.63 501 1/24/2023
1.1.61 373 1/24/2023
1.1.59 436 1/24/2023
1.1.56 526 12/30/2022
1.1.54 419 12/28/2022
1.1.51 435 12/27/2022
1.1.47 829 10/26/2022
1.1.44 497 10/25/2022
1.1.41 493 10/24/2022
1.1.39 539 10/23/2022
1.1.36 546 10/10/2022
1.1.32 520 10/8/2022
1.1.29 584 9/22/2022
1.1.27 549 9/22/2022
1.1.25 552 9/22/2022
1.1.23 599 9/16/2022
1.1.21 601 9/15/2022
1.1.19 620 8/29/2022
1.1.17 638 8/6/2022
1.1.14 551 8/4/2022
1.1.12 518 8/3/2022
1.1.10 555 8/3/2022
1.1.8 539 8/3/2022
1.1.6 725 6/13/2022
1.1.4 609 6/8/2022
1.1.2 540 6/8/2022
1.1.1 585 5/30/2022
1.0.0 814 3/30/2022
1.0.0-preview.260 241 3/29/2022
1.0.0-preview.258 221 3/28/2022
1.0.0-preview.256 241 3/28/2022
1.0.0-preview.254 245 3/28/2022
1.0.0-preview.252 241 3/28/2022
1.0.0-preview.250 220 3/28/2022
1.0.0-preview.248 242 3/17/2022
1.0.0-preview.246 255 3/14/2022
1.0.0-preview.244 228 3/14/2022
1.0.0-preview.242 244 3/14/2022
1.0.0-preview.240 239 3/14/2022
1.0.0-preview.238 227 3/8/2022
1.0.0-preview.236 229 3/8/2022
1.0.0-preview.234 217 3/4/2022
1.0.0-preview.232 237 3/3/2022
1.0.0-preview.230 233 3/2/2022
1.0.0-preview.228 224 2/28/2022
1.0.0-preview.226 265 2/24/2022
1.0.0-preview.222 246 2/17/2022
1.0.0-preview.220 232 2/17/2022
1.0.0-preview.218 286 2/6/2022
1.0.0-preview.216 249 2/4/2022
1.0.0-preview.214 248 2/4/2022
1.0.0-preview.212 267 1/28/2022
1.0.0-preview.210 249 1/28/2022
1.0.0-preview.208 276 1/28/2022
1.0.0-preview.206 237 1/25/2022
1.0.0-preview.204 246 1/21/2022
1.0.0-preview.202 228 1/21/2022
1.0.0-preview.200 239 1/21/2022
1.0.0-preview.198 234 1/21/2022
1.0.0-preview.196 249 1/21/2022
1.0.0-preview.194 254 1/13/2022
1.0.0-preview.192 276 1/12/2022
1.0.0-preview.190 259 1/12/2022
1.0.0-preview.188 237 1/11/2022
1.0.0-preview.186 264 1/11/2022
1.0.0-preview.183 252 1/6/2022
1.0.0-preview.181 262 1/5/2022
1.0.0-preview.180 262 1/3/2022
1.0.0-preview.179 258 1/3/2022
1.0.0-preview.178 243 1/3/2022
1.0.0-preview.177 258 12/30/2021
1.0.0-preview.176 284 12/28/2021
1.0.0-preview.174 307 12/3/2021
1.0.0-preview.172 276 12/3/2021
1.0.0-preview.170 244 12/3/2021
1.0.0-preview.168 271 12/3/2021
1.0.0-preview.166 268 12/3/2021
1.0.0-preview.164 269 12/2/2021
1.0.0-preview.162 283 12/2/2021
1.0.0-preview.160 244 12/2/2021
1.0.0-preview.158 246 12/2/2021
1.0.0-preview.156 265 12/2/2021
1.0.0-preview.154 253 12/2/2021
1.0.0-preview.152 277 12/1/2021
1.0.0-preview.150 240 12/1/2021
1.0.0-preview.148 255 12/1/2021
1.0.0-preview.145 295 11/11/2021
1.0.0-preview.143 309 10/22/2021
1.0.0-preview.141 277 10/18/2021
1.0.0-preview.138 301 10/18/2021
1.0.0-preview.136 412 7/17/2021
1.0.0-preview.134 282 7/16/2021
1.0.0-preview.132 265 7/16/2021
1.0.0-preview.130 290 7/15/2021
1.0.0-preview.128 282 7/14/2021
1.0.0-preview.126 395 6/19/2021
1.0.0-preview.124 357 6/19/2021
1.0.0-preview.122 291 6/17/2021
1.0.0-preview.119 268 6/7/2021
1.0.0-preview.117 275 6/7/2021
1.0.0-preview.115 296 6/7/2021
1.0.0-preview.113 301 6/7/2021
1.0.0-preview.111 312 6/6/2021
1.0.0-preview.109 1,027 6/5/2021
1.0.0-preview.107 287 6/3/2021
1.0.0-preview.105 281 6/2/2021
1.0.0-preview.103 284 6/2/2021
1.0.0-preview.101 293 6/1/2021
1.0.0-preview.99 289 6/1/2021
1.0.0-preview.96 294 6/1/2021
1.0.0-preview.94 314 5/31/2021
1.0.0-preview.92 303 5/30/2021
1.0.0-preview.90 295 5/27/2021
1.0.0-preview.88 291 5/26/2021
1.0.0-preview.86 385 5/23/2021
1.0.0-preview.84 293 5/22/2021
1.0.0-preview.82 351 5/21/2021
1.0.0-preview.80 293 5/19/2021
1.0.0-preview.78 268 5/19/2021
1.0.0-preview.76 288 5/19/2021
1.0.0-preview.71 295 5/15/2021
1.0.0-preview.69 265 5/14/2021
1.0.0-preview.66 278 5/13/2021
1.0.0-preview.64 284 5/11/2021
1.0.0-preview.62 290 5/11/2021
1.0.0-preview.59 339 5/6/2021
1.0.0-preview.57 287 5/5/2021
1.0.0-preview.51 284 4/12/2021
1.0.0-preview.49 295 4/12/2021
1.0.0-preview.47 315 4/10/2021
1.0.0-preview.44 307 4/6/2021
1.0.0-preview.41 282 4/5/2021
1.0.0-preview.32 309 3/21/2021
1.0.0-preview.30 333 3/20/2021
1.0.0-preview.28 312 3/19/2021
1.0.0-preview.26 325 3/18/2021
1.0.0-preview.24 277 3/17/2021
1.0.0-preview.22 274 3/17/2021
1.0.0-preview.20 322 3/5/2021
1.0.0-preview.18 295 3/2/2021
1.0.0-preview.15 544 1/19/2021
1.0.0-preview.13 291 1/19/2021
1.0.0-preview.11 371 1/7/2021
1.0.0-preview.10 327 12/22/2020
1.0.0-preview.6 396 12/1/2020
1.0.0-preview.3 392 11/6/2020