Funciones de HTTP

Las funciones de HTTP se usan cuando quieres invocar directamente tu función a través de HTTP(S). Para permitir la semántica de HTTP, las firmas de las funciones de HTTP aceptan argumentos específicos de HTTP.

Ejemplo de uso

El siguiente ejemplo muestra cómo procesar una solicitud HTTP POST que contiene un parámetro name:

Node.js

const escapeHtml = require('escape-html');

/**
 * HTTP Cloud Function.
 *
 * @param {Object} req Cloud Function request context.
 *                     More info: https://expressjs.com/en/api.html#req
 * @param {Object} res Cloud Function response context.
 *                     More info: https://expressjs.com/en/api.html#res
 */
exports.helloHttp = (req, res) => {
  res.send(`Hello ${escapeHtml(req.query.name || req.body.name || 'World')}!`);
};

Python (Beta)

from flask import escape

def hello_http(request):
    """HTTP Cloud Function.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """
    request_json = request.get_json(silent=True)
    request_args = request.args

    if request_json and 'name' in request_json:
        name = request_json['name']
    elif request_args and 'name' in request_args:
        name = request_args['name']
    else:
        name = 'World'
    return 'Hello {}!'.format(escape(name))

Go (Beta)

// Package helloworld provides a set of Cloud Function samples.
package helloworld

import (
	"encoding/json"
	"fmt"
	"html"
	"net/http"
)

// HelloHTTP is an HTTP Cloud Function with a request parameter.
func HelloHTTP(w http.ResponseWriter, r *http.Request) {
	var d struct {
		Name string `json:"name"`
	}
	if err := json.NewDecoder(r.Body).Decode(&d); err != nil {
		fmt.Fprint(w, "Hello, World!")
		return
	}
	if d.Name == "" {
		fmt.Fprint(w, "Hello, World!")
		return
	}
	fmt.Fprintf(w, "Hello, %s!", html.EscapeString(d.Name))
}

El siguiente comando muestra cómo llamar a la función y pasarle un parámetro con curl:

curl -X POST HTTP_TRIGGER_ENDPOINT -H "Content-Type:application/json"  -d '{"name":"Jane"}'

en el que HTTP_TRIGGER_ENDPOINT es la URL de la función, que se obtiene cuando esta se implementa. Para obtener más información, consulta activadores HTTP.

Analiza solicitudes HTTP

El cuerpo de la solicitud se analiza automáticamente según el encabezado content-type y se encuentra disponible mediante los argumentos de la función de HTTP.

El siguiente ejemplo muestra cómo leer las solicitudes HTTP en varios formatos:

Node.js

const escapeHtml = require('escape-html');

