全部产品

C# 函数入口

更新时间:2019-04-16 11:02:34

在函数计算中使用 C# 编程,您需要定义一个 C# 编写的函数作为入口。本文详细介绍了 C# 的函数入口定义项。

函数入口概述

C# 运行环境(dotnetcore2.1)根据是否支持 HTTP 触发器分为 普通函数入口设置 HTTP 触发器 两种函数入口,为函数设置 HTTP 触发器后的函数入口形式会不同,这是为了方便处理发来的 HTTP request 请求, 同时还有相应的 initializer 入口

普通函数入口

函数入口定义

当创建一个基于 C# 的函数时,需要指定一个 handler 方法,该方法在函数执行时被执行。这个handler 方法可以是 static 方法或者 instance 方法,如果想在该方法中访问 IFcContext 对象,则可以将该方法中的第二个参数指定为 IFcContext 对象。支持的 handler 方法定义如下:

  1. ReturnType HandlerName(InputType input, IFcContext context); //包含IFcContext
  2. ReturnType HandlerName(InputType input); // 不包含IFcContext
  3. Async Task<ReturnType> HandlerName(InputType input, IFcContext context);
  4. Async Task<ReturnType> HandlerName(InputType input);

函数计算支持在使用 C# 编写的函数中应用 Async, 此时函数的执行会等待异步方法执行结束。

在上述定义中:

  • ReturnType: 返回对像可以是 void (注:此时 Async Task 退化为 async Task), System.IO.Stream 对象或者任何可以被 JSON 序列化和 JSON 反序列化的对象,如果是 Stream对象,则该 Stream 内容直接在响应 Body 返回;否则该返回对象被 JSON 序列化后在响应 Body 返回。
  • InputType:input 参数可以是 System.IO.Stream 或者 任何可以被 JSON 序列化和 JSON 反序列化的对象
  • IFcContext: 函数的 Context 对象,包括以下信息:
参数 类型 描述
RequestId String 当前调用请求的唯一 ID,常用于问题复查或者历史调用计数等。
FunctionParam Class 当前调用的函数的基本信息,如函数名、函数入口、函数内存和超时时间等。
Credentials Class 函数计算服务通过扮演您提供的 服务角色 获得的一组临时密钥 securityToken,每 15 分钟更新一次。您可以在函数代码中使用临时密钥去访问其他阿里云服务,例如 OSS,避免您将重要的身份凭证 AccessKey 写死在函数代码里。
ServiceMeta Class 当前调用的函数所在的服务的信息,包括服务名称,接入的日志服务的 logProject 和 logStore 信息, service 的版本信息 qualifier 和 version_id,qualifier 表示调用函数时指定的 service 版本或别名,version_id 表示实际调用的 service 版本。
Region String 当前调用的函数所在地域,如 cn-shanghai。更多详情,请参阅 地域与可用区
AccountId String 当前调用函数用户的阿里云账号 ID。更多详情,请参阅 获取账号ID

更多详情请参考:fc-dotnet-libs

Handler方法示例

函数计算使用 C# 编写函数, 需要 Nuget 引入 Aliyun.Serverless.Core package.

Stream Handler

以下方法将用户请求中的输入原样返回。

  1. using System.IO;
  2. using System.Threading.Tasks;
  3. using Aliyun.Serverless.Core;
  4. using Microsoft.Extensions.Logging;
  5. namespace FC.Examples
  6. {
  7. public class TestHandler
  8. {
  9. public async Task<Stream> Echo(Stream input, IFcContext context)
  10. {
  11. ILogger logger = context.Logger;
  12. logger.LogInformation("Handle request: {0}", context.RequestId);
  13. MemoryStream copy = new MemoryStream();
  14. await input.CopyToAsync(copy);
  15. copy.Seek(0, SeekOrigin.Begin);
  16. return copy;
  17. }
  18. }
  19. }

POCO Handler

除了 Stream 作为输入输出参数,POCO(Plain old CLR objects)对象同样也可以作为输入和输出。如果该 POCO 没有指定特定的 JSON Serializer 对象,则函数计算默认用 Json.Net 进行对象的 JSON Serialize 以及Deserialize。

  1. using Microsoft.Extensions.Logging;
  2. namespace FC.Examples
  3. {
  4. public class TestHandler
  5. {
  6. public class Product
  7. {
  8. public string Id { get; set; }
  9. public string Description { get; set; }
  10. }
  11. // optional serializer class, if it’s not specified, the default serializer (based on JSON.Net) will be used.
  12. // [FcSerializer(typeof(MySerialization))]
  13. public Product Echo(Product product, IFcContext context)
  14. {
  15. string Id = product.Id;
  16. string Description = product.Description;
  17. context.Logger.LogInformation("Id {0}, Description {1}", Id, Description);
  18. return product;
  19. }
  20. }
  21. }

