nanoFramework.WebServer.FileSystem 1.2.56

前缀已保留
dotnet add package nanoFramework.WebServer.FileSystem --version 1.2.56                
NuGet\Install-Package nanoFramework.WebServer.FileSystem -Version 1.2.56                
此命令旨在在 Visual Studio 的包管理器控制台中使用,因为它使用 NuGet 模块的 Install-Package 版本。
<PackageReference Include="nanoFramework.WebServer.FileSystem" Version="1.2.56" />                
对于支持 PackageReference 的项目,请将此 XML 节点复制到项目文件中以引用该包。
paket add nanoFramework.WebServer.FileSystem --version 1.2.56                
#r "nuget: nanoFramework.WebServer.FileSystem, 1.2.56"                
#r 指令可用于 F# Interactive 和 Polyglot Notebooks。将其复制到交互式工具或脚本的源代码中以引用包。
// Install nanoFramework.WebServer.FileSystem as a Cake Addin
#addin nuget:?package=nanoFramework.WebServer.FileSystem&version=1.2.56

// Install nanoFramework.WebServer.FileSystem as a Cake Tool
#tool nuget:?package=nanoFramework.WebServer.FileSystem&version=1.2.56                

Quality Gate Status Reliability Rating NuGet #yourfirstpr Discord

nanoFramework logo


欢迎使用 .NET nanoFramework WebServer 仓库

构建状态

组件 构建状态 NuGet 包
nanoFramework.WebServer Build Status NuGet
nanoFramework.WebServer.FileSystem Build Status NuGet

.NET nanoFramework WebServer

此库是由 Laurent Ellerbach 编写的,他将它慷慨地捐赠给了 .NET nanoFramework 项目。

这是一个简单的 nanoFramework WebServer。特性

  • 处理多线程请求
  • 使用 nanoFramework.WebServer.FileSystem NuGet 从任何存储中提供静态文件。需要支持存储的目标设备(具有 System.IO.FileSystem 功能)。
  • 处理 URL 中的参数
  • 可以同时运行多个 WebServer
  • 支持 GET/PUT 和任何其他动词
  • 支持任何类型的头
  • 支持 POST 中的内容
  • 反射,方便使用控制器和路由的概念
  • 辅助函数以直接返回错误代码,简化 REST API 的使用
  • 支持 HTTPS
  • URL 编码/解码

限制

  • 不支持请求或响应流中的任何zip文件

用法

您只需要指定查询的端口和超时,并在请求到来时添加事件处理程序。使用这种方法,每次收到请求都会引发一个事件。

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);
}

同样,您可以将控制器传递给你可以在其中为路由和方法使用装饰的程序。

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

    Thread.Sleep(Timeout.Infinite);
}

在这种情况下,您正在传递2个类,您有公开的方法被标记了装饰,每次找到路由时将调用它们。

使用前面的示例,一个非常简单且直接的Test控制器将看起来像这样

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);
    }
}

在这个例子中,每当调用url将是testTest2tEst42TEST时,RoutePostTest都会被调用,url可以带有参数和方法GET。请注意,Test不会调用该函数,也不调用test/

RouteAnyTest每次url是test/any且不受方法限制时都会被调用。

示例中还有一个更高级的示例,展示了一个简单的REST API来获取人员列表和添加人员。

[!重要]

默认情况下,路由是不可区分大小写的,并且属性必须是小写的。如果您想使用与前一个示例类似的区分大小写的路由,请使用属性CaseSensitive。与前一个示例一样,您必须按照您希望它被响应的格式编写路由。

简单的GPIO控制器REST API

您可以在GPIO控制器示例 REST API中找到简单示例。该控制器不可区分大小写,其工作方式如下

  • 将引脚2打开为输出:http://yoururl/open/2/output
  • 将引脚4打开为输入:http://yoururl/open/4/input
  • 将引脚2的值写入为高:http://yoururl/write/2/high
    • 您可以使用高或1,它们具有相同的作用,并将引脚置于高电平
    • 您可以使用低或0,它们具有相同的作用,并将引脚置于低电平
  • 读取引脚4:http://yoururl/read/4,您将以纯文本的形式获取highlow,具体取决于状态

控制器上的身份验证

控制器支持身份验证。仅在控制器上实现了3种类型的身份验证

  • 基本身份验证:遵循HTTP标准的经典用户名和密码。用法
    • [Authentication("Basic")]将使用web服务器的默认凭证
    • [Authentication("Basic:myuser mypassword")]将使用myuser作为用户名和mypass作为密码。注意:用户名不能包含空格。
  • API密钥在标题中:在标题中添加API密钥。用法
    • [Authentication("ApiKey")]将使用web服务器的默认凭证
    • [Authentication("ApiKeyc:akey")]将使用akey作为ApiKey。
  • 无:无需身份验证。用法
    • [Authentication("None")]将使用web服务器的默认凭证

身份验证属性适用于public Classes和public Methods。

对于控制器中的其余部分,您可以添加属性来定义它们,覆盖它们。以下示例给出了一些想法

[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);
    }
}

您还可以将默认凭证传递给服务器

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);
}

使用前面的示例,以下情况发生

  • 默认情况下,所有控制器都会使用控制器凭证,即使没有指定也是如此。在我们的情况下,将使用默认用户(topuser)和密码(topPassword)的基本身份验证。
    • 当从浏览器调用http://yoururl/authbasic时,您将提示用户和密码,使用默认的用户名topuser和密码topPassword来获取访问权限
    • 当调用http://yoururl/authnone时,您不会受到提示,因为已经针对无身份验证覆盖了身份验证
    • 当调用http://yoururl/authbasicspecial时,用户名和密码与默认值不同,此处user2和password是正确的组合
  • 如果您在控制器中定义了特定的用户名和密码,例如 [Authentication("Basic:myuser mypassword")],则所有控制器将默认使用 myuser 和 mypassword。
  • 当调用 http://yoururl/authapi 时,您必须传递 API 密钥 ApiKey (区分大小写)并使用 superKey1234 的值进行授权,这将覆盖默认的基本认证。
  • 当调用 http://yoururl/authdefaultapi 时,将使用默认密钥 ATopSecretAPIKey1234,因此您必须在请求头中传递它。

