Écrire des fonctions Cloud Run

Cette page explique comment écrire des fonctions Cloud Run HTTP et basées sur des événements avec Functions Framework.

Présentation du framework des fonctions

Lorsque vous écrivez le code source des fonctions, vous devez utiliser le framework des fonctions, une bibliothèque Open Source permettant d'écrire des fonctions Cloud Run. Le framework des fonctions vous permet d'écrire des fonctions légères qui s'exécutent dans Cloud Run et dans d'autres environnements, y compris votre ordinateur de développement local et les environnements basés sur Knative.

Le framework des fonctions vous permet d'effectuer les actions suivantes :

  • Appelez une fonction Cloud Run en réponse à une requête.
  • Annulation automatique des événements conformes à la spécification CloudEvents, une spécification standard dans l'industrie pour la description des données d'événement de manière courante.
  • Démarrez un serveur de développement local pour les tests.

Functions Framework fournit une interface permettant de créer des services modulaires. Pour utiliser le Functions Framework dans votre code source, spécifiez les éléments suivants :

Point d'entrée de la fonction

Votre code source doit définir un point d'entrée de fonction, qui est le code qui s'exécute lorsque Cloud Run appelle votre fonction. Vous spécifiez ce point d'entrée lorsque vous déployez votre fonction.

La façon dont vous définissez le point d'entrée dépend de l'environnement d'exécution du langage que vous utilisez. Certaines langues utilisent une fonction comme point d'entrée, tandis que d'autres utilisent une classe.

Type de signature

Lorsque vous écrivez le code source d'une fonction avec le framework Functions, vous devez spécifier l'un des deux types de signature :

  • Fonctions HTTP : enregistre une fonction de gestionnaire HTTP. Utilisez une fonction HTTP lorsque votre fonction a besoin d'un point de terminaison d'URL et doit répondre aux requêtes HTTP, par exemple pour les webhooks.
  • Fonctions basées sur des événements, également appelées fonctions CloudEvents : enregistre une fonction de gestionnaire CloudEvents. Utilisez une fonction basée sur des événements lorsqu'elle est déclenchée directement en réponse à des événements au sein de votre projet Google Cloud , tels que des messages sur un sujet Pub/Sub ou des modifications dans un bucket Cloud Storage.

Structure du répertoire source

Le framework des fonctions est compatible avec plusieurs langages de programmation. L'environnement d'exécution du langage que vous choisissez et le type de fonction que vous souhaitez écrire déterminent la structure de votre code et l'implémentation de la fonction.

Pour que Cloud Run puisse localiser votre définition de fonction, chaque environnement d'exécution de langage comporte des exigences concernant la structuration de votre code source.

Node.js

La structure du répertoire de base des fonctions Node.js est la suivante :

.
├── index.js
└── package.json

Par défaut, Cloud Run tente de charger le code source à partir d'un fichier nommé index.js à la racine du répertoire de votre fonction. Pour spécifier un autre fichier source principal, utilisez le champ main dans votre fichier package.json.

Votre fichier package.json doit également inclure le framework des fonctions pour Node.js en tant que dépendance :

{
  "main": "index.js",
  "dependencies": {
    "@google-cloud/functions-framework": "^3.0.0"
  },
  "type": "module"
}

Le code de votre fichier principal doit définir le point d'entrée de votre fonction et peut importer d'autres codes et modules Node.js. Le fichier principal peut également définir plusieurs points d'entrée de fonction pouvant être déployés séparément.

Pour en savoir plus, consultez la présentation de l'environnement d'exécution Node.js et le framework Functions pour Node.js.

Python

La structure du répertoire de base des fonctions Python est la suivante :

.
├── main.py
└── requirements.txt

Cloud Run charge le code source à partir d'un fichier nommé main.py à la racine du répertoire de votre fonction. Vous devez nommer votre fichier principal main.py.

Votre fichier requirements.txt doit inclure le framework des fonctions pour Python en tant que dépendance :

functions-framework==3.*

Le code de votre fichier main.py doit définir le point d'entrée de votre fonction et peut importer d'autres codes et dépendances externes normalement. Le fichier main.py peut également définir plusieurs points d'entrée de fonction pouvant être déployés séparément.

Pour en savoir plus, consultez la présentation de l'environnement d'exécution Python et le framework Functions pour Python.