Handler 规范

命名格式

在创建函数时,你需要指定一个 handler 方法的字符串,用来告诉函数计算调用哪个方法,该字符串格式如下:AssemblyFileName::FullClassName::METHOD

其中

  • AssemblyFileName 是该函数所在的 Assembly 的文件名(省去.dll)
  • FullClassName 是该函数所在类的全名,Namespace.ClassName
  • Method 是该方法的名字

在上述 Handler 例子中,如果 Assembly 文件为 test_assembly, 则其 handler 字符串为:test_assembly::FC.Examples.TestHandler::Echo

限制

  • Handler 参数格式严格按照上述定义,也就是说参数 1 为必须输入,参数 2 可选,但必须为 IFcContext
  • Handler 函数不支持 Generic Method
  • 输入输出参数必须为 Stream 或者 可Json序列化
  • Async函数返回值 Task 中 T 必须为 Stream 或者 可Json序列化的类

Custom Serializer

函数计算针对 POCO Handler 提供了默认的 基于JSON .NET Serializer,如果默认的 Serializer 不能满足需求, 可以基于 Aliyun.Serverless.Core 中的 interface IFcSerializer 实现Custom Serializer

  1. public interface IFcSerializer
  2. {
  3. T Deserialize<T>(Stream requestStream);
  4. void Serialize<T>(T response, Stream responseStream);
  5. }

普通函数完整操作示例

临时密钥用于辨识请求者身份和权限,在访问其他服务,例如 OSS 时,您必须设置 securityToken。下面的示例 C# 代码使用临时密钥,向 OSS 的一个 Bucket 获取指定的一个 object:

  1. 创建一个 .net core 的 console 工程

    1. [songluo@~/tmp]# mkdir fcdotnetsample
    2. [songluo@~/tmp]# cd fcdotnetsample
    3. [songluo@~/tmp/fcdotnetsample]# dotnet new console
  2. 在 fcdotnetsample.csproj 中添加如下 package:

    1. <ItemGroup>
    2. <PackageReference Include="Aliyun.Serverless.Core" Version="1.0.1" />
    3. <PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.9.1" />
    4. </ItemGroup>
  3. 编辑 Program.cs

    1. using System;
    2. using System.IO;
    3. using Aliyun.OSS;
    4. using Aliyun.Serverless.Core;
    5. namespace fcdotnetsample
    6. {
    7. class Program
    8. {
    9. static void Main(string[] args)
    10. {
    11. Console.WriteLine("Hello World!");
    12. }
    13. }
    14. public class OssFileHandlerRequest
    15. {
    16. public string Bucket;
    17. public string Key;
    18. public string Endpoint;
    19. }
    20. public class OSSFileHandler
    21. {
    22. public Stream GetOssFile(OssFileHandlerRequest req, IFcContext context)
    23. {
    24. if (req == null)
    25. {
    26. throw new ArgumentNullException(nameof(req));
    27. }
    28. if (context == null || context.Credentials == null)
    29. {
    30. throw new ArgumentNullException(nameof(context));
    31. }
    32. OssClient ossClient = new OssClient(req.Endpoint, context.Credentials.AccessKeyId, context.Credentials.AccessKeySecret, context.Credentials.SecurityToken);
    33. OssObject obj = ossClient.GetObject(req.Bucket, req.Key);
    34. return obj.Content;
    35. }
    36. }
    37. }
  4. publish 工程并将目标文件打成 zip 包

    1. [songluo@~/tmp/fcdotnetsample]# dotnet publish -c Release
    2. Microsoft (R) Build Engine version 15.9.20+g88f5fadfbe for .NET Core
    3. Copyright (C) Microsoft Corporation. All rights reserved.
    4. Restore completed in 47.9 ms for /Users/songluo/tmp/fcdotnetsample/fcdotnetsample.csproj.
    5. fcdotnetsample -> /Users/songluo/tmp/fcdotnetsample/bin/Release/netcoreapp2.1/fcdotnetsample.dll
    6. fcdotnetsample -> /Users/songluo/tmp/fcdotnetsample/bin/Release/netcoreapp2.1/publish/
    7. [songluo@~/tmp/fcdotnetsample]# cd /Users/songluo/tmp/fcdotnetsample/bin/Release/netcoreapp2.1/publish/
    8. [songluo@~/tmp/fcdotnetsample/bin/Release/netcoreapp2.1/publish]# zip -r fcdotnetsample.zip *
    9. adding: Aliyun.OSS.Core.dll (deflated 60%)
    10. adding: Aliyun.Serverless.Core.dll (deflated 59%)
    11. adding: Microsoft.Extensions.Logging.Abstractions.dll (deflated 53%)
    12. adding: fcdotnetsample.deps.json (deflated 73%)
    13. adding: fcdotnetsample.dll (deflated 57%)
    14. adding: fcdotnetsample.pdb (deflated 27%)
    15. adding: fcdotnetsample.runtimeconfig.json (deflated 23%)
    16. [songluo@~/tmp/fcdotnetsample/bin/Release/netcoreapp2.1/publish]# ls -ll fcdotnetsample.zip
    17. -rw-r--r-- 1 songluo staff 130276 Mar 14 17:48 fcdotnetsample.zip

