NanUI

数据资源1

概述

数据资源是以提供数据为目标的资源。例如,您可以在前端使用 fetch 或者 axios 等命令和工具来向数据资源处理器发起请求,从而获取后端的数据。WinFormium 的数据资源控制器是一个模拟 HTTP 协议的请求拦截器,它可以拦截前端发起的 HTTP 请求,并将请求转发到后端的数据资源处理器,然后调用注册的数据服务并执行数据服务中的行为方法并返回数据。

这里提到了数据资源控制器是一个模拟 HTTP 协议的请求拦截器,所以数据资源控制器并不是一个真正的 HTTP 服务器,它只是模拟了 HTTP 协议的请求拦截器的行为,从而使得前端可以使用 HTTP 协议来访问数据资源控制器,因此您无法在当前 WinFormium 进程以外访问到数据服务提供的接口。

注册数据资源控制器

您可以在 WinFormium 应用程序创建阶段使用 AppBuilder 来注册数据资源控制器,也可以在 WinFormium 应用程序配置阶段使用 WinFormiumStartup 类的 ConfigureServices 以配置服务的方式来注册数据资源控制器。

注册示例

AppBuilder

class Program
{
    [STAThread]
    static void Main(){
        var builder = WinFormiumApp.CreateBuilder();

        var app = builder
        //...
        .UseDataResource("http","api.app.local", provider => {
            provider.ImportFromCurrentAssembly(); // 从当前程序集导入数据服务
        })
        .build();

        app.Run();

    }
}

使用 AppBuilder 的扩展方法 UseDataResource 来注册数据资源控制器,该方法第一个参数为数据资源控制器的 Url 协议,第二个参数是数据资源控制器的 Url,第三个参数是一个 Action<DataResourceProvider> 类型的委托,您可以在该委托中配置数据资源控制器的行为,DataResourceProvider 在后面小结中会详细介绍。

WinFormiumStartup

class MyApp : WinFormiumStartup
{
    //...

    public override void ConfigureServices(IServiceCollection services)
    {
        //...
        services.AddDataResource("http","api.app.local", provider => {
            provider.ImportFromCurrentAssembly(); // 从当前程序集导入数据服务
        });
        //...
    }

}

DataResourceProvider

DataResourceProvider 是数据资源控制器的配置类,您可以在 DataResourceProvider 中配置数据资源控制器的行为。它提供以下方法:

方法 说明
ImportFromAssemblies(string[] files) 从指定的程序集文件中导入数据服务。files 是包含数据服务的程序集的绝对路径。
ImportFromAssembly(Assembly assembly) 从指定的程序集中导入数据服务。assembly 是包含数据服务的程序集。
ImportFromCurrentAssembly() 从当前程序集中导入数据服务。
ImportFromDirectory(string directoryName) 从指定的文件夹中导入数据服务。directoryName 是包含数据服务的文件夹的绝对路径。
ImportFromFile(string fileName) 从指定的文件中导入数据服务。fileName 是包含数据服务的文件的绝对路径。

数据服务

数据服务是一个继承自 DataResourceService 的类,您可以在数据服务中定义一些行为方法,然后在数据资源控制器初始化时会根据数据服务以及行为方法的配置来生成数据服务的实例,当前端发起 HTTP 请求时,数据资源控制器会根据请求的 Url 来调用数据服务的行为方法并返回数据。

创建数据服务

在下面的代码示例中,我们定义了一个数据服务 EchoService,它继承自 DataResourceService。需要特别声明的时,数据服务必须以 Service 结尾,例如 EchoService,否则数据资源控制器无法识别该类为数据服务。

using WinFormium.WebResource;

[RoutePath("/Echo/")]
public class TestService : DataResourceService
{
    ILogger<TestService> _logger;

    public TestService(ILogger<TestService> logger)
    {
        _logger = logger;
    }
    // ...
}

RoutePathAttribute 是数据服务的路由配置,如果您不为数据服务配置路由,那么数据资源控制器会使用数据服务的类名作为路由,例如 TestService 的路由为 /Test/。在上面的示例代码中,我们为数据服务配置了路由 /Echo/,那么数据资源控制器会使用 /Echo/ 作为数据服务的路由。

另外,数据服务是支持依赖注入的,您可以在数据服务的构造函数中注入您需要的服务,例如上面的代码中,我们注入了一个 ILogger<TestService> 类型的服务,这样我们就可以在数据服务中使用 ILogger<TestService> 服务了。服务需要在 WinFormiumStartupConfigureServices 方法中进行注册。

创建行为

我们继续来完善上述数据服务示例,在数据服务中定义一个行为方法 Hello,该方法返回一个字符串 Hello World!

using WinFormium.WebResource;

[RoutePath("/Echo/")]
public class TestService : DataResourceService
{
    [HttpGetMethod("Hi")]
    public IResourceResult Hello()
    {
        return Text("Hello World!");
    }
}