Go

La structure du répertoire de base des fonctions Go est la suivante :

.
├── myfunction.go
└── go.mod

Votre fonction doit figurer dans un package Go, à la racine de votre projet. Le package et ses fichiers sources peuvent porter n'importe quel nom, sauf que votre fonction ne peut pas se trouver dans package main. Si vous avez besoin d'un package main, par exemple pour les tests locaux, vous pouvez en créer un dans un sous-répertoire :

.
├── myfunction.go
├── go.mod
└── cmd/
  └── main.go

Votre fichier go.mod doit inclure le framework des fonctions pour Go en tant que dépendance :

module example.com/my-module

require (
  github.com/GoogleCloudPlatform/functions-framework-go v1.5.2
)

Le code de votre package racine doit définir votre point d'entrée de la fonction et peut importer normalement d'autres codes à partir de sous-packages et de dépendances. Votre package peut également définir plusieurs points d'entrée de fonction pouvant être déployés séparément.

Pour en savoir plus, consultez la présentation de l'environnement d'exécution Go et le framework des fonctions pour Go.

Java

La structure du répertoire de base des fonctions Java est la suivante :

.
├── pom.xml
└── src/
  └── main/
      └── java/
          └── MyFunction.java

Vos fichiers source Java doivent se trouver dans le répertoire src/main/java/ et peuvent porter n'importe quel nom. Si vos fichiers source déclarent un package, ajoutez un répertoire supplémentaire sous src/main/java avec le nom du package :

.
├── pom.xml
└── src/
  └── main/
      └── java/
          └── mypackage/
              └── MyFunction.java

Nous vous recommandons de placer les tests associés dans un sous-répertoire src/test/java/.

Votre fichier pom.xml doit inclure le framework des fonctions pour Java en tant que dépendance :

...
    <dependency>
      <groupId>com.google.cloud.functions</groupId>
      <artifactId>functions-framework-api</artifactId>
      <version>1.0.4</version>
    </dependency>
...

Le code de vos fichiers source doit définir votre point d'entrée de la fonction et peut importer d'autres codes et dépendances externes normalement. Vos fichiers sources peuvent également définir plusieurs points d'entrée de fonctions pouvant être déployés séparément.

Pour en savoir plus, consultez la présentation de l'environnement d'exécution Java et le Functions Framework pour Java.

.NET

La structure du répertoire de base des fonctions .NET est la suivante :

.
├── MyFunction.cs
└── MyProject.csproj

Vous pouvez structurer vos projets comme vous le feriez avec tout autre code source .NET. Vous pouvez attribuer n'importe quel nom aux fichiers sources.

Votre fichier de projet doit inclure le framework des fonctions pour .NET en tant que dépendance :

...
    <PackageReference Include="Google.Cloud.Functions.Hosting" Version="1.0.0" />
...

Le code de vos fichiers source doit définir votre point d'entrée de la fonction et peut importer d'autres codes et dépendances externes normalement. Vos fichiers sources peuvent également définir plusieurs points d'entrée de fonctions pouvant être déployés séparément.

Pour en savoir plus, consultez la présentation de l'environnement d'exécution.NET et le framework des fonctions pour .NET.

Ruby

La structure de répertoires de base des fonctions Ruby est la suivante:

.
├── app.rb
├── Gemfile
└── Gemfile.lock

Cloud Run charge le code source à partir d'un fichier nommé app.rb à la racine du répertoire de votre fonction. Votre fichier principal doit être nommé app.rb.

Votre fichier Gemfile doit inclure le framework des fonctions pour Ruby en tant que dépendance :

source "https://rubygems.org"
gem "functions_framework", "~> 1.0"

Le code de votre fichier app.rb doit définir le point d'entrée de votre fonction et peut importer d'autres codes et dépendances externes normalement. Le fichier app.rb peut également définir plusieurs points d'entrée de fonction pouvant être déployés séparément.

Pour en savoir plus, consultez la présentation de l'environnement d'exécution Ruby et le framework des fonctions pour Ruby.

PHP

La structure du répertoire de base des fonctions PHP est la suivante :

.
├── index.php
└── composer.json

Cloud Run charge le code source à partir d'un fichier nommé index.php à la racine du répertoire de votre fonction. Vous devez nommer votre fichier principal index.php.

