Associazioni HTTP e webhook in Funzioni di Azure

Questo articolo illustra come configurare e usare trigger e associazioni HTTP in Funzioni di Azure. In questo modo è possibile usare Funzioni di Azure per compilare API senza server e rispondere ai webhook.

Funzioni di Azure fornisce le associazioni seguenti:

Informazioni di riferimento per gli sviluppatori delle Funzioni di Azure. Se non si ha familiarità con le Funzioni di Azure, iniziare con le seguenti risorse:

Suggerimento

Se si prevede di usare binding HTTP o WebHook, evitare l'esaurimento delle porte che può essere causato da un'errata creazione di istanze di HttpClient. Per altre informazioni, vedere l'articolo Improper Instantiation antipattern (Antipattern non valido per la creazione di istanze).

Trigger HTTP

Il trigger HTTP esegue la funzione in risposta a una richiesta HTTP. È possibile personalizzarlo per rispondere a un particolare URL o set di metodi HTTP. Un trigger HTTP può essere configurato anche per rispondere ai webhook.

Se si usa il portale di Funzioni, è anche possibile iniziare subito a usare un modello predefinito. Selezionare Nuova funzione e scegliere "API e webhook" dal menu a discesa Scenario. Selezionare uno dei modelli e fare clic su Crea.

Per impostazione predefinita, un trigger HTTP risponde alla richiesta con un codice di stato HTTP 200 OK e un corpo vuoto. Per modificare la risposta, configurare un'associazione di output HTTP

Configurazione di un trigger HTTP

Un trigger HTTP è definito da un oggetto JSON nella matrice bindings di function.json, come illustrato nell'esempio seguente:

{
    "name": "req",
    "type": "httpTrigger",
    "direction": "in",
    "authLevel": "function",
    "methods": [ "get" ],
    "route": "values/{id}"
},

L'associazione supporta le proprietà seguenti:

Proprietà Descrizione
nome Obbligatoria. Nome della variabile usato nel codice della funzione per la richiesta o il corpo della richiesta. Vedere Uso di un trigger HTTP dal codice.
type Obbligatoria. Deve essere impostata su httpTrigger.
direction Obbligatoria. Deve essere impostata su in.
authLevel Determina le eventuali chiavi che devono essere presenti nella richiesta per richiamare la funzione. Il valore può essere uno dei seguenti:
  • anonymous—Non è richiesta nessuna chiave API.
  • function—È richiesta una chiave API specifica della funzione. Questo è il valore predefinito se non ne viene specificato nessuno.
  • admin—È richiesta la chiave master.
Per altre informazioni, vedere Uso delle chiavi.
methods Matrice di metodi HTTP a cui la funzione risponde. Se non viene specificata, la funzione risponde a tutti i metodi HTTP. Vedere Personalizzazione dell'endpoint HTTP.
route Definisce il modello di route, controllando a quali URL di richiesta risponde la funzione. Il valore predefinito, se non ne viene specificato nessuno, è <functionname>. Per altre informazioni, vedere Personalizzazione dell'endpoint HTTP.
webHookType Configura il trigger HTTP perché funga da ricevitore webhook per il provider specificato. Non usare la proprietà methods quando si usa questa impostazione. Il valore può essere uno dei seguenti:
  • genericJson—Endpoint di webhook per uso generico senza logica per un provider specifico.
  • github—La funzione risponde ai webhook GitHub. Non usare la proprietà authLevel quando si usa questo valore.
  • slack—La funzione risponde ai webhook Slack. Non usare la proprietà authLevel quando si usa questo valore.
Per altre informazioni, vedere Risposta ai webhook.

Uso di un trigger HTTP dal codice

Per le funzioni C# e F#, è possibile dichiarare il tipo di input del trigger come HttpRequestMessage o un tipo .NET personalizzato. Se si sceglie HttpRequestMessage, si ottiene accesso completo all'oggetto richiesta. Per un tipo .NET personalizzato, Funzioni cerca di analizzare il corpo della richiesta JSON per impostare le proprietà dell'oggetto.

Per le funzioni Node.js, il runtime di Funzioni fornisce il corpo della richiesta invece dell'oggetto richiesta. Per altre informazioni, vedere Esempi di trigger HTTP.

Associazione di output della risposta HTTP

