Best Practices für die Verwendung von Elevation API-Webdiensten

Die Google Maps Platform-Webdienste sind eine Sammlung von HTTP-Schnittstellen zu Google-Diensten, über die geografische Daten für Ihre Kartenanwendungen bereitgestellt werden.

In diesem Leitfaden werden einige gängige Vorgehensweisen beschrieben, die beim Einrichten von Webdienstanfragen und Verarbeiten von Dienstantworten hilfreich sind. Eine vollständige Dokumentation zur Elevation API finden Sie im Entwicklerhandbuch.

Was ist ein Webdienst?

Google Maps Platform-Webdienste sind eine Schnittstelle, über die Sie Maps API-Daten von externen Diensten anfordern und in Ihren Maps-Anwendungen verwenden können. Diese Dienste sind für die Verwendung in Verbindung mit einer Karte gemäß den Lizenzbeschränkungen in den Nutzungsbedingungen der Google Maps Platform konzipiert.

Die Maps APIs-Webdienste verwenden HTTP(S)-Anfragen an bestimmte URLs und übergeben URL-Parameter und/oder POST-Daten im JSON-Format als Argumente an die Dienste. Im Allgemeinen geben diese Dienste Daten im Antworttext entweder im JSON- oder im XML-Format zurück, um sie durch Ihre Anwendung zu parsen und/oder zu verarbeiten.

Eine typische Elevation API-Anfrage hat im Allgemeinen die folgende Form:

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

Dabei gibt output das Antwortformat an (normalerweise json oder xml).

Hinweis: Alle Elevation API-Anwendungen erfordern eine Authentifizierung. Weitere Informationen zu Authentifizierungsdaten

SSL/TLS-Zugriff

HTTPS ist für alle Google Maps Platform-Anfragen erforderlich, die API-Schlüssel verwenden oder Nutzerdaten enthalten. Über HTTP erfolgte Anfragen mit sensiblen Daten werden möglicherweise abgelehnt.

Gültige URL erstellen

Es mag den Anschein haben, dass „gültige“ URLs eine Selbstverständlichkeit sind. Das ist jedoch nicht der Fall. So kann beispielsweise eine URL, die in die Adresszeile eines Browsers eingegeben wird, Sonderzeichen wie "上海+中國" enthalten. Der Browser muss diese Zeichen vor der Übertragung intern in eine andere Codierung umwandeln. Ebenso ist es möglich, dass Code, der UTF-8-Eingaben erzeugt oder akzeptiert, URLs mit UTF-8-Zeichen als „gültig“ behandelt; diese Zeichen müssten jedoch vor dem Senden an einen Webbrowser ebenfalls umgewandelt werden. Dieser Vorgang wird als URL-Codierung oder Prozentcodierung bezeichnet.

Sonderzeichen

Sonderzeichen müssen umgewandelt werden, da alle URLs der Syntax entsprechen müssen, die in der Spezifikation Uniform Resource Identifier (URI) angegeben ist. Das bedeutet, dass URLs nur einen Teil der ASCII-Zeichen enthalten dürfen: die bekannten alphanumerischen Symbole und einige reservierte Zeichen, die in den URLs als Steuerzeichen dienen. Hier eine Übersicht:

Gültige URL-Zeichen
ZeichensatzcharactersVerwendung in der URL
Alphanumerisch A b c d e f g h i j k l m n o p q r s t u v b x y z Textstrings, Schemas (http), Portangaben (8080) usw.
Nicht reserviert - _ . ~ Textstrings
Reserviert ! * ' ( ) ; : @ & = + $ , / ? % # [ ] Steuerzeichen und/oder Textstrings

