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
<PackageReference Include="nanoFramework.WebServer.FileSystem" Version="1.2.56" />
paket add nanoFramework.WebServer.FileSystem --version 1.2.56
#r "nuget: nanoFramework.WebServer.FileSystem, 1.2.56"
// 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
欢迎使用 .NET nanoFramework WebServer 仓库
构建状态
组件 | 构建状态 | NuGet 包 |
---|---|---|
nanoFramework.WebServer | ||
nanoFramework.WebServer.FileSystem |
.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将是test
或Test2
或tEst42
或TEST
时,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,您将以纯文本的形式获取
high
或low
,具体取决于状态
控制器上的身份验证
控制器支持身份验证。仅在控制器上实现了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 时,您可以检查是否调用了一个特定的控制器。您还有参数和内容有效负载!
调用示例的结果
还有更多!请查看完整的示例以了解更多关于此 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 兼容。 |
-
- nanoFramework.CoreLibrary (>= 1.15.5)
- nanoFramework.System.IO.FileSystem (>= 1.1.54)
- nanoFramework.System.Net.Http.Server (>= 1.5.145)
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 |