All Products
Search
Document Center

Function Compute:Handlers

Last Updated:Apr 18, 2024

You can use C# handlers to respond to received events and execute the corresponding business logic. This topic describes the concepts and features of C# handlers and provides examples.

Note

If you want to use HTTP triggers or custom domain names to access functions, obtain request struct before you define HTTP responses. For more information, see Use an HTTP trigger to invoke a function.

What is a handler?

A handler for a function in Function Compute is the method that is used to process requests in function code. When the function is invoked, Function Compute uses the handler that you configure to process requests.

You can configure a handler for a function when you create or update the function in the Function Compute console. For more information, see Manage functions.

Handlers of C# functions in Function Compute follow the Assembly::Namespace.ClassName::MethodName format.

Parameter

Description

Assembly

The assembly name.

Namespace

The namespace name.

ClassName

The class name.

MethodName

The method name.

For example, if the name of the assembly is HelloFcApp, the handler is HelloFcApp::Example.HelloFC::StreamHandler. The following code snippet provides an example:

using System.IO;

namespace Example
{
    public class HelloFC
  {
      public async Task<Stream> StreamHandler(Stream input)
    {
        //function logic
    }
  }
}

Configurations of handlers must conform to the configuration specifications of Function Compute. The configuration specifications vary based on the handler type.

Handler interfaces

When you create a C# function, you must specify a handler method, which is executed when the function is invoked. This handler method can be either a static method or an instance method. If you want to access an IFcContext object in the handler method, you must specify the second parameter in the method as the IFcContext object. The following code snippet shows the definition of the handler method that is supported by event functions.

ReturnType HandlerName(InputType input, IFcContext context);  //IFcContext contained 
ReturnType HandlerName(InputType input); //IFcContext not contained 
Async Task<ReturnType> HandlerName(InputType input, IFcContext context);
Async Task<ReturnType> HandlerName(InputType input);

Function Compute supports Async in C# functions. If Async is used, the execution of functions waits for the execution of asynchronous methods to complete.

The following items describe the parameters:

  • ReturnType: the type of the returned object. The returned object can be a void or System.IO.Stream object, or any object that can be serialized and deserialized in JSON. If a Stream object is returned, the content of the Stream object is directly returned in the response body. Otherwise, the object is returned in the response body after being JSON serialized.

  • InputType: the type of the input parameter, which can be a System.IO.Stream object or an object that can be JSON serialized or deserialized.

  • IFcContext: the context object of the function. For more information, see Context.

Event handlers

Function Compute writes functions in C#. The Aliyun.Serverless.Core dependency package is required. You can introduce the dependency package to the .csproj file by using the following method:

  <ItemGroup>
        <PackageReference Include="Aliyun.Serverless.Core" Version="1.0.1" />
  </ItemGroup>

The Aliyun.Serverless.Core package defines two parameter types for an event handler:

  • Stream handlers

    Use streams to receive the input event data and returns execution results. You must read input data from the input streams and then write execution results to output streams.

  • POCO handlers

    Allow you to customize input and output data in plain old class objects (POCO).

Stream handlers

The following example provides the sample code of a simple stream handler:

using System.IO;
using System.Threading.Tasks;
using Aliyun.Serverless.Core;
using Microsoft.Extensions.Logging;

namespace Example
{
    public class Hello
    {
        public async Task<Stream> StreamHandler(Stream input, IFcContext context)
        {
            IFcLogger 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;
        }

        static void Main(string[] args){}
    }
}

The following items describe the parameters:

  • Namespaces and classes

    In the preceding sample code, the namespace is Example, the class name is Hello, and the method name is StreamHandler. If the assembly name is HelloFcApp, the configuration of the handler is HelloFcApp::Example.Hello::StreamHandler.

  • Stream input parameter

    The input of the handler. The input type for this example is Stream.

  • (Optional) IFcContext context parameter

    A context object that contains information about the function and requests.

  • Task<Stream> response

    The return value, which is of the Stream type.

POCO handlers

The following example provides the sample code of a simple POCO handler:

using Aliyun.Serverless.Core;
using Microsoft.Extensions.Logging;

namespace Example
{
    public class Hello
    {
        public class Product
        {
            public string Id { get; set; }
            public string Description { get; set; }
        }

        // Optional serializer class. If it is not specified, the default serializer (based on JSON.Net) will be used.
        // [FcSerializer(typeof(MySerialization))]
        public Product PocoHandler(Product product, IFcContext context)
        {
            string Id = product.Id;
            string Description = product.Description;
            context.Logger.LogInformation("Id {0}, Description {1}", Id, Description);
            return product;
        }

        static void Main(string[] args){}
    }
}

In addition to Stream objects, POCO can also be used as input and output parameters. If the POCO does not specify a specific JSON serialization object, Function Compute uses JSON.Net to perform JSON serialization and deserialization of the object.