Beachten Sie bei der Generierung einer URL, dass diese nur die in der Tabelle aufgeführten Zeichen enthalten darf. Die Anpassung der URL an diesen Zeichensatz führt in der Regel zu zwei Problemen, nämlich dass Zeichen weggelassen oder ersetzt werden müssen:

  • Die Zeichen, die Sie verarbeiten möchten, sind nicht im obigen Zeichensatz enthalten. So müssen beispielsweise Zeichen ausländischer Sprachen, wie 上海+中國, mithilfe der oben angegebenen Zeichen codiert werden. Auch werden Leerzeichen, die innerhalb von URLs nicht zulässig sind, entsprechend den geltenden Konventionen oftmals durch das Zeichen '+' dargestellt.
  • Die Zeichen sind im obigen Zeichensatz als reservierte Zeichen enthalten, müssen aber im ursprünglichen Sinn des Zeichens verwendet werden. So wird beispielsweise ? in URLs für den Beginn eines Abfragestrings verwendet. Möchten Sie es stattdessen für den Text „? and the Mysterions“ verwenden, müssen Sie das Zeichen '?' codieren.

Alle Zeichen, die als URL codiert werden sollen, werden mithilfe des Zeichens '%' und eines Hexadezimalwerts aus zwei Zeichen codiert, der ihrem UTF-8-Zeichen entspricht. So würde zum Beispiel der UTF-8-String 上海+中國 durch die URL-Codierung in %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B umgewandelt. Und aus ? and the Mysterians würde %3F+and+the+Mysterians oder %3F%20and%20the%20Mysterians werden.

Häufig vorkommende Zeichen, die codiert werden müssen

Folgende häufig vorkommende Zeichen müssen codiert werden:

Unsicheres Zeichen Codierter Wert
Leerzeichen %20
" %22
< %3C
> %3E
# %23
% %25
| %7C

Die Konvertierung von URLs, die aus Nutzereingaben empfangen werden, kann manchmal Probleme mit sich bringen. Beispielsweise kann ein Nutzer eine Adresse als „5th&Main St.“ eingeben. Im Allgemeinen sollten Sie die URL aus ihren Teilen erstellen und jede Nutzereingabe wortwörtlich betrachten.

Außerdem sind URLs für alle Google Maps Platform-Webdienste und statischen Web APIs auf 16.384 Zeichen beschränkt. Bei den meisten Diensten wird diese Begrenzung selten erreicht. Beachten Sie jedoch, dass bestimmte Dienste einige Parameter haben, die zu langen URLs führen können.

Respektvolle Nutzung der Google APIs

Schlecht konzipierte API-Clients können sowohl das Internet als auch die Server von Google stärker als nötig belasten. Dieser Abschnitt enthält einige bewährte Methoden für Kunden der APIs. Mit diesen Best Practices können Sie verhindern, dass Ihre Anwendung aufgrund eines unbeabsichtigten Missbrauchs der APIs blockiert wird.

Exponentieller Backoff

In seltenen Fällen kann bei der Verarbeitung Ihrer Anfrage ein Fehler auftreten. Möglicherweise erhalten Sie einen HTTP-Antwortcode 4XX oder 5XX oder die TCP-Verbindung zwischen Ihrem Client und dem Google-Server schlägt fehl. Oft lohnt es sich, die Anfrage noch einmal zu senden, da die Folgeanfrage möglicherweise erfolgreich ist, wenn die ursprüngliche Anfrage fehlgeschlagen ist. Es ist jedoch wichtig, Anfragen nicht einfach in einer Schleife wiederholt an die Server von Google zu senden. Dieses Verhalten kann das Netzwerk zwischen Ihrem Client und Google überlasten und zu Problemen für viele Parteien führen.

Ein besserer Ansatz ist es, wiederholte Versuche in immer größeren Abständen durchzuführen. Normalerweise wird die Verzögerung bei jedem Versuch um einen multiplikativen Faktor erhöht. Dieser Ansatz wird als exponentieller Backoff bezeichnet.

Angenommen, eine Anwendung möchte diese Anfrage an die Time Zone API senden:

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

Im folgenden Beispiel mit Python wird gezeigt, wie die Anforderung mit exponentiellem Backoff durchgeführt wird:

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}")

Achten Sie außerdem darauf, dass es keinen Wiederholungscode weiter oben in der Anwendungsaufrufkette gibt, der zu wiederholten Anfragen in schneller Abfolge führt.