Usare l'associazione di output HTTP per rispondere al mittente della richiesta HTTP. Questa associazione richiede un trigger HTTP e consente di personalizzare la risposta associata alla richiesta del trigger. Se non viene specificato un binding di output HTTP, un trigger HTTP restituisce HTTP 200 OK con un corpo vuoto.

Configurazione di un'associazione di output HTTP

Un binding di output HTTP è definito da un oggetto JSON nella matrice bindings di function.json, come illustrato nell'esempio seguente:

{
    "name": "res",
    "type": "http",
    "direction": "out"
}

Il binding supporta le proprietà obbligatorie seguenti:

Proprietà Descrizione
nome Nome della variabile usato nel codice della funzione per la risposta. Vedere Uso di un'associazione di output HTTP dal codice.
type Il valore deve essere impostato su http.
direction Il valore deve essere impostato su out.

Uso di un'associazione di output HTTP dal codice

È possibile usare il parametro di output per rispondere al chiamante HTTP o webhook. È anche possibile usare modelli di risposta standard del linguaggio. Per esempi di risposte, vedere Esempi di trigger HTTP ed Esempi di webhook.

Risposta ai webhook

Un trigger HTTP con la proprietà webHookType è configurato per rispondere ai webhook. La configurazione di base usa l'impostazione "genericJson", che limita le richieste solo a quelle che usano HTTP POST e con il tipo di contenuto application/json.

Il trigger può anche essere personalizzato per un provider di webhook specifico, ad esempio, GitHub o Slack. Se viene specificato un provider, il runtime di Funzioni può gestire automaticamente la logica di convalida del provider.

Configurazione di GitHub come provider di webhook

Per rispondere ai webhook GitHub, creare prima di tutto la funzione con un trigger HTTP e impostare la proprietà webHookType su github. Copiare quindi l'URL e la chiave API nella pagina Aggiungi webhook del repository GitHub.

Per un esempio, vedere Creare una funzione attivata da un webhook GitHub.

Configurazione di Slack come provider di webhook

Il webhook Slack genera automaticamente un token invece di consentire di specificarlo, quindi è necessario configurare una chiave specifica della funzione con il token da Slack. Vedere Uso delle chiavi.

Personalizzazione dell'endpoint HTTP

Per impostazione predefinita, quando si crea una funzione per un trigger HTTP o un webhook, la funzione può essere indirizzata con una route nel formato seguente:

http://<yourapp>.azurewebsites.net/api/<funcname> 

È possibile personalizzare questa route tramite la proprietà route facoltativa nell'associazione di input del trigger HTTP. Ad esempio, il file function.json seguente definisce una proprietà route per un trigger HTTP:

{
    "bindings": [
    {
        "type": "httpTrigger",
        "name": "req",
        "direction": "in",
        "methods": [ "get" ],
        "route": "products/{category:alpha}/{id:int?}"
    },
    {
        "type": "http",
        "name": "res",
        "direction": "out"
    }
    ]
}

Con questa configurazione, la funzione può ora essere indirizzata con la route seguente invece che con quella originale.

http://<yourapp>.azurewebsites.net/api/products/electronics/357

In questo modo il codice della funzione può supportare due parametri nell'indirizzo, category e id. I parametri sono compatibili con qualsiasi vincolo di route dell'API Web. Il codice di funzione C# seguente usa entrambi i parametri.

public static Task<HttpResponseMessage> Run(HttpRequestMessage req, string category, int? id, 
                                                TraceWriter log)
{
    if (id == null)
        return  req.CreateResponse(HttpStatusCode.OK, $"All {category} items were requested.");
    else
        return  req.CreateResponse(HttpStatusCode.OK, $"{category} item with id = {id} has been requested.");
}

Di seguito è mostrato il codice di funzione Node.js per usare gli stessi parametri di route.

module.exports = function (context, req) {

    var category = context.bindingData.category;
    var id = context.bindingData.id;

    if (!id) {
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "All " + category + " items were requested."
        };
    }
    else {
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: category + " item with id = " + id + " was requested."
        };
    }

    context.done();
} 

Per impostazione predefinita, tutte le route di funzione sono precedute da api. È inoltre possibile personalizzare o rimuovere il prefisso con la proprietà http.routePrefix nel file host.json. Nell'esempio seguente viene rimosso il prefisso della route api usando una stringa vuota per il prefisso nel file host.json.