Votre fichier composer.json doit inclure le framework des fonctions pour PHP en tant que dépendance :

{
  "require": {
    "google/cloud-functions-framework": "^1.1"
  }
}

Le code de votre fichier index.php doit définir le point d'entrée de votre fonction et peut importer d'autres codes et dépendances externes normalement. Le fichier index.php peut également définir plusieurs points d'entrée de fonction pouvant être déployés séparément.

Pour en savoir plus, consultez la présentation de l'environnement d'exécution PHP et le Framework des fonctions pour PHP.

Si vous regroupez plusieurs fonctions dans un seul projet, sachez qu'il est possible qu'elles partagent le même ensemble de dépendances. Cependant, certaines fonctions ne nécessitent pas l'ensemble des dépendances.

Dans la mesure du possible, nous vous recommandons de diviser les codebases multifonctions volumineux et de placer chaque fonction dans son propre répertoire de premier niveau, comme indiqué dans les exemples précédents, avec ses propres fichiers de configuration source et de projet. Cette approche minimise le nombre de dépendances requises par une fonction particulière et réduit la quantité de mémoire dont votre fonction a besoin.

Écrire des fonctions HTTP

Écrivez une fonction HTTP lorsque vous souhaitez appeler une fonction via une requête HTTP(S). Pour autoriser la sémantique HTTP, vous utilisez le framework de fonctions et spécifiez la signature de fonction HTTP pour accepter les arguments HTTP spécifiques.

L'exemple suivant montre un fichier source de fonction HTTP de base pour chaque environnement d'exécution. Pour obtenir un exemple complet et fonctionnel, consultez Déployer une fonction Cloud Run à l'aide de Google Cloud CLI. Pour savoir où trouver votre code source, consultez la section Structure du répertoire source.

Node.js

Module ES

  import { http } from '@google-cloud/functions-framework';
  http('myHttpFunction', (req, res) => {
    // Your code here

    // Send an HTTP response
    res.send('OK');
  });

Ajoutez les dépendances suivantes, y compris "type": "module", à votre fichier package.json :

  {
    "dependencies": {
      "@google-cloud/functions-framework": "^3.0.0"
    },
    "type": "module"
  }

Module CommonJS

  const functions = require('@google-cloud/functions-framework');

  // Register an HTTP function with the Functions Framework
  functions.http('myHttpFunction', (req, res) => {
    // Your code here

    // Send an HTTP response
    res.send('OK');
  });

Ajoutez les dépendances suivantes dans votre fichier package.json :

  {
    "dependencies": {
      "@google-cloud/functions-framework": "^3.0.0"
    }
  }

Avec Node.js, vous enregistrez une fonction de gestionnaire HTTP avec le framework des fonctions pour Node.js. Votre fonction de gestionnaire HTTP doit être une fonction de middleware Express qui accepte les arguments de requête et de réponse et envoie une réponse HTTP.

Cloud Run analyse automatiquement le corps de la requête en fonction de son en-tête Content-Type à l'aide de body-parser. Vous pouvez ainsi accéder aux objets req.body et req.rawBody dans votre gestionnaire HTTP.

Le point d'entrée de la fonction est le nom auprès duquel le gestionnaire est enregistré dans le framework des fonctions. Dans cet exemple, le point d'entrée est myHttpFunction.

Python

import functions_framework

# Register an HTTP function with the Functions Framework
@functions_framework.http
def my_http_function(request):
  # Your code here

  # Return an HTTP response
  return 'OK'

Avec Python, vous enregistrez une fonction de gestionnaire HTTP avec le framework des fonctions pour Python. Votre fonction de gestionnaire HTTP doit accepter un objet de requête Flask en tant qu'argument et renvoyer une valeur que Flask peut convertir en objet de réponse HTTP.

Le point d'entrée de la fonction est le nom auprès duquel le gestionnaire est enregistré dans le framework des fonctions. Dans cet exemple, le point d'entrée est my_http_function.

Go

package myhttpfunction

import (
    "fmt"
    "net/http"

    "github.com/GoogleCloudPlatform/functions-framework-go/functions"
)

func init() {
    // Register an HTTP function with the Functions Framework
    functions.HTTP("MyHTTPFunction", myHTTPFunction)
}