Synchronisierte Anforderungen

Eine große Anzahl synchronisierter Anfragen an die APIs von Google kann wie ein DDoS-Angriff (Distributed Denial of Service) auf die Infrastruktur von Google aussehen und entsprechend behandelt werden. Um dies zu vermeiden, sollten Sie darauf achten, dass API-Anfragen nicht zwischen Clients synchronisiert werden.

Angenommen, eine Anwendung zeigt die Zeit in der aktuellen Zeitzone an. Diese Anwendung wird wahrscheinlich einen Alarm im Client-Betriebssystem einstellen, durch den sie zu Beginn der Minute aktiviert wird, sodass die angezeigte Zeit aktualisiert werden kann. Die Anwendung sollte keine API-Aufrufe im Rahmen der Verarbeitung des Alarms ausführen.

API-Aufrufe als Reaktion auf einen festgelegten Alarm sind nicht gut, da sie dazu führen, dass die API-Aufrufe mit dem Anfang der Minute synchronisiert werden, auch zwischen verschiedenen Geräten, anstatt gleichmäßig über die Zeit verteilt zu werden. Eine schlecht konzipierte Anwendung, die dies tut, führt zu einer Traffic-Spitze mit einem 60-fachen Normalniveau zu Beginn jeder Minute.

Stattdessen wird bei einer guten Lösung ein zweiter Alarm für eine zufällig gewählte Zeit festgelegt. Wenn dieser zweite Alarm ausgelöst wird, ruft die Anwendung alle erforderlichen APIs auf und speichert die Ergebnisse. Wenn die Anzeige der Anwendung zu Beginn der Minute aktualisiert werden soll, verwendet sie zuvor gespeicherte Ergebnisse, anstatt die API noch einmal aufzurufen. Bei diesem Ansatz werden API-Aufrufe gleichmäßig über die Zeit verteilt. Außerdem verzögern die API-Aufrufe das Rendering nicht, wenn die Anzeige aktualisiert wird.

Abgesehen vom Beginn der Minute sollten andere häufige Synchronisierungszeiten, die Sie nicht als Ziel haben, der Beginn einer Stunde und der Beginn jedes Tages um Mitternacht sein.

Antworten verarbeiten

In diesem Abschnitt wird gezeigt, wie diese Werte dynamisch aus Webdienstanforderungen extrahiert werden können.

Die Google Maps-Webdienste bieten Antworten, die leicht verständlich, aber nicht wirklich nutzerfreundlich sind. Wenn Sie eine Abfrage ausführen, anstatt einen Datensatz anzuzeigen, möchten Sie wahrscheinlich einige spezifische Werte extrahieren. Im Allgemeinen sollten Sie Antworten aus dem Webdienst parsen und nur die Werte extrahieren, die Sie interessieren.

Welches Parsing-Schema Sie verwenden, hängt davon ab, ob Sie die Ausgabe im XML- oder JSON-Format zurückgeben. JSON-Antworten liegen bereits in Form von JavaScript-Objekten vor und können in JavaScript auf dem Client verarbeitet werden. XML-Antworten sollten mit einem XML-Prozessor und einer XML-Abfragesprache verarbeitet werden, um Elemente innerhalb des XML-Formats anzusprechen. In den folgenden Beispielen verwenden wir XPath, da dies häufig in XML-Verarbeitungsbibliotheken unterstützt wird.

XML mit XPath verarbeiten

XML ist ein relativ ausgereiftes Format für strukturierte Informationen und dient dem Datenaustausch. XML ist zwar nicht so einfach wie JSON, bietet aber mehr Sprachunterstützung und robustere Tools. Code zum Verarbeiten von XML in Java ist beispielsweise in die javax.xml-Pakete integriert.

Bei der Verarbeitung von XML-Antworten sollten Sie eine geeignete Abfragesprache zur Auswahl von Knoten im XML-Dokument verwenden und nicht davon ausgehen, dass sich die Elemente an absoluten Positionen innerhalb des XML-Markups befinden. XPath ist eine Sprachsyntax zum eindeutigen Beschreiben von Knoten und Elementen in einem XML-Dokument. Mit XPath-Ausdrücken können Sie bestimmte Inhalte im XML-Antwortdokument identifizieren.