/**
 * Responds to an HTTP request using data from the request body parsed according
 * to the "content-type" header.
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.helloContent = (req, res) => {
  let name;

  switch (req.get('content-type')) {
    // '{"name":"John"}'
    case 'application/json':
      name = req.body.name;
      break;

    // 'John', stored in a Buffer
    case 'application/octet-stream':
      name = req.body.toString(); // Convert buffer to a string
      break;

    // 'John'
    case 'text/plain':
      name = req.body;
      break;

    // 'name=John' in the body of a POST request (not the URL)
    case 'application/x-www-form-urlencoded':
      name = req.body.name;
      break;
  }

  res.status(200).send(`Hello ${escapeHtml(name || 'World')}!`);
};

Python (Beta)

from flask import escape

def hello_content(request):
    """ Responds to an HTTP request using data from the request body parsed
    according to the "content-type" header.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """
    content_type = request.headers['content-type']
    if content_type == 'application/json':
        request_json = request.get_json(silent=True)
        if request_json and 'name' in request_json:
            name = request_json['name']
        else:
            raise ValueError("JSON is invalid, or missing a 'name' property")
    elif content_type == 'application/octet-stream':
        name = request.data
    elif content_type == 'text/plain':
        name = request.data
    elif content_type == 'application/x-www-form-urlencoded':
        name = request.form.get('name')
    else:
        raise ValueError("Unknown content type: {}".format(content_type))
    return 'Hello {}!'.format(escape(name))

Go (Beta)

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

import (
	"encoding/json"
	"fmt"
	"html"
	"io/ioutil"
	"log"
	"net/http"
)

// HelloContentType is an HTTP Cloud function.
// It uses the Content-Type header to identify the request payload format.
func HelloContentType(w http.ResponseWriter, r *http.Request) {
	var name string

	switch r.Header.Get("Content-Type") {
	case "application/json":
		var d struct {
			Name string `json:"name"`
		}
		err := json.NewDecoder(r.Body).Decode(&d)
		if err != nil {
			log.Printf("error parsing application/json: %v", err)
		} else {
			name = d.Name
		}
	case "application/octet-stream":
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			log.Printf("error parsing text/plain: %v", err)
		} else {
			name = string(body)
		}
	case "text/plain":
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			log.Printf("error parsing text/plain: %v", err)
		} else {
			name = string(body)
		}
	case "application/x-www-form-urlencoded":
		if err := r.ParseForm(); err != nil {
			log.Printf("error parsing application/x-www-form-urlencoded: %v", err)
		} else {
			name = r.FormValue("name")
		}
	}

	if name == "" {
		name = "World"
	}

	fmt.Fprintf(w, "Hello, %s!", html.EscapeString(name))
}

Administra solicitudes de CORS

El uso compartido de recursos multiorigen (CORS) es una manera de permitir que las aplicaciones que se ejecutan en un dominio accedan a contenido de otro dominio, por ejemplo, cuando se le permite a yourdomain.com hacer solicitudes a region-project.cloudfunctions.net/yourfunction.

Si CORS no está configurado correctamente, es probable que recibas errores similares a los siguientes:

XMLHttpRequest cannot load https://region-project.cloudfunctions.net/function.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://yourdomain.com' is therefore not allowed access.

CORS consiste en dos solicitudes:

  • Una solicitud de comprobación previa de OPTIONS
  • Una solicitud principal que sigue a la solicitud de OPTIONS

La solicitud de comprobación previa contiene encabezados que indican qué método (Access-Control-Request-Method) y qué encabezados adicionales (Access-Control-Request-Headers) se enviarán en la solicitud principal, así como el origen de esta última (Origin).

Para administrar solicitudes de comprobación previa, debes configurar los encabezados de Access-Control-Allow-* para que coincidan con las solicitudes que quieres aceptar:

Node.js

/**
 * HTTP function that supports CORS requests.
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.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 {
    // Set CORS headers for the main request
    res.set('Access-Control-Allow-Origin', '*');
    res.send('Hello World!');
  }
};

Python (Beta)

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
    # for more information.

    # 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 (Beta)

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

import (
	"fmt"
	"net/http"
)

// 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!")
}

De manera alternativa, puedes usar una biblioteca de terceros para que administre CORS por ti.

Autenticación y CORS

Si planeas enviar una solicitud con un encabezado de Authorization, debes hacer lo siguiente:

  1. Agrega el encabezado de Authorization a Access-Control-Allow-Headers.
  2. Configura el encabezado de Access-Control-Allow-Credentials en true.
  3. Configura un origen específico en Access-Control-Allow-Origin (no se aceptan comodines).

Node.js

/**
 * HTTP function that supports CORS requests with credentials.
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.corsEnabledFunctionAuth = (req, res) => {
  // Set CORS headers for preflight requests
  // Allows GETs from origin https://mydomain.com with Authorization header

  res.set('Access-Control-Allow-Origin', 'https://mydomain.com');
  res.set('Access-Control-Allow-Credentials', 'true');

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

Python (Beta)

def cors_enabled_function_auth(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 preflight requests
    if request.method == 'OPTIONS':
        # Allows GET requests from origin https://mydomain.com with
        # Authorization header
        headers = {
            'Access-Control-Allow-Origin': 'https://mydomain.com',
            'Access-Control-Allow-Methods': 'GET',
            'Access-Control-Allow-Headers': 'Authorization',
            'Access-Control-Max-Age': '3600',
            'Access-Control-Allow-Credentials': 'true'
        }
        return ('', 204, headers)

    # Set CORS headers for main requests
    headers = {
        'Access-Control-Allow-Origin': 'https://mydomain.com',
        'Access-Control-Allow-Credentials': 'true'
    }

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

Go (Beta)

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

import (
	"fmt"
	"net/http"
)

// CORSEnabledFunctionAuth is an example of setting CORS headers with
// authentication enabled.
// For more information about CORS and CORS preflight requests, see
// https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request.
func CORSEnabledFunctionAuth(w http.ResponseWriter, r *http.Request) {
	// Set CORS headers for the preflight request
	if r.Method == http.MethodOptions {
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		w.Header().Set("Access-Control-Allow-Headers", "Authorization")
		w.Header().Set("Access-Control-Allow-Methods", "POST")
		w.Header().Set("Access-Control-Allow-Origin", "https://example.com")
		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-Credentials", "true")
	w.Header().Set("Access-Control-Allow-Origin", "https://example.com")
	fmt.Fprint(w, "Hello World!")
}

Hosting en el mismo dominio

En lugar de implementar CORS, podrías alojar tu sitio web y tus funciones en el mismo dominio. Puesto que las solicitudes ahora provendrían del mismo origen, CORS no se aplicará. Esta acción simplifica tu código de manera considerable.

La manera más sencilla de hacerlo es integrar Firebase Hosting a Google Cloud Functions.

Administra métodos de HTTP

El siguiente ejemplo muestra cómo administrar varios métodos de HTTP (por ejemplo, GET y PUT):

Node.js

function handleGET(req, res) {
  // Do something with the GET request
  res.status(200).send('Hello World!');
}

function handlePUT(req, res) {
  // Do something with the PUT request
  res.status(403).send('Forbidden!');
}

/**
 * Responds to a GET request with "Hello World!". Forbids a PUT request.
 *
 * @example
 * gcloud functions call helloHttp
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.helloHttp = (req, res) => {
  switch (req.method) {
    case 'GET':
      handleGET(req, res);
      break;
    case 'PUT':
      handlePUT(req, res);
      break;
    default:
      res.status(405).send({error: 'Something blew up!'});
      break;
  }
};

Python (Beta)

def hello_method(request):
    """ Responds to a GET request with "Hello world!". Forbids a PUT request.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
         Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """
    from flask import abort

    if request.method == 'GET':
        return 'Hello World!'
    elif request.method == 'PUT':
        return abort(403)
    else:
        return abort(405)

Go (Beta)

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

import (
	"fmt"
	"net/http"
)

// HelloHTTPMethod is an HTTP Cloud function.
// It uses the request method to differentiate the response.
func HelloHTTPMethod(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodGet:
		fmt.Fprint(w, "Hello World!")
	case http.MethodPut:
		http.Error(w, "403 - Forbidden", http.StatusForbidden)
	default:
		http.Error(w, "405 - Method Not Allowed", http.StatusMethodNotAllowed)
	}
}

Administra tipos de contenido

Cloud Functions analiza los tipos de contenido del cuerpo de las solicitudes de application/json y application/x-www-form-urlencoded como se muestra anteriormente. Los tipos de contenido con texto sin formato (text/plain) se pasan como strings con UTF-8 como la codificación predeterminada (o una codificación personalizada proporcionada en el encabezado content-type).

Se puede acceder a otros tipos de contenido mediante la inspección del argumento de tu función de HTTP. Los métodos para hacerlo varían según el lenguaje.

En el siguiente ejemplo, se administra una solicitud con un tipo de contenido de text/xml:

Node.js

La propiedad rawBody contiene bytes sin analizar del cuerpo de la solicitud.

/**
 * Parses a document of type 'text/xml'
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.parseXML = (req, res) => {
  // Convert the request to a Buffer and a string
  // Use whichever one is accepted by your XML parser
  let data = req.rawBody;
  let xmlData = data.toString();

  const parseString = require('xml2js').parseString;

  parseString(xmlData, (err, result) => {
    if (err) {
      console.error(err);
      res.status(500).end();
      return;
    }
    res.send(result);
  });
};

Python (Beta)

import json
import xmltodict

def parse_xml(request):
    """ Parses a document of type 'text/xml'
    Args:
        request (flask.Request): The request object.
    Returns:
        The response text, or any set of values that can be turned into a
         Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """
    data = xmltodict.parse(request.data)
    return json.dumps(data, indent=2)

Go (Beta)

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

import (
	"encoding/xml"
	"fmt"
	"html"
	"io/ioutil"
	"net/http"
)

// ParseXML is an example of parsing a text/xml request.
func ParseXML(w http.ResponseWriter, r *http.Request) {
	var d struct {
		Name string
	}
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Could not read request", http.StatusBadRequest)
	}
	if err := xml.Unmarshal(b, &d); err != nil {
		http.Error(w, "Could not parse request", http.StatusBadRequest)
	}
	if d.Name == "" {
		d.Name = "World"
	}
	fmt.Fprintf(w, "Hello, %v!", html.EscapeString(d.Name))
}

Datos de varias partes

El siguiente ejemplo muestra cómo procesar datos con un tipo de contenido de multipart/form-data. Según el lenguaje que hayas elegido, es posible que debas usar una biblioteca de análisis.

Node.js

/**
 * Parses a 'multipart/form-data' upload request
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
const path = require('path');
const os = require('os');
const fs = require('fs');

// Node.js doesn't have a built-in multipart/form-data parsing library.
// Instead, we can use the 'busboy' library from NPM to parse these requests.
const Busboy = require('busboy');

exports.uploadFile = (req, res) => {
  if (req.method === 'POST') {
    const busboy = new Busboy({headers: req.headers});
    const tmpdir = os.tmpdir();

    // This object will accumulate all the fields, keyed by their name
    const fields = {};

    // This object will accumulate all the uploaded files, keyed by their name.
    const uploads = {};

    // This code will process each non-file field in the form.
    busboy.on('field', (fieldname, val) => {
      // TODO(developer): Process submitted field values here
      console.log(`Processed field ${fieldname}: ${val}.`);
      fields[fieldname] = val;
    });

    let fileWrites = [];

    // This code will process each file uploaded.
    busboy.on('file', (fieldname, file, filename) => {
      // Note: os.tmpdir() points to an in-memory file system on GCF
      // Thus, any files in it must fit in the instance's memory.
      console.log(`Processed file ${filename}`);
      const filepath = path.join(tmpdir, filename);
      uploads[fieldname] = filepath;

      const writeStream = fs.createWriteStream(filepath);
      file.pipe(writeStream);

      // File was processed by Busboy; wait for it to be written to disk.
      const promise = new Promise((resolve, reject) => {
        file.on('end', () => {
          writeStream.end();
        });
        writeStream.on('finish', resolve);
        writeStream.on('error', reject);
      });
      fileWrites.push(promise);
    });

    // Triggered once all uploaded files are processed by Busboy.
    // We still need to wait for the disk writes (saves) to complete.
    busboy.on('finish', () => {
      Promise.all(fileWrites).then(() => {
        // TODO(developer): Process saved files here
        for (const name in uploads) {
          const file = uploads[name];
          fs.unlinkSync(file);
        }
        res.send();
      });
    });

    busboy.end(req.rawBody);
  } else {
    // Return a "method not allowed" error
    res.status(405).end();
  }
};

Python (Beta)

import os
import tempfile
from werkzeug.utils import secure_filename

# Helper function that computes the filepath to save files to
def get_file_path(filename):
    # Note: tempfile.gettempdir() points to an in-memory file system
    # on GCF. Thus, any files in it must fit in the instance's memory.
    file_name = secure_filename(filename)
    return os.path.join(tempfile.gettempdir(), file_name)

def parse_multipart(request):
    """ Parses a 'multipart/form-data' upload request
    Args:
        request (flask.Request): The request object.
    Returns:
        The response text, or any set of values that can be turned into a
         Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """

    # This code will process each non-file field in the form
    fields = {}
    data = request.form.to_dict()
    for field in data:
        fields[field] = data[field]
        print('Processed field: %s' % field)

    # This code will process each file uploaded
    files = request.files.to_dict()
    for file_name, file in files.items():
        file.save(get_file_path(file_name))
        print('Processed file: %s' % file_name)

    # Clear temporary directory
    for file_name in files:
        file_path = get_file_path(file_name)
        os.remove(file_path)

    return "Done!"

Go (Beta)

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

import (
	"fmt"
	"log"
	"net/http"
)

// UploadFile processes a 'multipart/form-data' upload request.
func UploadFile(w http.ResponseWriter, r *http.Request) {
	const maxMemory = 2 * 1024 * 1024 // 2 megabytes.

	// ParseMultipartForm parses a request body as multipart/form-data.
	// The whole request body is parsed and up to a total of maxMemory bytes of
	// its file parts are stored in memory, with the remainder stored on
	// disk in temporary files.
	if err := r.ParseMultipartForm(maxMemory); err != nil {
		http.Error(w, "Unable to parse form", http.StatusBadRequest)
		log.Printf("Error parsing form: %v", err)
		return
	}

	// Be sure to remove all temporary files after your function is finished.
	defer func() {
		if err := r.MultipartForm.RemoveAll(); err != nil {
			http.Error(w, "Error cleaning up form files", http.StatusInternalServerError)
			log.Printf("Error cleaning up form files: %v", err)
		}
	}()

	// r.MultipartForm.File contains *multipart.FileHeader values for every
	// file in the form. You can access the file contents using
	// *multipart.FileHeader's Open method.
	for _, headers := range r.MultipartForm.File {
		for _, h := range headers {
			fmt.Fprintf(w, "File uploaded: %q (%v bytes)", h.Filename, h.Size)
			// Use h.Open() to read the contents of the file.
		}
	}

}

Sube archivos a través de Cloud Storage

Un caso práctico común de Cloud Functions es el procesamiento de archivos. Para archivos grandes o aquellos que requieren un almacenamiento continuo más allá del alcance de una sola solicitud, puedes usar Cloud Storage como un punto de entrada para tus cargas de archivos. Para lograrlo, debes generar una URL firmada que proporcione acceso de escritura temporal al depósito de Cloud Storage.

Si usas Cloud Functions directamente, genera una URL firmada con la biblioteca de cliente de Cloud Storage.

El proceso para subir archivos a una función de Cloud Functions con Cloud Storage consta de tres pasos que se describen a continuación:

  1. Los clientes llaman a una función de Cloud Functions directamente para recuperar la URL firmada.

  2. Luego, los clientes envían los datos del archivo a la URL firmada a través de la solicitud HTTP PUT.

  3. Se activa una segunda función de Cloud Functions mediante la mutación en el depósito de Storage para procesar aún más el archivo.

Puedes ver el siguiente ejemplo de uso de la biblioteca cliente de Cloud Storage para generar una URL firmada.

Cloud Functions cuenta con una “credencial predeterminada de aplicación” que, por lo general, no incluye el permiso iam.serviceAccounts.signBlob. Para permitir que ocurra lo anterior, primero será necesario que te asegures de que la cuenta de servicio de tu función tiene el rol apropiado. Para lograrlo, usa GCP Console o la herramienta de línea de comandos de gcloud:

Console

Para asegurarte de que la cuenta de servicio de tu función tiene el rol apropiado, puedes modificar directamente los roles de IAM de una cuenta con los siguientes pasos:

  1. Ve a Google Cloud Platform Console.

    Ir a Google Cloud Platform Console

  2. Selecciona la cuenta correcta y elige Editor > Cuentas de servicio > Creador de token de cuenta de servicio.

gcloud

Para asegurarte de que la cuenta de servicio de la función tiene el rol apropiado, ejecuta el siguiente comando. El rol serviceAccountTokenCreator definido previamente tiene el permiso iam.serviceAccounts.signBlob que necesitas:

gcloud projects add-iam-policy-binding YOUR_PROJECT \
--member serviceAccount:YOUR_SERVICE_ACCOUNT --role roles/iam.serviceAccountTokenCreator

Puedes determinar la cuenta de servicio que usa una función con GCP Console o la herramienta de línea de comandos de gcloud:

Console

Sigue estos pasos para determinar la cuenta de servicio que usan tus funciones con GCP Console:

  1. Ve a Google Cloud Platform Console.

    Ir a Google Cloud Platform Console

  2. En la lista, selecciona la función que quieres inspeccionar.

Puedes ver la cuenta de servicio en la página de detalles de la función.

gcloud

Para determinar la cuenta de servicio que usan tus funciones, ejecuta el siguiente comando y busca la propiedad serviceAccountEmail:

gcloud beta functions describe YOUR_FUNCTION_NAME

Te mostramos un ejemplo de cómo generar una URL firmada que supone que el cliente enviará el depósito deseado y el nombre de archivo en el cuerpo de la solicitud, como sigue:

Node.js

const {Storage} = require('@google-cloud/storage');
const storage = new Storage();

/**
 * HTTP function that generates a signed URL
 * The signed URL can be used to upload files to Google Cloud Storage (GCS)
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.getSignedUrl = (req, res) => {
  if (req.method === 'POST') {
    // TODO(developer) check that the user is authorized to upload

    // Get a reference to the destination file in GCS
    const file = storage.bucket(req.body.bucket).file(req.body.filename);

    // Create a temporary upload URL
    const expiresAtMs = Date.now() + 300000; // Link expires in 5 minutes
    const config = {
      action: 'write',
      expires: expiresAtMs,
      contentType: req.body.contentType,
    };

    file.getSignedUrl(config, function(err, url) {
      if (err) {
        console.error(err);
        res.status(500).end();
        return;
      }
      res.send(url);
    });
  } else {
    // Return a "method not allowed" error
    res.status(405).end();
  }
};

Python (Beta)

from datetime import datetime, timedelta
from flask import abort
from google.cloud import storage
storage_client = storage.Client()

def get_signed_url(request):
    if request.method != 'POST':
        return abort(405)

    request_json = request.get_json()

    # Get a reference to the destination file in GCS
    file_name = request_json['filename']
    file = storage_client.bucket('my-bucket').blob(file_name)

    # Create a temporary upload URL
    expires_at_ms = datetime.now() + timedelta(seconds=30)
    url = file.generate_signed_url(expires_at_ms,
                                   content_type=request_json['contentType'])

    return url

Cuando el cliente sube un archivo a la URL firmada, puedes activar una segunda función desde esta mutación si quieres tomar medidas adicionales para la carga. Consulta el instructivo de Cloud Storage para obtener más información sobre cómo activar una función de Cloud Functions después de los cambios a un depósito de Cloud Storage.

Pasos siguientes

¿Te ha resultado útil esta página? Enviar comentarios:

Enviar comentarios sobre...

Documentación de Cloud Functions