The following items describe the parameters:

  • Namespaces and classes

    In the preceding sample code, the namespace is Example, the class name is Hello, and the method name is PocoHandler. If the assembly name is HelloFcApp, the configuration of the handler is HelloFcApp::Example.Hello::PocoHandler.

  • Product product parameter

    The input of the handler. The input type for this example is Product Class. If POCO does not specify a specific JSON serialization object, Function Compute uses JSON.Net to perform JSON deserialization of the object.

  • (Optional) IFcContext context parameter

    A context object that contains information about the function and requests.

  • Product response

    The return value is of the POCO Product type. If POCO does not specify a specific JSON serialization object, Function Compute uses JSON.Net to perform JSON serialization of the object.

Custom serializers

By default, Function Compute provides serializers that are based on Json.NET. If the default serializers cannot meet your business requirements, you can implement a custom serializer based on IFcSerializer in Aliyun.Serverless.Core.

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

Use an HTTP trigger to invoke a function

Sample code

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
using Aliyun.Serverless.Core;
using Microsoft.Extensions.Logging;

namespace Example
{
    public class Hello
    {
        public class HTTPTriggerEvent
        {
            public string Version { get; set; }
            public string RawPath { get; set; }
            public string Body { get; set; }
            public bool IsBase64Encoded { get; set; }
            public RequestContext RequestContext { get; set; }
            public Dictionary<string, string> Headers { get; set; }
            public Dictionary<string, string> QueryParameters { get; set; }

            public override string ToString()
            {
                return JsonSerializer.Serialize(this);
            }
        }

        public class RequestContext
        {
            public string AccountId { get; set; }
            public string DomainName { get; set; }
            public string DomainPrefix { get; set; }
            public string RequestId { get; set; }
            public string Time { get; set; }
            public string TimeEpoch { get; set; }
            public Dictionary<string, string> Http { get; set; }
        }

        public class HTTPTriggerResponse
        {
            public int StatusCode { get; set; }
            public Dictionary<string, string> Headers { get; set; }
            public bool IsBase64Encoded { get; set; }
            public string Body { get; set; }
        }

        public HTTPTriggerResponse PocoHandler(HTTPTriggerEvent input, IFcContext context)
        {
            context.Logger.LogInformation("receive event: {0}", input.ToString());
            string requestBody = input.Body;
            if (input.IsBase64Encoded)
            {
                byte[] decodedBytes = Convert.FromBase64String(input.Body);
                requestBody = Encoding.UTF8.GetString(decodedBytes);
            }
            return new HTTPTriggerResponse
            {
                StatusCode = 200,
                IsBase64Encoded = false,
                Body = requestBody
                };
        }
        static void Main(string[] args){}
    }
}

Prerequisites

You have used the preceding example to create a function in the C# runtime. For more information, see Create a function.

Procedure

  1. Log on to the Function Compute console. In the left-side navigation pane, click Functions.

  2. In the top navigation bar, select a region. On the Functions page, click the function that you want to manage.

  3. On the function details page, click the Triggers tab to obtain the public endpoint of the HTTP trigger.

  4. Run the following command by using curl to invoke the function:

    curl -i "https://test-python-ipgrwr****.cn-shanghai.fcapp.run" -d 'Hello fc3.0'

    In the preceding command, https://test-python-ipgrwr****.cn-shanghai.fcapp.run is the obtained public endpoint of the HTTP trigger.

    Important
    • If the Authentication Method parameter of the HTTP trigger is set to No Authentication, you can use Postman or curl to invoke the function. For more information, see Procedure.

    • If the Authentication Method parameter of the HTTP trigger is set to Signature Authentication or JWT Authentication, you can use the signature method or JWT authentication method to invoke the function. For more information, see Authentication.

    The following result is returned:

    HTTP/1.1 200 OK
    Content-Disposition: attachment
    Content-Length: 12
    Content-Type: application/json
    X-Fc-Request-Id: 1-64f7449a-127fbe39cd7681596e33ebad
    Date: Tue, 05 Sep 2023 15:09:14 GMT
    
    Hello fc3.0

Possible errors

This sample code can be called by using an HTTP trigger or a custom domain name. If you use an API operation but the configured test parameters do not comply with the request format requirements of HTTP triggers, an error is reported.

For example, the following response is returned if you invoke the function by clicking Test Function in the Function Compute console after you configure the request parameters as "Hello, FC!".

{
    "errorMessage": "Unexpected character encountered while parsing value: H. Path '', line 0, position 0.",
    "errorType": "Newtonsoft.Json.JsonReaderException",
    "stackTrace": [
        "   at Newtonsoft.Json.JsonTextReader.ParseValue()",
        "   at Newtonsoft.Json.JsonReader.ReadAndMoveToContent()",
        "   at Newtonsoft.Json.JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)",
        "   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)",
        "   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)",
        "   at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)",
        "   at Newtonsoft.Json.JsonSerializer.Deserialize[T](JsonReader reader)",
        "   at Aliyun.Serverless.Core.JsonSerializer.Deserialize[T](Stream requestStream) in /dotnetcore/Libraries/src/Aliyun.Serverless.Core.Impl/JsonSerializer.cs:line 95"
    ]
}

Sample programs

Function Compute official libraries contain sample programs that use various handler types and interfaces. Each sample program contains methods for easy compilation and deployment.