在函数计算服务使用C#编程,需要定义一个C#函数作为入口。本文介绍了C#事件函数的结构和特点。

事件函数定义

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

ReturnType HandlerName(InputType input, IFcContext context);  //包含IFcContext。
ReturnType HandlerName(InputType input); // 不包含IFcContext。
Async Task<ReturnType> HandlerName(InputType input, IFcContext context);
Async Task<ReturnType> HandlerName(InputType input);

函数计算支持在使用C#编写的函数中应用Async,此时函数的执行会等待异步方法执行结束。以下是对ReturnType、InputType和IFcContext的说明:

  • ReturnType:返回对象可以是void(此时Async Task退化为async Task)、System.IO.Stream对象、任何可以被JSON序列化和JSON反序列化的对象。如果是Stream对象,则该Stream内容直接在响应体返回。否则该返回对象被JSON序列化后在响应体返回。
  • InputType:输入参数可以是System.IO.Stream或任何可以被JSON序列化和JSON反序列化的对象。
  • IFcContext:函数的context对象。
    context中包含了以下信息。
    参数 类型 描述
    RequestId String 本次调用请求的唯一ID,您可以把它记录下来在出现问题的时候方便查询。
    FunctionParam Class 当前调用的函数的一些基本信息,例如函数名、函数入口、函数内存和超时时间。
    Credentials Class 函数计算服务通过扮演您提供的服务角色获得的一组临时密钥securityToken,每15分钟更新一次。您可以在函数代码中使用临时密钥去访问其他阿里云服务,例如OSS,避免您将重要的身份凭证AccessKey硬编码在函数代码里,请参见权限简介
    ServiceMeta Class 当前调用的函数所在的服务的信息,包含服务名称、接入的日志服务的LogProject和LogStore信息、服务的版本信息、qualifierversion_id。其中qualifier表示调用函数时指定的服务版本或别名,version_id表示实际调用的服务版本。
    Region String 当前调用的函数所在地域,例如cn-shanghai。更多详情,请参见服务地址
    AccountId String 当前调用函数用户的阿里云账号ID。更多详情,请参见获取帐号ID

更多详情请参见fc-dotnet-libs

handler方法示例

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

  • Stream handler
    用下文的方法可以将您的请求中的输入原样返回。
    using System.IO;
    using System.Threading.Tasks;
    using Aliyun.Serverless.Core;
    using Microsoft.Extensions.Logging;
    
    namespace FC.Examples
    {
        public class TestHandler
        {
            public async Task<Stream> Echo(Stream input, IFcContext context)
            {
                ILogger logger = context.Logger;
                logger.LogInformation("Handle request: {0}", context.RequestId);
                MemoryStream copy = new MemoryStream();
                await input.CopyToAsync(copy);
                copy.Seek(0, SeekOrigin.Begin);
                return copy;
            }
        }
    }
  • POCO handler
    关于POCO handler。除了Stream作为输入输出参数,POCO(Plain old CLR objects)对象同样也可以作为输入和输出。如果该POCO没有指定特定的JSON Serializer对象,则函数计算默认用JSON.Net进行对象的JSON Serialize和Deserialize。
    using Microsoft.Extensions.Logging;
    
    namespace FC.Examples
    {
        public class TestHandler
        {
            public class Product
            {
                public string Id { get; set; }
                public string Description { get; set; }
            }
    
            // optional serializer class, if it’s not specified, the default serializer (based on JSON.Net) will be used.  
            // [FcSerializer(typeof(MySerialization))]  
            public Product Echo(Product product, IFcContext context)
            {
                string Id = product.Id;
                string Description = product.Description;
                context.Logger.LogInformation("Id {0}, Description {1}", Id, Description);
                return product;
            }
        }
    }       

handler规范

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

参数 参数说明
AssemblyFileName 函数所在的文件名。
FullClassName 函数所在类的全名。
Method 方法名。

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

有关限制如下:

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

Custom Serializer

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

public interface IFcSerializer
{
    T Deserialize<T>(Stream requestStream);
    void Serialize<T>(T response, Stream responseStream);
}      

事件函数完整示例

下文演示了C#代码如何使用临时密钥向OSS的一个Bucket获取指定的一个对象。

  1. 创建一个.net core的console工程。
        [songluo@~/tmp]# mkdir fcdotnetsample
        [songluo@~/tmp]# cd fcdotnetsample
        [songluo@~/tmp/fcdotnetsample]# dotnet new console          
  2. fcdotnetsample.csproj中添加下文的包。
    <ItemGroup>
            <PackageReference Include="Aliyun.Serverless.Core" Version="1.0.1" />
            <PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.9.1" />
    </ItemGroup>    
  3. 编辑Program.cs
        using System;
        using System.IO;
        using Aliyun.OSS;
        using Aliyun.Serverless.Core;
    
        namespace fcdotnetsample
        {
             class Program
            {
                static void Main(string[] args)
                {
                    Console.WriteLine("Hello World!");
                }
            }
    
            public class OssFileHandlerRequest
            {
                public string Bucket;
                public string Key;
                public string Endpoint;
            }
    
            public class OSSFileHandler
            {
                public Stream GetOssFile(OssFileHandlerRequest req, IFcContext context)
                {
                    if (req == null)
                    {
                        throw new ArgumentNullException(nameof(req));
                    }
                    if (context == null || context.Credentials == null)
                    {
                        throw new ArgumentNullException(nameof(context));
                    }
                    OssClient ossClient = new OssClient(req.Endpoint, context.Credentials.AccessKeyId, context.Credentials.AccessKeySecret, context.Credentials.SecurityToken);
                    OssObject obj = ossClient.GetObject(req.Bucket, req.Key);
                    return obj.Content;
                }
            }
        }        
  4. 上传工程并将目标文件打成ZIP包。
        [songluo@~/tmp/fcdotnetsample]# dotnet publish -c Release
        Microsoft (R) Build Engine version 15.9.20+g88f5fadfbe for .NET Core
        Copyright (C) Microsoft Corporation. All rights reserved.
    
     Restore completed in 47.9 ms for /Users/songluo/tmp/fcdotnetsample/fcdotnetsample.csproj.
     fcdotnetsample -> /Users/songluo/tmp/fcdotnetsample/bin/Release/netcoreapp2.1/fcdotnetsample.dll
     fcdotnetsample -> /Users/songluo/tmp/fcdotnetsample/bin/Release/netcoreapp2.1/publish/
        [songluo@~/tmp/fcdotnetsample]# cd /Users/songluo/tmp/fcdotnetsample/bin/Release/netcoreapp2.1/publish/
        [songluo@~/tmp/fcdotnetsample/bin/Release/netcoreapp2.1/publish]# zip -r fcdotnetsample.zip *
     adding: Aliyun.OSS.Core.dll (deflated 60%)
     adding: Aliyun.Serverless.Core.dll (deflated 59%)
     adding: Microsoft.Extensions.Logging.Abstractions.dll (deflated 53%)
     adding: fcdotnetsample.deps.json (deflated 73%)
     adding: fcdotnetsample.dll (deflated 57%)
     adding: fcdotnetsample.pdb (deflated 27%)
     adding: fcdotnetsample.runtimeconfig.json (deflated 23%)
        [songluo@~/tmp/fcdotnetsample/bin/Release/netcoreapp2.1/publish]# ls -ll fcdotnetsample.zip
        -rw-r--r--  1 songluo  staff  130276 Mar 14 17:48 fcdotnetsample.zip
  5. 使用fcdotnetsample.zip创建Runtime为dotnetcore2.1,handler为fcdotnetsample::fcdotnetsample.OSSFileHandler::GetOssFile的函数。