// Function myHTTPFunction is an HTTP handler
func myHTTPFunction(w http.ResponseWriter, r *http.Request) {
    // Your code here

    // Send an HTTP response
    fmt.Fprintln(w, "OK")
}

Avec Go, vous enregistrez une fonction de gestionnaire HTTP avec le framework des fonctions pour Go dans votre fonction init(). Votre fonction de gestionnaire HTTP doit utiliser l'interface http.HandlerFunc standard pour envoyer une réponse HTTP.

Le point d'entrée de la fonction est le nom auprès duquel le gestionnaire est enregistré dans le framework des fonctions. Dans cet exemple, le point d'entrée est MyHTTPFunction.

Votre fonction de gestionnaire HTTP doit implémenter l'interface http.HandlerFunc standard. Elle accepte une interface http.ResponseWriter utilisée par votre fonction pour créer une réponse à la requête, et un pointeur vers une structure http.Request contenant les détails de la requête HTTP entrante.

Java

package myhttpfunction;

import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;

// Define a class that implements the HttpFunction interface
public class MyHttpFunction implements HttpFunction {
  // Implement the service() method to handle HTTP requests
  @Override
  public void service(HttpRequest request, HttpResponse response) throws Exception {
    // Your code here

    // Send an HTTP response
    response.getWriter().write("OK");
  }
}

Avec Java, vous utilisez l'API Java du framework des fonctions pour mettre en œuvre une classe de gestionnaire HTTP avec l'interface HttpFunction. La méthode service() doit envoyer une réponse HTTP.

Le point d'entrée de la fonction est le nom complet de la classe de gestionnaire HTTP, y compris le nom du package. Dans cet exemple, le point d'entrée est myhttpfunction.MyHttpFunction.

Votre méthode service reçoit un objet HttpRequest décrivant la requête HTTP entrante et un objet HttpResponse que votre fonction renseigne avec un message de réponse.

.NET

using Google.Cloud.Functions.Framework;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace MyProject
{
    // Define a class that implements the IHttpFunction interface
    public class MyHttpFunction : IHttpFunction
    {
        // Implement the HandleAsync() method to handle HTTP requests
        public async Task HandleAsync(HttpContext context)
        {
            // Your code here

            // Send an HTTP response
            await context.Response.WriteAsync("OK");
        }
    }
}

Dans les environnements d'exécution .NET, vous utilisez le framework des fonctions pour .NET pour mettre en œuvre une classe de gestionnaire HTTP avec l'interface IHttpFunction. La méthode HandleAsync() accepte un objet ASP.NET HttpContext standard en tant qu'argument et doit envoyer une réponse HTTP.

Le point d'entrée de la fonction est le nom complet de la classe de gestionnaire HTTP, y compris l'espace de noms. Dans cet exemple, le point d'entrée est MyProject.MyHttpFunction.

Ruby

require "functions_framework"

# Register an HTTP function with the Functions Framework
FunctionsFramework.http "my_http_function" do |request|
  # Your code here

  # Return an HTTP response
  "OK"
end

Avec Ruby, vous enregistrez une fonction de gestionnaire HTTP avec le framework des fonctions pour Ruby. Votre fonction de gestionnaire HTTP doit accepter un objet de requête de rack en tant qu'argument et renvoyer une valeur pouvant être utilisée comme réponse HTTP.

Le point d'entrée de la fonction est le nom auprès duquel le gestionnaire est enregistré dans le framework des fonctions. Dans cet exemple, le point d'entrée est my_http_function.

PHP

<?php

use Google\CloudFunctions\FunctionsFramework;
use Psr\Http\Message\ServerRequestInterface;

// Register an HTTP function with the Functions Framework
FunctionsFramework::http('myHttpFunction', 'myHttpHandler');

// Define your HTTP handler
function myHttpHandler(ServerRequestInterface $request): string
{
    // Your code here

    // Return an HTTP response
    return 'OK';
}

Avec PHP, vous enregistrez une fonction de gestionnaire HTTP avec le framework des fonctions pour PHP. Votre fonction de gestionnaire HTTP doit accepter un argument qui met en œuvre l'interface PSR-7 ServerRequestInterface et doit renvoyer une réponse HTTP sous forme de chaîne ou d'objet qui met en œuvre l'interface PSR-7 ResponseInterface.