数据服务的行为方法需要返回 IResourceResult 类型的数据,也就是说,返回值为 IResourceResult 的方法才会被识别为数据服务的行为方法。与数据服务一样,也可以单独为行为指定路由。例如上面的代码,我们为数据服务的行为 Hello 指定了路由 Hi,那么数据资源控制器会使用 /Echo/Hi 作为数据服务行为的路由。

数据服务行为的路由特性都继承自 HttpMethodAttribute。根据实际情况,您可以按需使用 HttpGetMethodAttributeHttpPostMethodAttributeHttpPutMethodAttributeHttpDeleteMethodAttributeHttpPatchMethodAttributeHttpHeadMethodAttributeHttpOptionsMethodAttribute 来指定行为的路由和 Http 方法。行为只响应指定 Http 方法的请求,如果您不指定 Http 方法,那么行为将响应所有 Http 方法的请求。

行为方法的返回值

在上述代码中,使用了 Text 方法来返回字符串内容,您也可以使用 Json 方法来返回 Json 数据。

DataResourceService 类提供了以下方法和属性:

属性

属性名称 类型 描述
Request ResourceRequest 获取当前请求的信息。
Response ResourceResponse 获取当前响应的信息。

方法

方法名 说明
BadReqeust() 返回一个状态码为 400 的错误结果。
Content(string,string) 返回一个状态码为 200 的结果,结果内容为 content 参数的内容,contentType 参数为结果的内容类型。
Content(string,string,Encoding) 返回一个状态码为 200 的结果,结果内容为 content 参数的内容,contentType 参数为结果的内容类型,encoding 参数为结果的编码。
Json(object,JsonSerializerOptions) 返回一个状态码为 200 的结果,结果内容为 object 参数的内容,此参数将被序列化为 Json 字符串,options 参数为结果的序列化选项。
Json(T,JsonSerializerOptions) 返回一个状态码为 200 的结果,结果内容为 T 参数的内容,此参数将被序列化为 Json 字符串,options 参数为结果的序列化选项。
NoContent() 返回一个状态码为 204 的结果。
NotFound() 返回一个状态码为 404 的错误结果。
Ok() 返回一个状态码为 200 的结果。
StatusCode(int) 返回一个状态码为 statusCode 参数的结果。
StatusCode(HttpStatusCode) 返回一个状态码为 HttpStatusCode 参数的结果。
Text(string) 返回一个状态码为 200 的结果,结果内容为 text 参数的内容。

行为方法的参数

您可以在数据服务的行为方法中定义参数,数据资源控制器会根据前端请求的参数来调用数据服务的行为方法并自动为参数赋值。

using WinFormium.WebResource;

[RoutePath("/Echo/")]
public class TestService : DataResourceService
{
    // ...

    [HttpGetMethod]
    public IResourceResult Test(int id, string[] message)
    {
        return Json(new { id = id, text = string.Join(",", message)  });
    }

}

上面的代码定义了一个 Test 行为,它有两个参数 idmessageid 是一个 int 类型的参数,message 是一个 string[] 类型的参数。当前端发起请求 http://api.app.local/Echo/Test?id=1&message=hello&message=world 时,数据资源控制器会自动为 id 参数赋值为 1,为 message 参数赋值为 ["hello","world"]

再来看下面的代码:

using WinFormium.WebResource;

[RoutePath("/Echo/")]
public class TestService : DataResourceService
{
    // Hi ...

    // Test ...

    [HttpPostMethod]
    public async Task<IResourceResult> TestAsync([JsonBody] Model m)
    {
        var request = Request;

        await Task.Delay(5000);

        return Json(m);
    }
}

上面的代码定义了一个 TestAsync 行为,它有一个参数 mm 是一个 Model 类型的参数。当前端发起请求 http://api.app.local/Echo/TestAsync 时,数据资源控制器会自动为 m 参数赋值为请求的 Json 数据。需要特别说明的是,m 参数必须使用 [JsonBody] 特性来修饰,否则数据资源控制器无法识别该参数为 Json 数据。

此外您应该还应注意到,这个方法是异步的,是的,您可以在数据服务的行为方法中使用异步方法。上面的例子中使用 await Task.Delay(5000); 来模拟一个耗时的操作。您还需要知道的是,不论异步方法还是同步方法,数据服务都不会阻塞主进程。

测试数据服务

您可以使用以下前端代码来测试数据服务的行为方法 /Echo/Hi

fetch("http://api.app.local/Echo/Hi")
  .then((res) => res.text())
  .then(console.log); //console: Hello World!

您可以使用以下前端代码来测试数据服务的行为方法 /Echo/Test

fetch("http://api.app.local/Echo/Test?id=1&message=hello&message=world")
  .then((res) => res.json())
  .then(console.log); //console: {id: 1, text: "hello,world"}

您可以使用以下前端代码来测试数据服务的行为方法 /Echo/TestAsync

fetch("http://api.app.local/Echo/TestAsync", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ id: 1, text: "Hello WinFormium !!" }),
})
  .then((res) => res.json())
  .then(console.log); //console: {id: 1, text: "Hello WinFormium !!"}

另请参阅

  1. 数据资源控制器仅支持 WinFormium 商业版。