AWS Lambda: QueryString & API Gateway

Durante il mio percorso di studio sulle AWS Lambda mi ha lasciato parecchio perplesso il non riuscire a ricevere i parametri della QueryString in maniera del tutto trasparente come sono abituato a fare con le Azure Function.

Prima di raccontarvi la soluzione che ho adottato vorrei spiegarvi il mio caso d’uso. Se ritenete sia non corretta la soluzione e/o qualcosa non torna scrivetemi pure contattandomi.

API Gateway -> AWS Lambda -> Response

Ignorando tutto il mondo che vi è dietro ho messo semplicemente la lettura del parametro dalla collection. Come dite? Il tutto risulta vuoto? Ora vedremo come cambiare la situazione e riceverli nel codice.

Come parere personale -per quello che conta- la gestione su AWS di un qualcosa di semplice come una QueryString risulta veramente lunga e complessa da gestire. Lavorare con una Azure Function da questo punto di vista semplifica veramente tanto la vita.

Amazon API Gateway

Il punto d’ingresso della nostra AWS Lamdba esposta come HTTP è il nostro Amazon API Gateway. Se -essendo alle prime armi- non avete idea di come crearlo per questo caso di studio, vi rimando al post dal titolo AWS, Lambda, API Gateway & Authorization API Key scritto in precedenza.

Una volta entrati nel pannello relativo l’API Gateway dovrete selezionare il Methods e Method Execution di nostro interesse. Nel mio caso -come credo nel vostro- selezionerò GET come Method Execution.

Avremo davanti una una serie “blocchi” collegati da frecce e l’ordine in cui verranno attraversati è il seguente.

  • Client
  • Method Request
  • Integration Request
  • Lambda XYZ
  • Integration Response
  • Method Response
  • Client

Per il nostro caso di studio andremo a modificare solamente i seguenti

  • Method Request
  • Integration Request

Pronti?

Method Request

Il mio scopo e’ quello di fare arrivare una coppia di parametri dalla QueryString al backend per fornire dei filtri alla mia AWS Lambda. Questi due filtri si chiameranno details e detailsOpt. Il primo sarà obbligatorio, mentre il secondo no. Mi serve saperlo anche a questo punto? Si ed ora vi mostro il motivo.

Setting

  • Authorization: NONE
  • Request Validator: Convalida corpo, parametri di stringa query e intestazioni
  • API Key Required true

URL Query String Parameters

Aprendo questa area troverete una griglia con al suo interno tre colonne:

  • Name
  • Required
  • Caching

Aggiungendo una nuova riga i valori diventeranno i seguenti:

  • Name: details
  • Required: true
  • Caching: false

In questo modo proprio grazie alla voce Required: true sarete certi di ricevere il parametro nel backend. Nel momento in cui non viene passato riceverete un 400 Bad Request col seguente body di risposta

1
2
3
{
    "message": "Missing required request parameters: [details]"
}

Integration Request

Nella parte Integration Request dovrete impostare

  • Integration type: Lambda Function
  • Use Lambda Proxy integration: false

Ora scendendo in fondo alla pagina troverete Mapping Templates e come Request body Passthrough dovrete selezione (credo dalla prove che ho fatto) la voce When no template matches the request Content-Type header.

Come “Content-Type” non dovrete fare altro che inserire application/json ed il seguente template:

1
2
3
{
"queryStringParameters": {#foreach($key in $input.params().querystring.keySet())#if($foreach.index > 0),#end"$key":"$input.params().querystring.get($key)"#end}
}

Ammetto di averlo copiato da un post su StackOverflow. Non si tratta di farina del mio sacco. Non ci sarei mai arrivato senza il sacro testo di riferimento.

A questo punto -dopo avere salvato il tutto- dovrete solamente effettuare la distribuzione dell’Amazon API Gateway selezionando la fase che desiderate testare.

Localhost 5000

Per testare in locale il tutto evitando una serie di rilasci su AWS Cloud non dovrete fare altro che inserire il seguente JSON come Function Input:

1
2
3
4
5
6
{
  "queryStringParameters": 
  {
    "details": "true"
  }
}

FunctionHandler

Per concludere, come devo definire il FunctionHandler nel mio codice?

1
2
3
4
5
6
7
8
public async Task<HashSet<dto>> FunctionHandler
(
  APIGatewayProxyRequest input, 
  ILambdaContext context
)
{
  await Task.Delay(1);
}

e per verificare la presenza del parametro (ed ottenere il valore)

1
2
3
4
5
6
7
string detailsOptParam = string.Empty;
if (input != null && 
    input.QueryStringParameters != null && 
    input.QueryStringParameters.Count != 0)
{
    _ = input.QueryStringParameters.TryGetValue(QUERY_STRING_DETAILS_OPT, out detailsOptParam);
}

Per quanto riguarda invece details posso prelevarlo direttamente da QueryStringParameters in quanto il flag required messo in precedenza mi garantisce la sua presenza.