{
    "http": {
    "routePrefix": ""
    }
}

Per informazioni dettagliate su come aggiornare il file host.json per la funzione, vedere Come aggiornare i file nelle app per le funzioni.

Per informazioni su altre proprietà che è possibile configurare nel file host.json, vedere il riferimento su host.json.

Uso delle chiavi

I trigger HTTP consentono di usare le chiavi per una maggiore sicurezza. Un trigger HTTP standard può usare queste chiavi come chiave API, richiedendone la presenza nella richiesta. I webhook possono usare le chiavi per autorizzare le richieste in svariati modi, a seconda di ciò che il provider supporta.

Le chiavi vengono archiviate come parte dell'app per le funzioni in Azure e crittografate inattive. Per visualizzare le chiavi, crearne di nuove o aggiornare le chiavi con nuovi valori, passare a una delle funzioni nel portale e selezionare "Gestisci".

Esistono due tipi di chiavi:

  • Chiavi host: queste chiavi vengono condivise da tutte le funzioni nell'app per le funzioni. Quando vengono usate come chiave API, consentono l'accesso a tutte le funzioni nell'app per le funzioni.
  • Chiavi di funzione: queste chiavi si applicano solo alle funzioni specifiche sotto le quali vengono definite. Quando vengono usate come chiave API, consentono l'accesso solo a tale funzione.

Ogni chiave viene denominata per riferimento ed esiste una chiave predefinita (denominata "default") a livello di funzione e di host. La chiave master è una chiave host predefinita denominata "_master", definita per ogni app per le funzioni. Questa chiave non può essere revocata. Fornisce l'accesso amministrativo alle API di runtime. Per usare "authLevel": "admin" nel file JSON di binding è necessario presentare questa chiave nella richiesta. Qualsiasi altra chiave provoca un errore di autorizzazione.

Importante

Date le autorizzazioni elevate concesse dalla chiave master, è consigliabile non condividere questa chiave con terze parti o distribuirla in applicazioni client native. Fare attenzione quando si sceglie il livello di autorizzazione di amministratore.

Autorizzazione della chiave API

Per impostazione predefinita, un trigger HTTP richiede una chiave API nella richiesta HTTP. La richiesta HTTP è quindi in genere simile alla seguente:

https://<yourapp>.azurewebsites.net/api/<function>?code=<ApiKey>

La chiave può essere inclusa in una variabile della stringa di query denominata code, come sopra, oppure in un'intestazione HTTP x-functions-key. Il valore della chiave può essere una chiave di funzione definita per la funzione o una chiave host.

È possibile consentire le richieste anonime, che non richiedono chiavi. È anche possibile richiedere l'uso della chiave master. Per modificare il livello di autorizzazione predefinito, usare la proprietà authLevel nel file JSON di binding. Per altre informazioni, vedere Trigger HTTP.

Chiavi e webhook

L'autorizzazione webhook viene gestita dal componente ricevitore dei webhook, che fa parte del trigger HTTP, e il meccanismo varia in base al tipo di webhook. Ogni meccanismo tuttavia si basa su una chiave. Per impostazione predefinita, viene usata la chiave di funzione denominata "default". Per usare una chiave diversa, configurare il provider di webhook per inviare il nome della chiave con la richiesta in uno dei modi seguenti:

  • Stringa di query: il provider passa il nome della chiave nel parametro della stringa di query clientid, ad esempio https://<yourapp>.azurewebsites.net/api/<funcname>?clientid=<keyname>.
  • Intestazione della richiesta: il provider passa il nome della chiave nell'intestazione x-functions-clientid.

Nota

Le chiavi di funzione hanno la precedenza sulle chiavi host. Se sono definite due chiavi con lo stesso nome, viene usata sempre la chiave di funzione.

Esempi di trigger HTTP

Si supponga che il trigger HTTP seguente sia presente nella matrice bindings di function.json:

{
    "name": "req",
    "type": "httpTrigger",
    "direction": "in",
    "authLevel": "function"
},

Vedere l'esempio specifico del linguaggio, che cerca un parametro name nella stringa di query o nel corpo della richiesta HTTP.

Esempio di trigger HTTP in C#

