Prova la nuova generazione di funzionalità Directions con l'API Routes.

Best practice per l'utilizzo di servizi web dell'API Directions

I servizi web di Google Maps Platform sono una raccolta di interfacce HTTP che consentono di accedere ai servizi Google e forniscono dati geografici per le tue applicazioni Maps.

Questa guida descrive alcune pratiche comuni utili per configurare le richieste del servizio web e le risposte dei servizi. Consulta la guida per gli sviluppatori per la documentazione completa sull'API Directions.

Che cos'è un servizio web?

I servizi web di Google Maps Platform sono un'interfaccia per richiedere i dati dell'API di Google Maps dai servizi esterni e per utilizzare i dati nelle tue applicazioni Maps. Questi servizi sono progettati per essere utilizzati insieme a una mappa, in base alle limitazioni delle licenze nei Termini di servizio di Google Maps Platform.

I servizi web delle API di Maps utilizzano le richieste HTTP(S) a URL specifici, trasmettendo i parametri URL e/o i dati POST in formato JSON come argomenti per i servizi. In genere, questi servizi restituiscono i dati nel corpo della risposta in formato JSON o XML per l'analisi e/o l'elaborazione da parte dell'applicazione.

In genere, una richiesta API Directions è nel formato seguente:

https://maps.googleapis.com/maps/api/directions/output?parameters

dove output indica il formato di risposta (di solito json o xml).

Nota: tutte le applicazioni API Directions richiedono l'autenticazione. Scopri di più sulle credenziali di autenticazione.

Accesso SSL/TLS

HTTPS è obbligatorio per tutte le richieste Google Maps Platform che utilizzano chiavi API o contengono dati utente. Le richieste effettuate tramite HTTP che contengono dati sensibili potrebbero essere rifiutate.

Creare un URL valido

Potresti pensare che un URL "valido" sia evidente, ma non è così. Un URL inserito in una barra degli indirizzi di un browser, ad esempio, potrebbe contenere caratteri speciali (ad esempio "上海+中國"); il browser deve tradurre internamente tali caratteri in una codifica diversa prima della trasmissione. Per lo stesso token, qualsiasi codice che genera o accetta un input UTF-8 potrebbe trattare gli URL con caratteri UTF-8 come "validi", ma dovrebbe anche tradurre tali caratteri prima di inviarli a un server web. Questa procedura è chiamata codifica URL o codifica percentuale.

Caratteri speciali

Dobbiamo tradurre caratteri speciali perché tutti gli URL devono essere conformi alla sintassi specificata dalla specifica Uniform Resource Identifier (URI). Di conseguenza, gli URL devono contenere solo un sottoinsieme speciale di caratteri ASCII: i simboli alfanumerici familiari e alcuni caratteri riservati da utilizzare come caratteri di controllo negli URL. La tabella riassume questi caratteri:

Riepilogo dei caratteri URL validi
ImpostacaratteriUtilizzo URL
Alfanumerico a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 Stringhe di testo, utilizzo dello schema (http), porta (8080) e così via.
Non prenotato - _ . ~ Stringhe di testo
Riservata ! * ' ( ) ; : @ & = + $ , / ? % # [ ] Caratteri di controllo e/o stringhe di testo