后面直接使用这个 fcdotnetsample.zip 创建 runtime 为 dotnetcore2.1, handler 为 fcdotnetsample::fcdotnetsample.OSSFileHandler::GetOssFile 的函数就行。

initializer 入口

函数计算提供了 Init 方法的机制,用于执行初始化工作。该 Init 方法会自动在后台容器启动时被调用,每个容器只调用一次。Init 方法定义:

  1. public void Init(); //没有context对象
  2. public void Init(IFcContext context); //包含context对象
  3. public static void Init(); //没有context对象
  4. public static void Init(IFcContext context); //包含context对象

initializer 格式

  • MyInitializer 需要与添加 initializer 函数时的 “initializer” 字段相对应:例如创建函数时指定的 initializer 入口 为 fcdotnetsample::fcdotnetsample.TestHandler::MyInitializer,那么函数计算在配置 initializer 功能后会首先加载 fcdotnetsample.TestHandler 中定义的 MyInitializer 函数。

initializer 特点

  • IFcContext 中的 FunctionParam 中 FunctionInitializerInitializationTimeout 两个信息是为 initializer 设计的,当使用 initializer 功能时,会被设置为用户创建函数时所设置的值,否则为空,且不生效。
  • 无返回值。在函数末尾增加 return 操作是无效的。

HTTP 触发器的函数入口

设置了 HTTP 触发器的函数入口与其他触发器要求的函数入口不同,以下为一个基本的 HTTP 触发器规定的函数入口定义:

函数计算使用 C# 编写 HTTP 触发器的函数, 需要 Nuget 引入 Aliyun.Serverless.CoreAliyun.Serverless.Core.Http package.

  1. using System.Threading.Tasks;
  2. using Microsoft.AspNetCore.Hosting;
  3. using Microsoft.AspNetCore.Http;
  4. using Aliyun.Serverless.Core;
  5. using Aliyun.Serverless.Core.Http;
  6. namespace MySpace.TestHandlers
  7. {
  8. public class SingleHttpHandler : FcHttpEntrypoint
  9. {
  10. protected override void Init(IWebHostBuilder builder)
  11. { }
  12. public override async Task<HttpResponse> HandleRequest(HttpRequest request, HttpResponse response, IFcContext fcContext)
  13. {
  14. response.StatusCode = 200;
  15. response.ContentType = "text/plain";
  16. await response.WriteAsync("hello world");
  17. return response;
  18. }
  19. }
  20. }

函数入参

说明

C# 编写 HTTP 触发器的函数必须继承 Aliyun.Serverless.Core.Http 中的 FcHttpEntrypoint, 其中 Init 函数必须 override, HandleRequest 是函数入口 handler, 可以根据情况决定是否 override

  • Single function: override HandleRequest, HandleRequest 实现自定义的逻辑处理

  • Asp.net core application: 只需要 override Init 函数

下节的示例会具体描述怎么使用 FcHttpEntrypoint

HTTP 触发器的函数入口示例

Single function 示例

以下示例示范了如何使用 HTTP 触发器的函数入口中的 HttpRequest 和 HttpResponse:

  1. using System.IO;
  2. using System.Threading.Tasks;
  3. using Microsoft.AspNetCore.Hosting;
  4. using Microsoft.AspNetCore.Http;
  5. using Aliyun.Serverless.Core;
  6. using Aliyun.Serverless.Core.Http;
  7. using Microsoft.Extensions.Logging;
  8. namespace MySpace.TestHandlers
  9. {
  10. public class SingleHttpHandler : FcHttpEntrypoint
  11. {
  12. protected override void Init(IWebHostBuilder builder)
  13. {
  14. }
  15. public override async Task<HttpResponse> HandleRequest(HttpRequest request, HttpResponse response, IFcContext fcContext)
  16. {
  17. string method = request.Method;
  18. string relativePath = request.Path.Value;
  19. fcContext.Logger.LogInformation("method = {0}; requestPath = {1}", method, relativePath);
  20. StreamReader sr = new StreamReader(request.Body);
  21. string requestBody = sr.ReadToEnd();
  22. fcContext.Logger.LogInformation("requestBody = {}", requestBody);
  23. // process request.Headers
  24. response.StatusCode = 200;
  25. response.ContentType = "text/plain";
  26. response.Headers.Add("customheader", "v1");
  27. await response.WriteAsync("hello world");
  28. return response;
  29. }
  30. }
  31. }