Le point d'entrée de la fonction est le nom auprès duquel le gestionnaire est enregistré dans le framework des fonctions. Dans cet exemple, le point d'entrée est myHttpFunction.

Requêtes et réponses HTTP

Lorsque vous enregistrez une fonction de gestionnaire HTTP avec le framework des fonctions, votre gestionnaire HTTP peut inspecter la méthode de requête et effectuer différentes actions en fonction de la méthode.

Lorsque vous configurez un fournisseur d'événements pour qu'il envoie des requêtes HTTP à votre fonction Cloud Run, celle-ci envoie une réponse HTTP. Si la fonction crée des tâches en arrière-plan (par exemple, des threads, des futurs, des objets Promise JavaScript, des rappels ou des processus système), vous devez arrêter ou résoudre ces tâches avant d'envoyer une réponse HTTP. Toute tâche qui n'est pas arrêtée avant l'envoi de la réponse HTTP peut ne pas être terminée et peut entraîner un comportement indéfini.

Gérer le CORS

Le partage de ressources inter-origines (CORS, Cross-Origin Resource Sharing) permet aux applications exécutées sur un domaine d'accéder aux ressources d'un autre domaine. Par exemple, vous devrez peut-être autoriser votre domaine à envoyer des requêtes au domaine Cloud Run Functions pour accéder à votre fonction.

Pour autoriser les requêtes inter-origines sur votre fonction, définissez l'en-tête Access-Control-Allow-Origin de manière appropriée dans votre réponse HTTP. Pour les requêtes inter-origines préliminaires, vous devez répondre à la requête OPTIONS préliminaire avec un code de réponse 204 et des en-têtes supplémentaires.

Node.js

const functions = require('@google-cloud/functions-framework');