Quando crei un URL valido, devi assicurarti che contenga solo i caratteri visualizzati nella tabella Riepilogo dei caratteri dell'URL valido. La conformità di un URL per l'utilizzo di questo set di caratteri generalmente determina due problemi: uno di omissione e uno di sostituzione:

  • I caratteri che vuoi gestire esistono al di fuori del set di cui sopra. Ad esempio, i caratteri in lingue straniere come 上海+中國 devono essere codificati utilizzando i caratteri sopra riportati. In genere, gli spazi (non consentiti all'interno degli URL) sono spesso rappresentati con il carattere '+'.
  • I caratteri esistono all'interno dell'insieme precedente come caratteri riservati, ma devono essere utilizzati letteralmente. Ad esempio, ? viene utilizzato all'interno degli URL per indicare l'inizio della stringa di query; se vuoi utilizzare la stringa "? e Mysterions", devi codificare il carattere '?'.

Tutti i caratteri da codificare per URL sono codificati con un carattere '%' e un valore esadecimale di due caratteri corrispondente al loro carattere UTF-8. Ad esempio, la codifica di 上海+中國 per UTF-8 sarà URL come %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B. La stringa ? and the Mysterians avrà una codifica URL come %3F+and+the+Mysterians o %3F%20and%20the%20Mysterians.

Caratteri comuni che richiedono la codifica

Alcuni caratteri comuni che devono essere codificati sono:

Carattere non sicuro Valore codificato
SPAZIO %20
" %22
< %3C
> %3E
# %23
% %25
| %7C

A volte, la conversione di un URL che ricevi dall'input dell'utente è complessa. Ad esempio, un utente può inserire un indirizzo come "Via Roma 1". In genere, l'URL dovrebbe essere composto da parti, trattando qualsiasi input utente come caratteri letterali.

Inoltre, gli URL sono limitati a 8192 caratteri per tutti i servizi web e le API web statiche di Google Maps Platform. Per la maggior parte dei servizi questo limite di caratteri verrà raramente raggiunto. Tuttavia, tieni presente che alcuni servizi hanno diversi parametri che possono comportare URL lunghi.

Uso educato delle API di Google

I client API con una progettazione scadente possono caricare più del necessario sia su Internet sia sui server di Google. Questa sezione contiene alcune best practice per i client delle API. Seguire queste best practice può aiutarti a evitare che la tua applicazione venga bloccata a causa di un uso involontario delle API.

Backoff esponenziale

In rari casi, potrebbe essersi verificato un problema durante la pubblicazione della richiesta; potresti ricevere un codice di risposta HTTP 4XX o 5XX oppure la connessione TCP potrebbe non riuscire correttamente tra il tuo client e il server di Google. Spesso vale la pena riprovare la richiesta perché quest'ultima potrebbe riuscire quando l'originale non è andato a buon fine. Tuttavia, è importante non limitarsi a effettuare loop ai server di Google. Questo comportamento di loop può sovraccaricare la rete tra il tuo client e Google, causando problemi per molte parti.

Un approccio migliore consiste nel riprovare con un aumento dei ritardi tra un tentativo e l'altro. Solitamente il ritardo è aumentato di un fattore moltiplicativo con ogni tentativo, un approccio noto come backoff esponenziale.

Ad esempio, prendi in considerazione un'applicazione che vuole effettuare questa richiesta all'API Time Zone:

https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510&timestamp=1331161200&key=YOUR_API_KEY

L'esempio Python seguente mostra come effettuare la richiesta con backoff esponenziale:

import json
import time
import urllib.error
import urllib.parse
import urllib.request

# The maps_key defined below isn't a valid Google Maps API key.
# You need to get your own API key.
# See https://developers.google.com/maps/documentation/timezone/get-api-key
API_KEY = "YOUR_KEY_HERE"
TIMEZONE_BASE_URL = "https://maps.googleapis.com/maps/api/timezone/json"


def timezone(lat, lng, timestamp):

    # Join the parts of the URL together into one string.
    params = urllib.parse.urlencode(
        {"location": f"{lat},{lng}", "timestamp": timestamp, "key": API_KEY,}
    )
    url = f"{TIMEZONE_BASE_URL}?{params}"

    current_delay = 0.1  # Set the initial retry delay to 100ms.
    max_delay = 5  # Set the maximum retry delay to 5 seconds.

    while True:
        try:
            # Get the API response.
            response = urllib.request.urlopen(url)
        except urllib.error.URLError:
            pass  # Fall through to the retry loop.
        else:
            # If we didn't get an IOError then parse the result.
            result = json.load(response)

            if result["status"] == "OK":
                return result["timeZoneId"]
            elif result["status"] != "UNKNOWN_ERROR":
                # Many API errors cannot be fixed by a retry, e.g. INVALID_REQUEST or
                # ZERO_RESULTS. There is no point retrying these requests.
                raise Exception(result["error_message"])

        if current_delay > max_delay:
            raise Exception("Too many retry attempts.")

        print("Waiting", current_delay, "seconds before retrying.")

        time.sleep(current_delay)
        current_delay *= 2  # Increase the delay each time we retry.


if __name__ == "__main__":
    tz = timezone(39.6034810, -119.6822510, 1331161200)
    print(f"Timezone: {tz}")

Devi inoltre fare attenzione che non ci sia un nuovo tentativo di codice nella catena delle chiamate dell'applicazione che porta a richieste ripetute in rapida successione.

Richieste sincronizzate

Un numero elevato di richieste sincronizzate alle API di Google può sembrare un attacco Distributed Denial of Service (DDoS) sull'infrastruttura di Google e viene trattato di conseguenza. Per evitare questo problema, devi assicurarti che le richieste API non siano sincronizzate tra i client.

Ad esempio, supponiamo che un'applicazione visualizzi l'ora nel fuso orario corrente. Questa applicazione imposterà probabilmente una sveglia nel sistema operativo del client all'inizio del minuto, in modo che l'ora visualizzata possa essere aggiornata. L'applicazione non deve effettuare chiamate API nell'ambito dell'elaborazione associata all'allarme.

Le chiamate API in risposta a una sveglia fissa non sono valide, perché le chiamate API vengono sincronizzate all'inizio del minuto, anche tra dispositivi diversi, anziché essere distribuite uniformemente nel tempo. Un'applicazione progettata in modo errato produrrà un picco di traffico a sessanta volte i livelli normali all'inizio di ogni minuto.

Un'altra possibile soluzione consiste nell'impostare una seconda sveglia a un orario scelto casualmente. Quando viene attivato questo secondo allarme, l'applicazione chiama le API necessarie e archivia i risultati. Quando l'applicazione vuole aggiornare la visualizzazione all'inizio del minuto, utilizza i risultati memorizzati in precedenza anziché richiamare l'API. Con questo approccio, le chiamate API sono distribuite in modo uniforme nel tempo. Inoltre, le chiamate API non ritardano il rendering quando il display viene aggiornato.

A parte l'inizio del minuto, altri orari di sincronizzazione comuni devono fare attenzione a non scegliere come target l'inizio di un'ora e l'inizio di ogni giornata a mezzanotte.

Elaborazione delle risposte

Questa sezione illustra come estrarre questi valori in modo dinamico dalle risposte dei servizi web.

I servizi web di Google Maps forniscono risposte facilmente comprensibili, ma non esatte. Quando esegui una query, invece di visualizzare un set di dati, puoi estrarre alcuni valori specifici. In genere, è consigliabile analizzare le risposte dal servizio web ed estrarre solo i valori che ti interessano.

Lo schema di analisi utilizzato varia a seconda che tu restituisca un output in formato XML o JSON. Le risposte JSON, che sono già sotto forma di oggetti Javascript, possono essere elaborate all'interno di JavaScript stesso sul client. Le risposte XML devono essere elaborate utilizzando un processore XML e un linguaggio di query XML per gestire gli elementi all'interno del formato XML. Utilizziamo XPath nei seguenti esempi, poiché è comunemente supportato nelle librerie di elaborazione XML.

Elaborazione di XML con XPath

XML è un formato di informazioni strutturate relativamente maturo utilizzato per l'interscambio di dati. Sebbene non sia leggero come JSON, il formato XML offre più supporto linguistico e strumenti più efficienti. Il codice per l'elaborazione di XML in Java, ad esempio, è integrato nei pacchetti javax.xml.

Quando elabori le risposte XML, dovresti usare un linguaggio di query appropriato per selezionare i nodi all'interno del documento XML, anziché presumere che gli elementi si trovino in posizioni assolute nel markup XML. XPath è una sintassi del linguaggio per descrivere in modo univoco nodi ed elementi all'interno di un documento XML. Le espressioni XPath consentono di identificare contenuti specifici all'interno del documento di risposta XML.

Espressioni XPath

Una certa familiarità con XPath contribuisce notevolmente allo sviluppo di uno schema di analisi efficace. Questa sezione si concentra sul modo in cui gli elementi all'interno di un documento XML vengono gestiti con XPath, permettendoti di gestire più elementi e creare query complesse.

XPath utilizza le espressioni per selezionare gli elementi all'interno di un documento XML, utilizzando una sintassi simile a quella utilizzata per i percorsi delle directory. Queste espressioni identificano gli elementi all'interno di un albero di documenti XML, che è un albero gerarchico simile a quello di un DOM. In genere, le espressioni XPath sono avide, il che indica che corrisponderanno a tutti i nodi che soddisfano i criteri forniti.

Utilizzeremo il seguente XML astratto per illustrare gli esempi:

<WebServiceResponse>
 <status>OK</status>
 <result>
  <type>sample</type>
  <name>Sample XML</name>
  <location>
   <lat>37.4217550</lat>
   <lng>-122.0846330</lng>
  </location>
 </result>
 <result>
  <message>The secret message</message>
 </result>
</WebServiceResponse>

Selezione dei nodi nelle espressioni

Le selezioni XPath selezionano nodi. Il nodo radice include l'intero documento. Selezioni questo nodo utilizzando l'espressione speciale "/". Tieni presente che il nodo principale non è il nodo di primo livello del tuo documento XML, ma che si trova a un livello sopra questo elemento di primo livello e lo include.

I nodi degli elementi rappresentano i vari elementi all'interno dell'albero dei documenti XML. Un elemento <WebServiceResponse>, ad esempio, rappresenta l'elemento di primo livello restituito nel servizio di esempio riportato sopra. Puoi selezionare singoli nodi tramite percorsi assoluti o relativi, indicati dalla presenza o dall'assenza di un carattere "/" iniziale.

  • Percorso assoluto: l'espressione "/WebServiceResponse/result" seleziona tutti i nodi <result> secondari del nodo <WebServiceResponse>. Tieni presente che entrambi gli elementi discendono dal nodo radice "/".
  • Percorso relativo dal contesto corrente: l'espressione "result" corrisponderà a tutti gli elementi <result> all'interno del contesto corrente. In genere, non devi preoccuparti del contesto, poiché di solito elabora i risultati del servizio web tramite una singola espressione.

Ognuna di queste espressioni può essere aumentata tramite l'aggiunta di un percorso di caratteri jolly, con una doppia barra ("//"). Questo carattere jolly indica che nessun o più elementi possono corrispondere nel percorso dell'intervallo. L'espressione XPath "//formatted_address", ad esempio, corrisponderà a tutti i nodi con quel nome nel documento corrente. L'espressione //viewport//lat corrisponderà a tutti gli elementi <lat> che possono tracciare <viewport> come elemento padre.

Per impostazione predefinita, le espressioni XPath corrispondono a tutti gli elementi. Puoi limitare l'espressione in modo che corrisponda a un determinato elemento fornendo un predicato, racchiuso tra parentesi quadre ([]). L'espressione XPath "/GeocodeResponse/result[2] restituisce sempre il secondo risultato, ad esempio.

Tipo di espressione
Nodo principale
Espressione XPath:  "/"
Selezione:
    <WebServiceResponse>
     <status>OK</status>
     <result>
      <type>sample</type>
      <name>Sample XML</name>
      <location>
       <lat>37.4217550</lat>
       <lng>-122.0846330</lng>
      </location>
     </result>
     <result>
      <message>The secret message</message>
     </result>
    </WebServiceResponse>
    
Percorso assoluto
Espressione XPath:  "/WebServiceResponse/result"
Selezione:
    <result>
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    </result>
    <result>
     <message>The secret message</message>
    </result>
    
Percorso con carattere jolly
Espressione XPath:  "/WebServiceResponse//location"
Selezione:
    <location>
     <lat>37.4217550</lat>
     <lng>-122.0846330</lng>
    </location>
    
Percorso con predicato
Espressione XPath:  "/WebServiceResponse/result[2]/message"
Selezione:
    <message>The secret message</message>
    
Tutti i figli diretti dei primi result
Espressione XPath:  "/WebServiceResponse/result[1]/*"
Selezione:
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    
Il name di un result il cui testo type è "sample".
Espressione XPath:  "/WebServiceResponse/result[type/text()='sample']/name"
Selezione:
    Sample XML
    

È importante notare che quando selezioni gli elementi, selezioni i nodi, non solo il testo all'interno di tali oggetti. In genere, è consigliabile eseguire l'iterazione su tutti i nodi corrispondenti ed estrarre il testo. Puoi anche trovare corrispondenze tra nodi di testo direttamente; consulta la sezione Nodi di testo di seguito.

Tieni presente che XPath supporta anche i nodi degli attributi. Tuttavia, tutti i servizi web di Google Maps pubblicano elementi senza attributi, pertanto la corrispondenza degli attributi non è necessaria.

Selezione del testo nelle espressioni

Il testo all'interno di un documento XML viene specificato nelle espressioni XPath tramite un operatore nodo di testo. Questo operatore "text()" indica l'estrazione di testo dal nodo indicato. Ad esempio, l'espressione XPath "//formatted_address/text()" restituirà tutto il testo all'interno degli elementi <formatted_address>.

Tipo di espressione
Tutti i nodi di testo (incluso lo spazio vuoto)
Espressione XPath:  "//text()"
Selezione:
    sample
    Sample XML

    37.4217550
    -122.0846330
    The secret message
    
Selezione del testo
Espressione XPath:  "/WebServiceRequest/result[2]/message/text()"
Selezione:
    The secret message
    
Selezione sensibile al contesto
Espressione XPath:  "/WebServiceRequest/result[type/text() = 'sample']/name/text()"
Selezione:
    Sample XML
    

In alternativa, puoi valutare un'espressione e restituire un insieme di nodi, quindi eseguire l'iterazione del "set di nodi", estraendo il testo da ogni nodo. Utilizziamo questo approccio nell'esempio riportato di seguito.

Per ulteriori informazioni su XPath, consulta la specifica XPath W3C.

Valutazione di XPath in Java

Java offre un ampio supporto per l'analisi del codice XML e per l'utilizzo delle espressioni XPath nel pacchetto javax.xml.xpath.*. Per questo motivo, il codice di esempio in questa sezione utilizza Java per illustrare come gestire XML e analizzare i dati dalle risposte di servizio XML.

Per utilizzare XPath nel tuo codice Java, devi prima creare un'istanza di un'istanza di XPathFactory e chiamare newXPath() su quella fabbrica per creare un oggetto XPath . Questo oggetto può quindi elaborare le espressioni XML e XPath passate utilizzando il metodo evaluate().

Quando valuti le espressioni XPath, assicurati di eseguire l'iterazione di tutti i possibili "set di nodi" che possono essere restituiti. Poiché questi risultati vengono restituiti come nodi DOM nel codice Java, è necessario acquisire tali valori multipli all'interno di un oggetto NodeList e ripetere l'operazione per estrarre il testo o i valori da questi nodi.

Il codice seguente illustra come creare un oggetto XPath, assegnargli un XML ed un'espressione XPath e valutare l'espressione per stampare i contenuti pertinenti.

import org.xml.sax.InputSource;
import org.w3c.dom.*;
import javax.xml.xpath.*;
import java.io.*;

public class SimpleParser {

  public static void main(String[] args) throws IOException {

	XPathFactory factory = XPathFactory.newInstance();

    XPath xpath = factory.newXPath();

    try {
      System.out.print("Web Service Parser 1.0\n");

      // In practice, you'd retrieve your XML via an HTTP request.
      // Here we simply access an existing file.
      File xmlFile = new File("XML_FILE");

      // The xpath evaluator requires the XML be in the format of an InputSource
	  InputSource inputXml = new InputSource(new FileInputStream(xmlFile));

      // Because the evaluator may return multiple entries, we specify that the expression
      // return a NODESET and place the result in a NodeList.
      NodeList nodes = (NodeList) xpath.evaluate("XPATH_EXPRESSION", inputXml, XPathConstants.NODESET);

      // We can then iterate over the NodeList and extract the content via getTextContent().
      // NOTE: this will only return text for element nodes at the returned context.
      for (int i = 0, n = nodes.getLength(); i < n; i++) {
        String nodeString = nodes.item(i).getTextContent();
        System.out.print(nodeString);
        System.out.print("\n");
      }
    } catch (XPathExpressionException ex) {
	  System.out.print("XPath Error");
    } catch (FileNotFoundException ex) {
      System.out.print("File Error");
    }
  }
}