XPath-Ausdrücke

Grundkenntnisse in XPath sind ein erster Schritt zur Entwicklung eines robusten Parsing-Schemas. In diesem Abschnitt geht es darum, wie Elemente in einem XML-Dokument mit XPath adressiert werden, sodass Sie mehrere Elemente ansprechen und komplexe Abfragen erstellen können.

XPath verwendet Ausdrücke, um Elemente in einem XML-Dokument auszuwählen. Dabei wird eine Syntax verwendet, die der für Verzeichnispfade ähnelt. Diese Ausdrücke identifizieren Elemente in einer XML-Dokumentstruktur, einer hierarchischen Baumstruktur ähnlich der eines DOM. Im Allgemeinen sind XPath-Ausdrücke „gierig“, was bedeutet, dass sie mit allen Knoten übereinstimmen, die den angegebenen Kriterien entsprechen.

Wir verwenden den folgenden abstrakten XML-Code, um unsere Beispiele zu veranschaulichen:

<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>

Auswahl von Knoten in Ausdrücken

Durch die XPath-Auswahl werden Knoten ausgewählt. Der Stammknoten umfasst das gesamte Dokument. Sie wählen diesen Knoten mit dem Spezialausdruck "/" aus. Beachten Sie, dass der Stammknoten nicht der oberste Knoten Ihres XML-Dokuments ist. Er befindet sich eine Ebene über diesem übergeordneten Element und enthält ihn.

Elementknoten stehen für die verschiedenen Elemente in der XML-Dokumentstruktur. Ein <WebServiceResponse>-Element steht beispielsweise für das Element der obersten Ebene, das im obigen Beispieldienst zurückgegeben wurde. Sie wählen einzelne Knoten entweder über absolute oder relative Pfade aus, was durch das Vorhandensein oder Fehlen eines vorangestellten „/“-Zeichens angegeben wird.

  • Absoluter Pfad: Mit dem Ausdruck „/WebServiceResponse/result“ werden alle <result>-Knoten ausgewählt, die dem <WebServiceResponse>-Knoten untergeordnet sind. Beachten Sie, dass beide Elemente vom Stammknoten "/" abstammen.
  • Relativer Pfad bezogen auf den aktuellen Kontext: Mit dem Ausdruck „result“ werden alle <result>-Elemente im aktuellen Kontext ausgewählt. Im Allgemeinen sollten Sie sich keine Gedanken über den Kontext machen müssen, da Webdienstergebnisse normalerweise über einen einzigen Ausdruck verarbeitet werden.

