I servizi web di 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 di servizio web e elaborare le risposte dei servizi. Consulta la guida per gli sviluppatori per la documentazione completa sull'API Geocoding.
Che cos'è un servizio web?
I servizi web di Google Maps Platform sono un'interfaccia per richiedere i dati dell'API di 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, in conformità alle Limitazioni di licenza nei Termini di servizio di Google Maps Platform.
I servizi web delle API di Google Maps utilizzano richieste HTTP(S) a URL specifici, trasmettendo parametri URL e/o 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.
Una tipica richiesta all'API Geocoding ha in genere il seguente formato:
https://maps.googleapis.com/maps/api/geocode/output?parameters
dove output
indica il formato della risposta (di solito
json
o xml
).
Nota: tutte le applicazioni API Geocoding richiedono l'autenticazione. Leggi ulteriori informazioni sulle credenziali di autenticazione.
Accesso SSL/TLS
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.
Creazione di un URL valido
Potresti pensare che un URL "valido" sia già evidente, ma non è così. Un URL inserito nella barra degli indirizzi di un browser, ad esempio, potrebbe 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 server web.
Questo processo è chiamato
codifica degli URL o codifica a percentuale.
Caratteri speciali
Dobbiamo tradurre i caratteri speciali perché tutti gli URL devono essere conformi alla sintassi specificata dalla specifica URI (Uniform Resource Identifier). In pratica, 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:
Imposta | 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 9 7 | Stringhe di testo, utilizzo degli schemi (http ), porta (8080 ), ecc. |
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 URL validi. La conformità di un URL per l'utilizzo di questo insieme di caratteri generalmente causa due problemi, uno di omissione e uno di sostituzione:
- I caratteri che vuoi gestire esistono al di fuori del set precedente. Ad esempio, i caratteri in lingue straniere come
上海+中國
devono essere codificati utilizzando i caratteri precedenti. Secondo una convenzione comune, gli spazi (che non sono consentiti negli URL) vengono spesso rappresentati utilizzando anche il carattere più'+'
. - I caratteri esistono all'interno dei set precedenti 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 "? e i misteri", devi codificare il carattere'?'
.
Tutti i caratteri da codificare nell'URL vengono codificati utilizzando un carattere '%'
e un valore esadecimale di due caratteri corrispondenti al relativo carattere UTF-8. Ad esempio,
上海+中國
in UTF-8 verrebbe codificata come URL come
%E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B
. La
stringa ? and the Mysterians
deve essere codificata nell'URL come
%3F+and+the+Mysterians
o %3F%20and%20the%20Mysterians
.
Caratteri comuni che richiedono la codifica
Ecco alcuni caratteri comuni che devono essere codificati:
Carattere non sicuro | Valore codificato |
---|---|
Spazio | %20 |
" | %22 |
< | %3C |
> | %3E |
# | %23 |
% | %25 |
| | %7C |
La conversione di un URL che ricevi dall'input utente a volte è difficoltosa. Ad esempio, un utente può inserire un indirizzo come "Via Roma". In genere, devi creare l'URL partendo dalle sue parti, considerando gli input utente come caratteri letterali.
Inoltre, gli URL sono limitati a 16.384 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 viene raggiunto raramente. Tuttavia, tieni presente che alcuni servizi hanno diversi parametri che possono generare URL lunghi.
Uso educato delle API di Google
I client API progettati in modo errato possono applicare un carico maggiore del necessario sia su Internet sia sui server di Google. Questa sezione contiene alcune best practice per i client delle API. Seguendo queste best practice, puoi evitare che la tua applicazione venga bloccata per uso illecito involontario delle API.
Gestione di errori e nuovi tentativi
Per informazioni sui codici di risposta UNKNOWN_ERROR
o OVER_QUERY_LIMIT
dell'API
Geocoding, leggi la sezione sulla gestione degli errori e dei nuovi tentativi.
Backoff esponenziale
In rari casi, potrebbe verificarsi un problema durante la gestione della tua 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 poiché la richiesta di follow-up potrebbe avere esito positivo quando l'originale non è andato a buon fine. Tuttavia, è importante non limitarsi a eseguire ripetutamente il loop delle 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 un ritardo maggiore tra un tentativo e l'altro. Di solito il ritardo è aumentato di un fattore moltiplicativo a ogni tentativo, un approccio noto come backoff esponenziale.
Prendi in considerazione, ad esempio, un'applicazione che desidera 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_KEY
Il 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}")
Devi inoltre fare attenzione che non ci sia 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) sull'infrastruttura di Google ed essere gestito di conseguenza. Per evitare questo problema, devi assicurarti che le richieste API non siano sincronizzate tra i client.
Prendiamo ad esempio un'applicazione che mostra l'ora nel fuso orario corrente. Questa applicazione probabilmente imposterà un allarme nel sistema operativo client riattivandolo all'inizio del minuto, in modo che sia possibile aggiornare l'ora visualizzata. L'applicazione non deve effettuare chiamate API nell'ambito dell'elaborazione associata a questo allarme.
Effettuare chiamate API in risposta a un allarme fisso non è un buon segno, perché le chiamate API vengono sincronizzate all'inizio del minuto, anche tra dispositivi diversi, invece di essere distribuite in modo uniforme nel tempo. Un'applicazione progettata male all'inizio di ogni minuto produrrà un picco di traffico a livelli sessanta volte normali.
Una soluzione efficace potrebbe essere quella di impostare una seconda sveglia su un orario scelto in modo casuale. Quando questo secondo allarme si attiva, l'applicazione chiama le API di cui ha bisogno e archivia i risultati. Quando l'applicazione vuole aggiornare la visualizzazione all'inizio del minuto, utilizza i risultati archiviati in precedenza anziché chiamare di nuovo l'API. Con questo approccio, le chiamate API vengono 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 in cui devi non scegliere come target sono all'inizio dell'ora e all'inizio di ogni giorno a mezzanotte.
Elaborazione risposte
Questa sezione illustra come estrarre questi valori in modo dinamico dalle risposte del servizio web.
I servizi web di Google Maps forniscono risposte facili da capire, ma non esattamente facili da usare. Quando esegui una query, anziché visualizzare un insieme di dati, probabilmente vorrai estrarre alcuni valori specifici. In genere, vorrai analizzare le risposte dal servizio web ed estrarre solo i valori che ti interessano.
Lo schema di analisi utilizzato dipende dalla restituzione dell'output in XML o JSON. Le risposte JSON, essendo già sotto forma di oggetti JavaScript, possono essere elaborate all'interno dello stesso codice JavaScript sul client. Le risposte XML devono essere elaborate utilizzando un processore XML e un linguaggio di query XML per indirizzare gli elementi nel formato XML. Negli esempi riportati di seguito utilizziamo XPath, 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. Nonostante non sia leggero come JSON, XML offre un maggiore supporto linguistico e strumenti più solidi. Il codice per l'elaborazione di XML in Java, ad esempio, è incorporato nei pacchetti javax.xml
.
Durante l'elaborazione delle risposte XML, devi utilizzare un linguaggio di query appropriato per la selezione dei nodi all'interno del documento XML, invece di presumere che gli elementi risiedano in posizioni assolute all'interno del markup XML. XPath è una sintassi del linguaggio per la descrizione univoca di 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 un solido schema di analisi. Questa sezione si concentrerà sul modo in cui gli elementi all'interno di un documento XML vengono indirizzati con XPath, che consente di gestire più elementi e creare 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 una struttura ad albero di documenti XML, che è una struttura gerarchica simile a quella di un DOM. In genere, le espressioni XPath sono greedy, il che indica che corrisponderanno a tutti i nodi che corrispondono ai criteri forniti.
Per illustrare i nostri esempi, utilizzeremo il seguente XML astratto:
<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. Seleziona questo nodo utilizzando l'espressione speciale "/
". Tieni presente che il nodo radice non è il nodo di primo livello del documento XML, che in realtà si trova a un livello superiore a questo elemento di primo livello e lo include.
I nodi degli elementi rappresentano i vari elementi all'interno della struttura ad albero del documento XML. Un elemento <WebServiceResponse>
,
ad esempio, rappresenta l'elemento di primo livello restituito nel nostro
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>
che sono figli del nodo<WebServiceResponse>
. Tieni presente che entrambi gli elementi discendono dal nodo radice "/
". - Percorso relativo dal contesto attuale: l'espressione "
result
" corrisponde a qualsiasi elemento<result>
all'interno del contesto corrente. In genere, non devi preoccuparti del contesto, dato che solitamente elabori i risultati del servizio web tramite una singola espressione.
Ognuna di queste espressioni può essere aumentata tramite l'aggiunta di un percorso con caratteri jolly, indicato con una doppia barra ("//
"). Questo carattere jolly indica che nel percorso intervenendo non possono corrispondere zero o più elementi. 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>
in grado di 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 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> |
L'elemento 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 anche i nodi, non solo il testo al loro interno. In genere, è consigliabile eseguire l'iterazione su tutti i nodi corrispondenti ed estrarre il testo. Puoi anche trovare corrispondenze dirette dei nodi di testo; 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, quindi 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 di 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 (spazio vuoto incluso) | 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 su quel "insieme di nodi", estraendo il testo da ciascun nodo. Questo approccio viene utilizzato nell'esempio riportato di seguito.
Per ulteriori informazioni su XPath, consulta la specifica W3C di XPath.
Valutazione di XPath in Java
Java supporta ampiamente l'analisi dei file XML e l'utilizzo delle espressioni XPath all'interno del pacchetto javax.xml.xpath.*
.
Per questo motivo, nel codice campione in questa sezione viene utilizzato Java per
illustrare come gestire XML e analizzare i dati delle risposte del servizio XML.
Per utilizzare XPath nel tuo codice Java, devi prima creare l'istanza di un'istanza XPathFactory
e chiamare newXPath()
su quella 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 eseguire l'iterazione di ogni possibile "set di nodi" che potrebbe essere restituito. Poiché questi risultati vengono restituiti come nodi DOM nel codice Java, devi acquisire questi valori multipli all'interno di un oggetto NodeList
ed eseguire l'iterazione dell'oggetto per estrarre testo o valori da questi nodi.
Il codice seguente illustra come creare un oggetto XPath
, assegnargli un 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"); } } }