I servizi web Google Maps Platform sono una raccolta di interfacce HTTP per i servizi Google che forniscono dati geografici per le tue applicazioni di mappe.
Questa guida descrive alcune pratiche comuni utili per configurare le richieste del servizio web e l'elaborazione delle risposte del servizio. Per la documentazione completa dell'API Directions (legacy), consulta la guida per gli sviluppatori.
Che cos'è un servizio web?
I servizi web di Google Maps Platform sono un'interfaccia per richiedere dati delle API Maps da servizi esterni e utilizzare i dati all'interno delle applicazioni Maps. Questi servizi sono progettati per essere utilizzati in combinazione con una mappa, come indicato nelle Limitazioni della licenza dei Termini di servizio di Google Maps Platform.
I servizi web delle API Maps utilizzano richieste HTTP(S) a URL specifici, passando parametri URL e/o dati POST in formato JSON come argomenti ai servizi. In genere, questi servizi restituiscono i dati nel corpo della risposta come JSON o XML per l'analisi e/o l'elaborazione da parte dell'applicazione.
Una tipica richiesta dell'API Directions (legacy) ha in genere la seguente forma:
https://maps.googleapis.com/maps/api/directions/output?parameters
dove output indica il formato della risposta (di solito
json o xml).
Nota: tutte le applicazioni dell'API Directions (legacy) richiedono l'autenticazione. Scopri di più sulle credenziali di autenticazione.
Accesso SSL/TLS
Il protocollo HTTPS è obbligatorio per tutte le richieste di 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 ovvio, ma
non è proprio così. Un URL inserito nella barra degli indirizzi di un browser, ad esempio, può contenere caratteri speciali (ad es. "上海+中國"); il browser deve tradurre internamente questi caratteri in una codifica diversa prima della trasmissione.
Allo stesso modo, qualsiasi codice che genera o accetta input UTF-8
potrebbe trattare gli URL con caratteri UTF-8 come "validi", ma dovrebbe anche
tradurre questi caratteri prima di inviarli a un web server.
Questa procedura è chiamata
codifica URL o codifica percentuale.
Caratteri speciali
Dobbiamo tradurre i caratteri speciali perché tutti gli URL devono essere conformi alla sintassi specificata dalla specifica Uniform Resource Identifier (URI). Ciò significa che gli URL devono contenere solo un sottoinsieme speciale di caratteri ASCII: i familiari simboli alfanumerici e alcuni caratteri riservati da utilizzare come caratteri di controllo all'interno degli URL. Questa tabella riassume questi caratteri:
| Configurato | caratteri | Utilizzo degli 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 8 9 | Stringhe di testo, utilizzo dello schema (http), porta (8080) e così via. |
| Non prenotato | - _ . ~ | Stringhe di testo |
| Prenotato | ! * ' ( ) ; : @ & = + $ , / ? % # [ ] | Caratteri di controllo e/o stringhe di testo |
Quando crei un URL valido, devi assicurarti che contenga solo i caratteri mostrati nella tabella. L'adeguamento di un URL per utilizzare questo insieme di caratteri genera in genere due problemi, uno di omissione e uno di sostituzione:
- I caratteri che vuoi gestire non rientrano nel set
precedente. Ad esempio, i caratteri in lingue straniere
come
上海+中國devono essere codificati utilizzando i caratteri sopra indicati. Per convenzione, gli spazi (che non sono consentiti negli URL) vengono spesso rappresentati anche utilizzando il carattere più'+'. - I caratteri esistono all'interno del set precedente come caratteri riservati,
ma devono essere utilizzati letteralmente.
Ad esempio,
?viene utilizzato negli URL per indicare l'inizio della stringa di query; se vuoi utilizzare la stringa "? and the Mysterions", devi codificare il carattere'?'.
Tutti i caratteri da codificare come URL vengono codificati
utilizzando un carattere '%' e un valore esadecimale di due caratteri
corrispondente al carattere UTF-8. Ad esempio,
上海+中國 in UTF-8 sarebbe codificato nell'URL come
%E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B. La
stringa ? and the Mysterians verrà codificata come URL
%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 |
La conversione di un URL ricevuto dall'input dell'utente a volte è complicata. Ad esempio, un utente potrebbe inserire un indirizzo come "5th&Main St.". In genere, devi costruire l'URL a partire dalle sue parti, trattando l'input dell'utente come caratteri letterali.
Inoltre, gli URL sono limitati a 16.384 caratteri per tutti i servizi web di Google Maps Platform e le API web statiche. Per la maggior parte dei servizi, questo limite di caratteri viene raramente raggiunto. Tuttavia, tieni presente che alcuni servizi hanno diversi parametri che possono generare URL lunghi.
Utilizzo corretto delle API di Google
I client API progettati male possono esercitare un carico maggiore 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 per abuso involontario delle API.
Backoff esponenziale
In rari casi, potrebbe verificarsi un problema durante l'elaborazione della richiesta. Potresti ricevere un codice di risposta HTTP 4XX o 5XX oppure la connessione TCP potrebbe semplicemente non riuscire da qualche parte tra il client e il server di Google. Spesso vale la pena riprovare la richiesta, in quanto la richiesta di follow-up potrebbe riuscire quando quella originale non è riuscita. Tuttavia, è importante non eseguire ripetutamente richieste ai server di Google. Questo comportamento di loop può sovraccaricare la rete tra il client e Google, causando problemi a molte parti.
Un approccio migliore è riprovare con ritardi crescenti tra i tentativi. In genere il ritardo viene aumentato di un fattore moltiplicativo a ogni tentativo, un approccio noto come Exponential Backoff.
Ad esempio, considera un'applicazione che vuole inviare questa richiesta all'API Time Zone:
https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510×tamp=1331161200&key=YOUR_API_KEYIl seguente esempio Python 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}")
Inoltre, devi fare attenzione che non ci sia un codice di ripetizione più in alto nella catena di 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) all'infrastruttura di Google e viene trattato di conseguenza. Per evitare questo problema, assicurati che le richieste API non siano sincronizzate tra i client.
Ad esempio, considera un'applicazione che mostra l'ora nel fuso orario corrente. Questa applicazione probabilmente imposterà una sveglia nel sistema operativo client che lo riattiverà 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.
Effettuare chiamate API in risposta a un allarme fisso è sconsigliato, in quanto le chiamate API vengono sincronizzate con l'inizio del minuto, anche tra dispositivi diversi, anziché essere distribuite in modo uniforme nel tempo. Un'applicazione progettata male che esegue questa operazione produrrà un picco di traffico sessanta volte superiore ai livelli normali all'inizio di ogni minuto.
Un buon design potrebbe essere quello di impostare una seconda sveglia a un orario scelto in modo casuale. Quando scatta il secondo allarme, l'applicazione chiama le API necessarie e memorizza i risultati. Quando l'applicazione vuole aggiornare la visualizzazione all'inizio del minuto, utilizza i risultati memorizzati in precedenza anziché chiamare di nuovo l'API. Con questo approccio, le chiamate API vengono distribuite uniformemente nel tempo. Inoltre, le chiamate API non ritardano il rendering quando il display viene aggiornato.
Oltre all'inizio del minuto, altri orari di sincronizzazione comuni da non prendere di mira sono l'inizio di un'ora e l'inizio di ogni giorno a mezzanotte.
Elaborazione delle risposte
Questa sezione descrive come estrarre questi valori in modo dinamico dalle risposte del servizio web.
I servizi web di Google Maps forniscono risposte facili da comprendere, ma non esattamente intuitive. Quando esegui una query, anziché visualizzare un insieme di dati, probabilmente vuoi estrarre alcuni valori specifici. In genere, ti consigliamo di analizzare le risposte del servizio web ed estrarre solo i valori che ti interessano.
Lo schema di analisi che utilizzi dipende dal fatto che restituisci l'output in XML o JSON. Le risposte JSON, essendo 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 indirizzare gli elementi all'interno del formato XML. Utilizziamo XPath negli esempi seguenti, in quanto è comunemente supportato nelle librerie di elaborazione XML.
Elaborazione di XML con XPath
XML è un formato di informazioni strutturate relativamente maturo utilizzato per
lo scambio di dati. Sebbene non sia leggero come JSON, XML
offre un maggiore supporto linguistico e strumenti più robusti. Il codice per l'elaborazione di XML in Java, ad esempio, è integrato nei pacchetti javax.xml.
Quando elabori le risposte XML, devi utilizzare un linguaggio di query appropriato per selezionare i nodi all'interno del documento XML, anziché presupporre che gli elementi si trovino in posizioni assolute all'interno del markup XML. XPath è una sintassi del linguaggio per descrivere in modo univoco nodi ed elementi all'interno di un documento XML. Le espressioni XPath ti consentono di identificare contenuti specifici all'interno del documento di risposta XML.
Espressioni XPath
Una certa familiarità con XPath è molto utile per sviluppare uno schema di analisi efficace. Questa sezione si concentrerà su come gli elementi all'interno di un documento XML vengono indirizzati con XPath, consentendoti di indirizzare più elementi e costruire query complesse.
XPath utilizza 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 un documento XML, che è un albero gerarchico simile a quello di un DOM. In genere, le espressioni XPath sono avide, il che significa che corrisponderanno a tutti i nodi che soddisfano i criteri forniti.
Utilizzeremo il seguente XML astratto per illustrare i nostri 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 i nodi. Il nodo radice
comprende l'intero documento. Selezioni questo nodo utilizzando
l'espressione speciale "/". Tieni presente che il nodo radice
non è il nodo di primo livello del documento XML, ma
si trova un livello sopra questo elemento di primo livello e lo include.
I nodi degli elementi rappresentano i vari elementi all'interno della struttura del documento XML. Un elemento <WebServiceResponse>,
ad esempio, rappresenta l'elemento di primo livello restituito nel nostro
servizio di esempio sopra. Seleziona i singoli nodi tramite percorsi assoluti o relativi, indicati dalla presenza o dall'assenza del carattere iniziale "/".
- Percorso assoluto: l'espressione "
/WebServiceResponse/result" seleziona tutti i nodi<result>che sono elementi secondari del nodo<WebServiceResponse>. Tieni presente che entrambi questi elementi derivano dal nodo radice "/". - Percorso relativo dal contesto corrente: l'espressione
"
result" corrisponde a qualsiasi elemento<result>all'interno del contesto corrente. In genere, non devi preoccuparti del contesto, in quanto di solito elabori i risultati del servizio web tramite una singola espressione.
Una di queste espressioni può essere aumentata tramite l'aggiunta
di un percorso jolly, indicato con una doppia barra ("//").
Questo carattere jolly indica che zero o più elementi possono corrispondere nel
percorso intermedio. L'espressione XPath "//formatted_address",
ad esempio, corrisponderà a tutti i nodi con quel nome nel documento corrente.
L'espressione //viewport//lat corrisponde a tutti gli elementi <lat> che possono tracciare <viewport> come elemento principale.
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,
che è 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 gli elementi secondari diretti del primo 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 questi oggetti. In genere, devi iterare su tutti i nodi corrispondenti ed estrarre il testo. Puoi anche abbinare direttamente i nodi di testo. Vedi Nodi di testo di seguito.
Tieni presente che XPath supporta anche i nodi degli attributi; tuttavia, tutti i servizi web di Google Maps forniscono 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 del 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 (inclusi gli spazi) | Espressione XPath: "
//text()"Selezione:
sample
Sample XML
37.4217550
-122.0846330
The secret message
|
| Selezione di 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 iterare su questo "insieme di nodi" ed estrarre il testo da ogni nodo. Utilizziamo questo approccio nell'esempio riportato di seguito.
Per saperne di più su XPath, consulta la specifica W3C di XPath.
Valutazione di XPath in Java
Java supporta ampiamente l'analisi XML e l'utilizzo di espressioni XPath
all'interno del pacchetto javax.xml.xpath.*.
Per questo motivo, il codice campione in questa sezione utilizza Java per
illustrare come gestire XML e analizzare i dati dalle risposte del servizio XML.
Per utilizzare XPath nel codice Java, devi prima creare un'istanza di XPathFactory e chiamare newXPath() su questa fabbrica per creare un oggetto XPath
. Questo oggetto può quindi elaborare le espressioni XML
e XPath trasmesse utilizzando il metodo evaluate().
Quando valuti le espressioni XPath, assicurati di iterare
su tutti i possibili "set di nodi" che potrebbero essere restituiti. Poiché questi
risultati vengono restituiti come nodi DOM nel codice Java, devi acquisire
più valori all'interno di un oggetto NodeList e
iterare su questo oggetto per estrarre testo o valori da questi
nodi.
Il seguente codice illustra come creare un oggetto XPath, assegnargli XML e 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"); } } }