Asp.net core application 示例

  1. using System;
  2. using Aliyun.Serverless.Core.Http;
  3. using Microsoft.AspNetCore.Hosting;
  4. namespace MySpace.TestWebApi
  5. {
  6. public class FcRemoteEntrypoint : FcHttpEntrypoint
  7. {
  8. protected override void Init(IWebHostBuilder builder)
  9. {
  10. builder
  11. .UseStartup<Startup>();
  12. }
  13. }
  14. }

具体操作

  1. 创建一个 asp.net core 的 webapi 工程

    1. [songluo@~/tmp]# mkdir fcaspdotnetsample
    2. [songluo@~/tmp]# cd fcaspdotnetsample
    3. [songluo@~/tmp/fcaspdotnetsample]# dotnet new webapi
  2. 在 fcaspdotnetsample.csproj 中添加如下 package:

    1. <ItemGroup>
    2. <PackageReference Include="Aliyun.Serverless.Core" Version="1.0.1" />
    3. <PackageReference Include="Aliyun.Serverless.Core.Http" Version="1.0.2" />
    4. </ItemGroup>
  3. 新建文件 FcRemoteEntrypoint.cs, 文件内容为 Asp.net core application 示例代码

  4. publish 工程并将目标文件打成 zip 包

    1. [songluo@~/tmp/fcaspdotnetsample]# dotnet publish -c Release
    2. Microsoft (R) Build Engine version 15.9.20+g88f5fadfbe for .NET Core
    3. Copyright (C) Microsoft Corporation. All rights reserved.
    4. Restore completed in 88.39 ms for /Users/songluo/tmp/fcaspdotnetsample/fcaspdotnetsample.csproj.
    5. fcaspdotnetsample -> /Users/songluo/tmp/fcaspdotnetsample/bin/Release/netcoreapp2.1/fcaspdotnetsample.dll
    6. fcaspdotnetsample -> /Users/songluo/tmp/fcaspdotnetsample/bin/Release/netcoreapp2.1/publish/
    7. [songluo@~/tmp/fcaspdotnetsample]# cd /Users/songluo/tmp/fcaspdotnetsample/bin/Release/netcoreapp2.1/publish/
    8. [songluo@~/tmp/fcaspdotnetsample/bin/Release/netcoreapp2.1/publish]# zip -r fcaspdotnetsample.zip *
    9. adding: appsettings.Development.json (deflated 40%)
    10. adding: appsettings.json (deflated 30%)
    11. adding: fcaspdotnetsample.deps.json (deflated 85%)
    12. adding: fcaspdotnetsample.dll (deflated 61%)
    13. adding: fcaspdotnetsample.pdb (deflated 40%)
    14. adding: fcaspdotnetsample.runtimeconfig.json (deflated 31%)
    15. adding: web.config (deflated 40%)
    16. [songluo@~/tmp/fcaspdotnetsample/bin/Release/netcoreapp2.1/publish]# ls -ll fcaspdotnetsample.zip
    17. -rw-r--r-- 1 songluo staff 39101 Mar 15 09:47 fcaspdotnetsample.zip

后面直接使用这个 fcaspdotnetsample.zip 创建 runtime 为 dotnetcore2.1, handler 为 fcaspdotnetsample::MySpace.TestWebApi.FcRemoteEntrypoint::HandleRequest 的函数就行。

如果使用 Single function, 参考 普通函数完整操作示例, 创建 console 工程,新建 FcRemoteEntrypoint.cs, 代码改成 Single function 示例代码即可。

HTTP 触发器的函数入口限制项

Request 限制项

如果 HTTP 触发器的函数入口 Request 超过以下限制,会抛出 400 状态码和 InvalidArgument 错误码

参数 限制 HTTP 状态码 错误码
headers headers 中的所有键值对(key 和 value)的大小不能超过 4 KB。 400 InvalidArgument
path path 以及所有 query 参数(params)的大小不能超过 4 KB。
body HTTP body 的大小不能超过 6 MB。

Response 限制项

如果超过以下限制,会抛出 502 状态码和 BadResponse 错误码。

参数 限制 HTTP 状态码 错误码
headers headers 中的所有键值对(key 和 value)的大小不能超过 4 KB。 502 BadResponse
body HTTP body 的大小不能超过 6 MB。

更多有关 http trigger 的详情,请参考 HTTP 触发器

参考链接

有关 .NET core 运行环境的详细信息,请参阅 .NET core 运行环境