Jeder dieser Ausdrücke kann durch das Hinzufügen eines Platzhalterpfads erweitert werden, der durch einen doppelten Schrägstrich („//“) gekennzeichnet ist. Dieser Platzhalter gibt an, dass null oder mehr Elemente im dazwischenliegenden Pfad übereinstimmen können. Mit dem XPath-Ausdruck „//formatted_address“ werden beispielsweise alle Knoten im aktuellen Dokument abgeglichen, die diesen Namen haben. Mit dem Ausdruck //viewport//lat werden alle <lat>-Elemente abgeglichen, die <viewport> als übergeordnetes Element verfolgen können.

Standardmäßig finden XPath-Ausdrücke alle übereinstimmenden Elemente. Sie können den Ausdruck auf ein bestimmtes Element beschränken, indem Sie ein predicate angeben, das in eckige Klammern ([]) eingeschlossen ist. Der XPath-Ausdruck „/GeocodeResponse/result[2]“ gibt beispielsweise immer das zweite Ergebnis zurück.

Ausdruckstyp
Stammknoten
XPath-Ausdruck: "/"
Auswahl:
    <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>
    
Absoluter Pfad
XPath-Ausdruck: "/WebServiceResponse/result"
Auswahl:
    <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>
    
Pfad mit Platzhalter
XPath-Ausdruck: "/WebServiceResponse//location"
Auswahl:
    <location>
     <lat>37.4217550</lat>
     <lng>-122.0846330</lng>
    </location>
    
Pfad mit Prädikat
XPath-Ausdruck: "/WebServiceResponse/result[2]/message"
Auswahl:
    <message>The secret message</message>
    
Alle direkt untergeordneten Elemente der ersten result
XPath-Ausdruck: "/WebServiceResponse/result[1]/*"
Auswahl:
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    
Die name einer result, deren type-Text „sample“ lautet.
XPath-Ausdruck: "/WebServiceResponse/result[type/text()='sample']/name"
Auswahl:
    Sample XML
    

Wichtig: Wenn Sie Elemente auswählen, wählen Sie Knoten und nicht nur den Text innerhalb dieser Objekte aus. Im Allgemeinen sollten Sie über alle übereinstimmenden Knoten iterieren und den Text extrahieren. Sie können Textknoten auch direkt abgleichen. Siehe Textknoten unten.

XPath unterstützt auch Attributknoten. Da jedoch alle Google Maps-Webdienste Elemente ohne Attribute bereitstellen, ist ein Abgleich der Attribute nicht erforderlich.

Auswahl von Text in Ausdrücken

Text in einem XML-Dokument wird in XPath-Ausdrücken mithilfe eines Textknoten-Operators angegeben. Dieser Operator „text()“ gibt an, dass Text aus dem angegebenen Knoten extrahiert wird. Der XPath-Ausdruck „//formatted_address/text()“ gibt beispielsweise den gesamten Text in <formatted_address>-Elementen zurück.

Ausdruckstyp
Alle Textknoten (einschl. Leerzeichen)
XPath-Ausdruck: "//text()"
Auswahl:
    sample
    Sample XML

    37.4217550
    -122.0846330
    The secret message
    
Textauswahl
XPath-Ausdruck: "/WebServiceRequest/result[2]/message/text()"
Auswahl:
    The secret message
    
Kontextbezogene Auswahl
XPath-Ausdruck: "/WebServiceRequest/result[type/text() = 'sample']/name/text()"
Auswahl:
    Sample XML
    

Alternativ können Sie einen Ausdruck auswerten, eine Reihe von Knoten zurückgeben, diese "Knotengruppe" durchlaufen und den Text aus jedem Knoten extrahieren. Dieser Ansatz wird im folgenden Beispiel dargestellt:

Weitere Informationen zu XPath finden Sie in der XPath-W3C-Spezifikation.

XPath in Java auswerten

Java bietet im Paket javax.xml.xpath.* umfassende Unterstützung für das Parsen von XML und die Verwendung von XPath-Ausdrücken. Aus diesem Grund wird im Beispielcode in diesem Abschnitt Java verwendet, um zu veranschaulichen, wie XML verarbeitet und Daten aus XML-Dienstantworten geparst werden.

Wenn Sie XPath in Ihrem Java-Code verwenden möchten, müssen Sie zuerst eine Instanz einer XPathFactory instanziieren und in dieser Factory newXPath() aufrufen, um ein XPath -Objekt zu erstellen. Dieses Objekt kann dann übergebene XML- und XPath-Ausdrücke mit der Methode evaluate() verarbeiten.

Achten Sie beim Auswerten von XPath-Ausdrücken darauf, alle möglichen "Knotengruppen" zu durchlaufen, die zurückgegeben werden können. Da diese Ergebnisse im Java-Code als DOM-Knoten zurückgegeben werden, sollten Sie mehrere Werte in einem NodeList-Objekt erfassen und über dieses Objekt iterieren, um Text oder Werte aus diesen Knoten zu extrahieren.

Der folgende Code zeigt, wie Sie ein XPath-Objekt erstellen, ihm XML-Daten und einen XPath-Ausdruck zuweisen und den Ausdruck auswerten, um den relevanten Inhalt auszugeben.

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");
    }
  }
}