/**
 * HTTP function that supports CORS requests.
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
functions.http('corsEnabledFunction', (req, res) => {
  // Set CORS headers for preflight requests
  // Allows GETs from any origin with the Content-Type header
  // and caches preflight response for 3600s

  res.set('Access-Control-Allow-Origin', '*');

  if (req.method === 'OPTIONS') {
    // Send response to OPTIONS requests
    res.set('Access-Control-Allow-Methods', 'GET');
    res.set('Access-Control-Allow-Headers', 'Content-Type');
    res.set('Access-Control-Max-Age', '3600');
    res.status(204).send('');
  } else {
    res.send('Hello World!');
  }
});

Python

import functions_framework

@functions_framework.http
def cors_enabled_function(request):
    # For more information about CORS and CORS preflight requests, see:
    # https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request

    # Set CORS headers for the preflight request
    if request.method == "OPTIONS":
        # Allows GET requests from any origin with the Content-Type
        # header and caches preflight response for an 3600s
        headers = {
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Methods": "GET",
            "Access-Control-Allow-Headers": "Content-Type",
            "Access-Control-Max-Age": "3600",
        }

        return ("", 204, headers)

    # Set CORS headers for the main request
    headers = {"Access-Control-Allow-Origin": "*"}

    return ("Hello World!", 200, headers)

Go


// Package http provides a set of HTTP Cloud Functions samples.
package http

import (
	"fmt"
	"net/http"

	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
)

// CORSEnabledFunction is an example of setting CORS headers.
// For more information about CORS and CORS preflight requests, see
// https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request.
func CORSEnabledFunction(w http.ResponseWriter, r *http.Request) {
	// Set CORS headers for the preflight request
	if r.Method == http.MethodOptions {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "POST")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
		w.Header().Set("Access-Control-Max-Age", "3600")
		w.WriteHeader(http.StatusNoContent)
		return
	}
	// Set CORS headers for the main request.
	w.Header().Set("Access-Control-Allow-Origin", "*")
	fmt.Fprint(w, "Hello, World!")
}

func init() {
	functions.HTTP("CORSEnabledFunction", CORSEnabledFunction)
}

Java


import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.HttpURLConnection;

public class CorsEnabled implements HttpFunction {
  // corsEnabled is an example of setting CORS headers.
  // For more information about CORS and CORS preflight requests, see
  // https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request.
  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    // Set CORS headers
    //   Allows GETs from any origin with the Content-Type
    //   header and caches preflight response for 3600s
    response.appendHeader("Access-Control-Allow-Origin", "*");

    if ("OPTIONS".equals(request.getMethod())) {
      response.appendHeader("Access-Control-Allow-Methods", "GET");
      response.appendHeader("Access-Control-Allow-Headers", "Content-Type");
      response.appendHeader("Access-Control-Max-Age", "3600");
      response.setStatusCode(HttpURLConnection.HTTP_NO_CONTENT);
      return;
    }

    // Handle the main request.
    BufferedWriter writer = response.getWriter();
    writer.write("CORS headers set successfully!");
  }
}

.NET

using Google.Cloud.Functions.Framework;
using Microsoft.AspNetCore.Http;
using System.Net;
using System.Threading.Tasks;

namespace Cors;

// For more information about CORS and CORS preflight requests, see
// https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request.
public class Function : IHttpFunction
{
    public async Task HandleAsync(HttpContext context)
    {
        HttpRequest request = context.Request;
        HttpResponse response = context.Response;

        // Set CORS headers
        //   Allows GETs from any origin with the Content-Type
        //   header and caches preflight response for 3600s

        response.Headers.Append("Access-Control-Allow-Origin", "*");
        if (HttpMethods.IsOptions(request.Method))
        {
            response.Headers.Append("Access-Control-Allow-Methods", "GET");
            response.Headers.Append("Access-Control-Allow-Headers", "Content-Type");
            response.Headers.Append("Access-Control-Max-Age", "3600");
            response.StatusCode = (int) HttpStatusCode.NoContent;
            return;
        }

        await response.WriteAsync("CORS headers set successfully!", context.RequestAborted);
    }
}

Ruby

FunctionsFramework.http "cors_enabled_function" do |request|
  # For more information about CORS and CORS preflight requests, see
  # https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
  # for more information.

  # Set CORS headers for the preflight request
  if request.options?
    # Allows GET requests from any origin with the Content-Type
    # header and caches preflight response for an 3600s
    headers = {
      "Access-Control-Allow-Origin"  => "*",
      "Access-Control-Allow-Methods" => "GET",
      "Access-Control-Allow-Headers" => "Content-Type",
      "Access-Control-Max-Age"       => "3600"
    }
    [204, headers, []]
  else
    # Set CORS headers for the main request
    headers = {
      "Access-Control-Allow-Origin" => "*"
    }

    [200, headers, ["Hello World!"]]
  end
end

PHP


use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use GuzzleHttp\Psr7\Response;

function corsEnabledFunction(ServerRequestInterface $request): ResponseInterface
{
    // Set CORS headers for preflight requests
    // Allows GETs from any origin with the Content-Type header
    // and caches preflight response for 3600s
    $headers = ['Access-Control-Allow-Origin' => '*'];

    if ($request->getMethod() === 'OPTIONS') {
        // Send response to OPTIONS requests
        $headers = array_merge($headers, [
            'Access-Control-Allow-Methods' => 'GET',
            'Access-Control-Allow-Headers' => 'Content-Type',
            'Access-Control-Max-Age' => '3600'
        ]);
        return new Response(204, $headers, '');
    } else {
        return new Response(200, $headers, 'Hello World!');
    }
}

Si le CORS n'est pas configuré correctement, vous pouvez rencontrer des erreurs de ce type :

XMLHttpRequest cannot load https://YOUR_FUNCTION_URL.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'https://YOUR_DOMAIN' is therefore not allowed access.

Limites du CORS

Pour les requêtes inter-origines préliminaires, les requêtes OPTIONS préliminaires sont envoyées sans en-tête Authorization. Elles seront donc refusées pour toutes les fonctions HTTP nécessitant une authentification. Comme les requêtes préliminaires échouent, les requêtes principales échoueront également. Pour contourner cette limitation, utilisez l'une des options suivantes :

Écrire des fonctions basées sur des événements

Écrivez une fonction basée sur des événements lorsqu'elle doit être déclenchée directement en réponse à des événements au sein de votre projet Google Cloud , tels que des messages sur un sujet Pub/Sub ou des modifications dans un bucket Cloud Storage.

Les fonctions basées sur des événements sont basées sur CloudEvents, une spécification standard dans l'industrie pour la description des données d'événement de manière courante. Pour en savoir plus sur la spécification CloudEvents, consultez le dépôt GitHub de CloudEvents. Le projet CloudEvents fournit également un ensemble de SDK CloudEvents pour vous aider à utiliser des objets CloudEvents dans votre code.

L'exemple suivant montre un fichier source de fonction basée sur des événements pour chaque environnement d'exécution. Consultez la section Structure du répertoire source pour savoir où trouver votre code source.

Node.js

Module ES

  import { cloudEvent } from "@google-cloud/functions-framework";
  cloudEvent('myCloudEventFunction', cloudEvent => {
    // Your code here
    // Access the CloudEvent data payload using cloudEvent.data
  });

Ajoutez les dépendances suivantes, y compris "type": "module", à votre fichier package.json :

  {
    "dependencies": {
      "@google-cloud/functions-framework": "^3.0.0"
    },
    "type": "module"
  }

Module CommonJS

const functions = require('@google-cloud/functions-framework');

// Register a CloudEvent function with the Functions Framework
functions.cloudEvent('myCloudEventFunction', cloudEvent => {
  // Your code here
  // Access the CloudEvent data payload using cloudEvent.data
});

Ajoutez les dépendances suivantes dans votre fichier package.json :

  {
    "dependencies": {
      "@google-cloud/functions-framework": "^3.0.0"
    }
  }

Dans Node.js, vous enregistrez une fonction de gestionnaire CloudEvent avec le framework des fonctions pour Node.js. Votre fonction de gestionnaire doit accepter un objet CloudEvent comme argument.

Le point d'entrée de la fonction est le nom auprès duquel le gestionnaire est enregistré dans le framework des fonctions. Dans cet exemple, le point d'entrée est myCloudEventFunction.

Python

import functions_framework

# Register a CloudEvent function with the Functions Framework
@functions_framework.cloud_event
def my_cloudevent_function(cloud_event):
  # Your code here
  # Access the CloudEvent data payload via cloud_event.data

En Python, vous enregistrez une fonction de gestionnaire CloudEvent avec le framework des fonctions pour Python. Votre fonction de gestionnaire doit accepter un objet CloudEvent comme argument.

Le point d'entrée de la fonction est le nom de la fonction de gestionnaire enregistrée avec le framework des fonctions. Dans cet exemple, le point d'entrée est my_cloudevent_function.

Go

package mycloudeventfunction

import (
    "context"

    "github.com/GoogleCloudPlatform/functions-framework-go/functions"
    "github.com/cloudevents/sdk-go/v2/event"
)

func init() {
    // Register a CloudEvent function with the Functions Framework
    functions.CloudEvent("MyCloudEventFunction", myCloudEventFunction)
}

// Function myCloudEventFunction accepts and handles a CloudEvent object
func myCloudEventFunction(ctx context.Context, e event.Event) error {
    // Your code here
    // Access the CloudEvent data payload using e.Data() or e.DataAs(...)

    // Returning an error causes its message to be logged.
    // Example:
    err := myInternalFunction() // may return an error
    if err != nil {
        // Append error message to log
        return err
    }

    // Return nil if no error occurred
    return nil
}

Dans Go, vous enregistrez une fonction de gestionnaire CloudEvent avec le framework des fonctions pour Go. Votre fonction de gestionnaire doit accepter un objet CloudEvents event.Event en tant qu'argument.

Le point d'entrée de la fonction est le nom auprès duquel le gestionnaire est enregistré dans le framework des fonctions. Dans cet exemple, le point d'entrée est MyCloudEventFunction.

Java

package mycloudeventfunction;

import com.google.cloud.functions.CloudEventsFunction;
import io.cloudevents.CloudEvent;

// Define a class that implements the CloudEventsFunction interface
public class MyCloudEventFunction implements CloudEventsFunction {
  // Implement the accept() method to handle CloudEvents
  @Override
  public void accept(CloudEvent event) {
    // Your code here
    // Access the CloudEvent data payload using event.getData()
    // To get the data payload as a JSON string, use:
    // new String(event.getData().toBytes())
  }
}

En Java, vous utilisez l'API Java du framework des fonctions pour mettre en œuvre une classe de gestionnaire CloudEvent avec l'interface CloudEventsFunction. La méthode accept() doit accepter un objet CloudEvent comme argument et effectuer tout traitement sur l'événement.

Le point d'entrée de la fonction est le nom complet de la classe de gestionnaire CloudEvent, y compris le nom du package. Dans cet exemple, le point d'entrée est mycloudeventfunction.MyCloudEventFunction.

.NET

using CloudNative.CloudEvents;
using Google.Cloud.Functions.Framework;
using System.Threading;
using System.Threading.Tasks;

namespace MyProject
{
  // Define a class that implements the ICloudEventFunction<T> interface
  public class MyCloudEventFunction : ICloudEventFunction<CloudEventDataType>
  {
      // Implement the HandleAsync() method to handle CloudEvents
      public Task HandleAsync(CloudEvent cloudEvent, CloudEventDataType data, CancellationToken cancellationToken)
      {
          // Your code here
          // The data argument represents the CloudEvent data payload

          // Signal function completion
          return Task.CompletedTask;
      }
  }
}

Dans les environnements d'exécution .NET, vous utilisez le framework des fonctions pour .NET pour mettre en œuvre une classe de gestionnaire CloudEvent avec l'interface ICloudEventFunction<T>. La méthode HandleAsync() accepte un objet CloudEvent et la charge utile de données CloudEvent associée en tant qu'arguments.

Le type de l'argument de charge utile de données CloudEvent, affiché dans l'exemple de code en tant que CloudEventDataType, doit correspondre au type d'événement géré par la fonction. La bibliothèque .NET Google CloudEvents fournit des types de données pour les différents événements acceptés par Google.

Le point d'entrée de la fonction est le nom complet de la classe de gestionnaire CloudEvent, y compris l'espace de noms. Dans cet exemple, le point d'entrée est MyProject.MyCloudEventFunction.

Ruby

require "functions_framework"

# Register a CloudEvent function with the Functions Framework
FunctionsFramework.cloud_event "my_cloudevent_function" do |cloud_event|
  # Your code here
  # Access the CloudEvent data payload via cloud_event.data
end

Dans Ruby, vous enregistrez une fonction de gestionnaire CloudEvent avec le framework des fonctions pour Ruby. Votre fonction de gestionnaire doit accepter un objet Event CloudEvents en tant qu'argument.

Le point d'entrée de la fonction est le nom auprès duquel le gestionnaire est enregistré dans le framework des fonctions. Dans cet exemple, le point d'entrée est my_cloudevent_function.

PHP

<?php

use CloudEvents\V1\CloudEventInterface;
use Google\CloudFunctions\FunctionsFramework;

// Register a CloudEvent function with the Functions Framework
FunctionsFramework::cloudEvent('myCloudEventFunction', 'myCloudEventHandler');

// Define your CloudEvent handler
function myCloudEventHandler(CloudEventInterface $event): void
{
    // Your code here
    // Access the CloudEvent data payload using $event->getData()
}

En PHP, vous enregistrez une fonction de gestionnaire CloudEvent avec le framework des fonctions pour PHP. Votre fonction de gestionnaire doit accepter un argument conforme à l'interface CloudEventInterface.

Le point d'entrée de la fonction est le nom auprès duquel le gestionnaire est enregistré dans le framework des fonctions. Dans cet exemple, le point d'entrée est myCloudEventFunction.

Pour les fonctions basées sur des événements, les données d'événement sont transmises à votre fonction au format CloudEvents, avec une charge utile de données CloudEvent correspondant au type d'événement qui déclenche votre fonction. Consultez Déclencheurs de fonction pour en savoir plus sur les déclencheurs, les types d'événements et les formats de données d'événement associés.

Le dépôt Google Events contient des ressources permettant d'utiliser les événements Cloud émis par Google.

Fonction terminée

Cloud Run considère que l'exécution de la fonction basée sur des événements est terminée lorsque la fonction renvoie un résultat. Si la fonction crée des tâches en arrière-plan (par exemple, des threads, des futurs, des objets Promise JavaScript, des rappels ou des processus système), vous devez arrêter ou résoudre ces tâches avant d'en finir avec la fonction. Toute tâche qui n'est pas arrêtée avant l'arrêt de la fonction peut ne pas être terminée et peut entraîner un comportement indéfini.

Nouvelles tentatives automatiques

Les fonctions basées sur des événements peuvent être configurées pour relancer automatiquement les appels ayant échoué. Pour en savoir plus, consultez Effectuer de nouvelles tentatives d'exécution des fonctions basées sur des événements.

Étapes suivantes