using System.Net;
using System.Threading.Tasks;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    log.Info($"C# HTTP trigger function processed a request. RequestUri={req.RequestUri}");

    // parse query parameter
    string name = req.GetQueryNameValuePairs()
        .FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
        .Value;

    // Get request body
    dynamic data = await req.Content.ReadAsAsync<object>();

    // Set name to query string or body data
    name = name ?? data?.name;

    return name == null
        ? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
        : req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
}

È anche possibile eseguire il binding a un oggetto .NET al posto di HttpRequestMessage. Questo oggetto viene creato dal corpo della richiesta e analizzato come JSON. Analogamente, un tipo può essere passato al binding di output della risposta HTTP e restituito come corpo della risposta, con un codice di stato 200.

using System.Net;
using System.Threading.Tasks;

public static string Run(CustomObject req, TraceWriter log)
{
    return "Hello " + req?.name;
}

public class CustomObject {
     public String name {get; set;}
}
}

Esempio di trigger HTTP in F#

open System.Net
open System.Net.Http
open FSharp.Interop.Dynamic

let Run(req: HttpRequestMessage) =
    async {
        let q =
            req.GetQueryNameValuePairs()
                |> Seq.tryFind (fun kv -> kv.Key = "name")
        match q with
        | Some kv ->
            return req.CreateResponse(HttpStatusCode.OK, "Hello " + kv.Value)
        | None ->
            let! data = Async.AwaitTask(req.Content.ReadAsAsync<obj>())
            try
                return req.CreateResponse(HttpStatusCode.OK, "Hello " + data?name)
            with e ->
                return req.CreateErrorResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
    } |> Async.StartAsTask

È necessario un file project.json che usa NuGet per fare riferimento agli assembly FSharp.Interop.Dynamic e Dynamitey, come illustrato di seguito:

{
  "frameworks": {
    "net46": {
      "dependencies": {
        "Dynamitey": "1.0.2",
        "FSharp.Interop.Dynamic": "3.0.0"
      }
    }
  }
}

Viene usato NuGet per recuperare le dipendenze e farvi riferimento nello script.

Esempio di trigger HTTP in Node.JS

module.exports = function(context, req) {
    context.log('Node.js HTTP trigger function processed a request. RequestUri=%s', req.originalUrl);

    if (req.query.name || (req.body && req.body.name)) {
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello " + (req.query.name || req.body.name)
        };
    }
    else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
    }
    context.done();
};

Esempi di webhook

Si supponga che il trigger webhook seguente sia presente nella matrice bindings di function.json:

{
    "webHookType": "github",
    "name": "req",
    "type": "httpTrigger",
    "direction": "in",
},

Vedere l'esempio specifico del linguaggio che registra i commenti del problema GitHub.

Esempio di webhook in C#

#r "Newtonsoft.Json"

using System;
using System.Net;
using System.Threading.Tasks;
using Newtonsoft.Json;

public static async Task<object> Run(HttpRequestMessage req, TraceWriter log)
{
    string jsonContent = await req.Content.ReadAsStringAsync();
    dynamic data = JsonConvert.DeserializeObject(jsonContent);

    log.Info($"WebHook was triggered! Comment: {data.comment.body}");

    return req.CreateResponse(HttpStatusCode.OK, new {
        body = $"New GitHub comment: {data.comment.body}"
    });
}

Esempio di webhook in F#

open System.Net
open System.Net.Http
open FSharp.Interop.Dynamic
open Newtonsoft.Json

type Response = {
    body: string
}

let Run(req: HttpRequestMessage, log: TraceWriter) =
    async {
        let! content = req.Content.ReadAsStringAsync() |> Async.AwaitTask
        let data = content |> JsonConvert.DeserializeObject
        log.Info(sprintf "GitHub WebHook triggered! %s" data?comment?body)
        return req.CreateResponse(
            HttpStatusCode.OK,
            { body = sprintf "New GitHub comment: %s" data?comment?body })
    } |> Async.StartAsTask

Esempio di webhook in Node.JS

module.exports = function (context, data) {
    context.log('GitHub WebHook triggered!', data.comment.body);
    context.res = { body: 'New GitHub comment: ' + data.comment.body };
    context.done();
};

Passaggi successivi

Per informazioni su altre associazioni e altri trigger per Funzioni di Azure, vedere Guida di riferimento per gli sviluppatori di trigger e associazioni di Funzioni di Azure.