总的来说,这是一个如何使用认证的示例,它被定义为允许灵活性。

通过事件管理传入的查询

基本使用方法是以下内容

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);
    }
}

您可以进行更高级的场景,例如返回一个完整的 HTML 页面

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>");

并且可以从 URL 获取参数,例如从 param.html 页面上的上一个链接

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);
}

并服务静态文件

// E = USB storage
// D = SD Card
// I = Internal storage
// Adjust this based on your configuration
const string DirectoryPath = "I:\\";
string[] _listFiles;

// Gets the list of all files in a specific directory
// See the MountExample for more details if you need to mount an SD card and adjust here
// https://github.com/nanoframework/Samples/blob/main/samples/System.IO.FileSystem/MountExample/Program.cs
_listFiles = Directory.GetFiles(DirectoryPath);
// Remove the root directory
for (int i = 0; i < _listFiles.Length; i++)
{
    _listFiles[i] = _listFiles[i].Substring(DirectoryPath.Length);
}

var fileName = url.Substring(1);
// Note that the file name is case sensitive
// Very simple example serving a static file on an SD card                   
foreach (var file in _listFiles)
{
    if (file == fileName)
    {
        WebServer.SendFileOverHTTP(e.Context.Response, DirectoryPath + file);
        return;
    }
}

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

[!重要]

服务文件需要 nanoFramework.WebServer.FileSystem nuget AND 设备支持存储 System.IO.FileSystem

此外,支持 REST API,这里有一个完整的示例

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);
}

这个 API 示例很简单,但是您理解了方法后,您可以选择执行什么操作。

当您获取到 URL 时,您可以检查是否调用了一个特定的控制器。您还有参数和内容有效负载!

调用示例的结果

result

还有更多!请查看完整的示例以了解更多关于此 WebServer 的信息!

使用 HTTPS

您需要生成证书和密钥

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);
}

[!重要] 由于上述证书不是由证书颁发机构签发的,它不会被视为有效证书。如果您想通过浏览器访问 nanoFramework 设备,例如,您必须将 CRT 文件 添加为可信文件。在 Windows 上,您只需双击 CRT 文件,然后点击 "安装证书..."。

当然,您可以使用之前定义的路由。它们都可以工作,无论是事件还是带有控制器概念的路径。

WebServer 状态

您可以订阅事件以获取 WebServer 的状态。这可以在重新启动服务器、放置重试机制或类似情况下很有用。

server.WebServerStatusChanged += WebServerStatusChanged;

private static void WebServerStatusChanged(object obj, WebServerStatusEventArgs e)
{
    // Do whatever you need like restarting the server
    Debug.WriteLine($"The web server is now {(e.Status == WebServerStatus.Running ? "running" : "stopped" )}");
}

E2E 测试

在 WebServerE2ETests 中有一个名为 nanoFramework WebServer E2E Tests.postman_collection.json 的 Postman 测试集合,该集合应用于在实际场景中对 WebServer 进行测试。用法很简单

  • 将 json 文件导入到 Postman
  • 将 WebServerE2ETests 部署到您的设备 - 复制 IP
  • base_url 变量设置为您设备的 IP 地址
  • 选择您要测试的请求或者运行整个集合并检查测试结果。

反馈和文档

有关文档、提供反馈、问题和了解如何贡献的信息,请参阅 Home 仓库

加入我们的 Discord 社区 这里

鸣谢

此项目的贡献者名单可在 CONTRIBUTORS 中找到。

许可证

nanoFramework WebServer 库的许可证为 MIT 许可证

行为准则

本项目采用了贡献者公约中定义的行为准则,以阐明我们社区中期望的行为。有关更多信息,请参阅 .NET 基金会行为准则

.NET 基金会

本项目由 .NET 基金会 支持的。

产品 兼容和additional 计算目标框架版本。
.NET 框架 net 兼容。
兼容的目标框架
包含的目标框架(在包中)
了解更多关于 目标框架.NET Standard

NuGet包

本包未被任何NuGet包使用。

GitHub仓库 (1)

显示依赖nanoFramework.WebServer.FileSystem的Top 1个最受欢迎的GitHub仓库

仓库 星标
nanoframework/Samples
🍬 nanoFramework团队用于测试、概念证明和其他探索性活动的代码示例
版本 下载 最后更新
1.2.56 46 7/30/2024
1.2.55 66 7/24/2024
1.2.52 125 6/3/2024
1.2.48 110 5/17/2024
1.2.45 90 5/13/2024
1.2.43 105 5/10/2024
1.2.40 153 4/12/2024
1.2.38 100 4/9/2024
1.2.36 97 4/8/2024
1.2.34 111 4/5/2024
1.2.32 97 4/3/2024
1.2.30 90 4/3/2024
1.2.27 165 2/14/2024
1.2.25 92 2/12/2024
1.2.23 132 1/26/2024
1.2.21 79 1/26/2024
1.2.19 82 1/26/2024
1.2.17 106 1/24/2024
1.2.14 323 11/17/2023
1.2.12 102 11/10/2023
1.2.9 98 11/9/2023
1.2.7 90 11/8/2023
1.2.6 95 11/8/2023
1.2.3 172 10/27/2023