Sprawdzone metody korzystania z usług internetowych Directions API (starszej wersji)

Usługi internetowe Google Maps Platform to zbiór interfejsów HTTP do usług Google, które udostępniają dane geograficzne dla aplikacji map.

Ten przewodnik opisuje niektóre typowe praktyki przydatne podczas konfigurowania żądań usługi sieciowej i przetwarzania odpowiedzi usługi. Pełną dokumentację interfejsu Directions API (starszego) znajdziesz w przewodniku dla deweloperów.

Co to jest usługa internetowa?

Usługi sieciowe Google Maps Platform to interfejs do wysyłania żądań danych z interfejsu API Map Google do usług zewnętrznych i używania tych danych w aplikacjach Map. Zgodnie z ograniczeniami licencji w Warunkach korzystania z usługi Google Maps Platform te usługi są przeznaczone do używania w połączeniu z mapą.

Usługi internetowe interfejsów API Map Google używają żądań HTTP(S) do określonych adresów URL, przekazując parametry URL lub dane POST w formacie JSON jako argumenty do usług. Zazwyczaj te usługi zwracają dane w treści odpowiedzi w formacie JSON lub XML, aby aplikacja mogła je przeanalizować lub przetworzyć.

Typowe żądanie do interfejsu Directions API (starsza wersja) ma zwykle następującą postać:

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

gdzie output oznacza format odpowiedzi (zwykle json lub xml).

Uwaga: wszystkie aplikacje korzystające z interfejsu Directions API (starszego) wymagają uwierzytelniania. Dowiedz się więcej o danych uwierzytelniających.

Dostęp SSL/TLS

Protokół HTTPS jest wymagany w przypadku wszystkich żądań Google Maps Platform, które używają kluczy API lub zawierają dane użytkowników. Żądania wysyłane przez HTTP, które zawierają dane wrażliwe, mogą zostać odrzucone.

Tworzenie prawidłowego adresu URL

Może Ci się wydawać, że „prawidłowy” adres URL jest oczywisty, ale tak nie jest. Adres URL wpisany na przykład w pasku adresu w przeglądarce może zawierać znaki specjalne (np."上海+中國"). Przed przesłaniem przeglądarka musi wewnętrznie przetłumaczyć te znaki na inne kodowanie. Podobnie każdy kod, który generuje lub akceptuje dane wejściowe w formacie UTF-8, może traktować adresy URL ze znakami UTF-8 jako „prawidłowe”, ale przed wysłaniem ich na serwer WWW musi przetłumaczyć te znaki. Ten proces nazywa się kodowaniem URL lub kodowaniem procentowym.

Znaki specjalne

Musimy przetłumaczyć znaki specjalne, ponieważ wszystkie adresy URL muszą być zgodne ze składnią określoną w specyfikacji Uniform Resource Identifier (URI). Oznacza to, że adresy URL muszą zawierać tylko specjalny podzbiór znaków ASCII: znane symbole alfanumeryczne i niektóre znaki zarezerwowane do użycia jako znaki kontrolne w adresach URL. W tej tabeli znajdziesz podsumowanie tych znaków:

Podsumowanie prawidłowych znaków w adresie URL
UstawznakówUżycie adresu URL
Znaki alfanumeryczne 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 ciągi tekstowe, użycie schematu (http), port (8080) itp.
Niezarezerwowane - _ . ~ Ciągi tekstowe
Zarezerwowane ! * ' ( ) ; : @ & = + $ , / ? % # [ ] znaki sterujące lub ciągi tekstowe,

Podczas tworzenia prawidłowego adresu URL musisz zadbać o to, aby zawierał on tylko znaki podane w tabeli. Dostosowanie adresu URL do tego zestawu znaków zwykle powoduje 2 problemy: pominięcie i zamianę:

  • Znaki, które chcesz obsługiwać, nie należą do powyższego zestawu. Na przykład znaki w językach obcych, takie jak 上海+中國, muszą być zakodowane przy użyciu powyższych znaków. Zgodnie z powszechną konwencją spacje (które są niedozwolone w adresach URL) są często reprezentowane za pomocą znaku plusa '+'.
  • Znaki z powyższego zestawu są znakami zarezerwowanymi, ale muszą być używane dosłownie. Na przykład symbol ? jest używany w adresach URL do oznaczania początku ciągu zapytania. Jeśli chcesz użyć ciągu „? and the Mysterions”, musisz zakodować znak '?'.

Wszystkie znaki, które mają być zakodowane w formacie URL, są kodowane za pomocą znaku '%' i dwuznakowej wartości szesnastkowej odpowiadającej znakowi UTF-8. Na przykład znak 上海+中國 w kodowaniu UTF-8 będzie zakodowany w formacie adresu URL jako %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B. Ciąg znaków ? and the Mysterians zostanie zakodowany jako %3F+and+the+Mysterians lub %3F%20and%20the%20Mysterians.

Typowe znaki, które wymagają kodowania

Oto kilka często używanych znaków, które muszą być zakodowane:

Niebezpieczny znak Zakodowana wartość
Spacja %20
%22
< %3C
> %3E
# %23
% %25
| %7C

Konwertowanie adresu URL otrzymanego z danych wejściowych użytkownika bywa czasami trudne. Na przykład użytkownik może wpisać adres jako „5th&Main St.”. Zwykle adres URL należy tworzyć z jego części, traktując dane wejściowe użytkownika jako znaki dosłowne.

Dodatkowo adresy URL są ograniczone do 16384 znaków w przypadku wszystkich usług sieciowych Google Maps Platform i statycznych interfejsów API sieciowych. W przypadku większości usług ten limit znaków rzadko będzie osiągany. Pamiętaj jednak, że niektóre usługi mają kilka parametrów, które mogą powodować powstawanie długich adresów URL.

Uprzejme korzystanie z interfejsów API Google

Źle zaprojektowane klienty interfejsu API mogą niepotrzebnie obciążać zarówno internet, jak i serwery Google. Ta sekcja zawiera sprawdzone metody dla klientów interfejsów API. Stosowanie się do tych sprawdzonych metod może pomóc Ci uniknąć zablokowania aplikacji z powodu niezamierzonego nadużywania interfejsów API.

Exponential Backoff

W rzadkich przypadkach podczas obsługi Twojego żądania może wystąpić błąd. Możesz otrzymać kod odpowiedzi HTTP 4XX lub 5XX albo połączenie TCP może po prostu ulec awarii gdzieś między Twoim klientem a serwerem Google. Często warto ponowić próbę, ponieważ kolejna prośba może się powieść, mimo że pierwsza się nie powiodła. Nie należy jednak po prostu wielokrotnie wysyłać żądań do serwerów Google. Takie zapętlenie może przeciążyć sieć między klientem a Google, co spowoduje problemy dla wielu podmiotów.

Lepszym rozwiązaniem jest ponawianie próby z coraz większymi opóźnieniami między kolejnymi próbami. Zazwyczaj opóźnienie jest zwiększane o czynnik multiplikatywny przy każdej próbie. Takie podejście jest znane jako wzrastający czas do ponowienia.

Rozważmy na przykład aplikację, która chce wysłać do interfejsu Time Zone API to żądanie:

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

Poniższy przykład w Pythonie pokazuje, jak wysłać żądanie z wzrastającym czasem do ponowienia:

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

Uważaj też, aby w łańcuchu wywołań aplikacji nie było kodu ponawiania, który powoduje szybkie powtarzanie żądań.

Synchronizowane żądania

Duża liczba zsynchronizowanych żądań do interfejsów API Google może wyglądać jak atak typu DDoS na infrastrukturę Google i być odpowiednio traktowana. Aby tego uniknąć, zadbaj o to, aby żądania do interfejsu API nie były synchronizowane między klientami.

Rozważmy na przykład aplikację, która wyświetla czas w bieżącej strefie czasowej. Ta aplikacja prawdopodobnie ustawi alarm w systemie operacyjnym klienta, który będzie go budzić na początku minuty, aby można było zaktualizować wyświetlany czas. Aplikacja nie powinna wywoływać żadnych interfejsów API w ramach przetwarzania związanego z tym alarmem.

Wywoływanie interfejsu API w odpowiedzi na stały alarm jest niekorzystne, ponieważ powoduje synchronizację wywołań interfejsu API z początkiem minuty, nawet na różnych urządzeniach, zamiast równomiernego rozłożenia w czasie. Źle zaprojektowana aplikacja, która to robi, spowoduje na początku każdej minuty skok ruchu do 60-krotności normalnego poziomu.

Dobrym rozwiązaniem może być ustawienie drugiego alarmu na losowo wybraną godzinę. Gdy drugi alarm zostanie uruchomiony, aplikacja wywoła wszystkie potrzebne interfejsy API i zapisze wyniki. Gdy aplikacja chce zaktualizować wyświetlanie na początku minuty, używa wcześniej zapisanych wyników zamiast ponownie wywoływać interfejs API. Dzięki temu wywołania interfejsu API są rozłożone równomiernie w czasie. Ponadto wywołania interfejsu API nie opóźniają renderowania, gdy wyświetlacz jest aktualizowany.

Oprócz początku minuty należy uważać, aby nie kierować reklam na początek godziny i początek każdego dnia o północy.

Przetwarzanie odpowiedzi

W tej sekcji omawiamy, jak dynamicznie wyodrębniać te wartości z odpowiedzi usług internetowych.

Usługi internetowe Map Google zwracają odpowiedzi, które są łatwe do zrozumienia, ale nie do końca przyjazne dla użytkownika. Podczas wykonywania zapytania zamiast wyświetlać zestaw danych prawdopodobnie chcesz wyodrębnić kilka konkretnych wartości. Zazwyczaj warto przeanalizować odpowiedzi z usługi internetowej i wyodrębnić tylko te wartości, które Cię interesują.

Używany schemat analizowania zależy od tego, czy zwracasz dane wyjściowe w formacie XML czy JSON. Odpowiedzi JSON, które są już w formie obiektów JavaScript, mogą być przetwarzane w JavaScript na urządzeniu klienta. Odpowiedzi XML należy przetwarzać za pomocą procesora XML i języka zapytań XML, aby odwoływać się do elementów w formacie XML. W przykładach poniżej używamy XPath, ponieważ jest on powszechnie obsługiwany w bibliotekach do przetwarzania XML.

Przetwarzanie XML za pomocą XPath

XML to stosunkowo dojrzały format informacji strukturalnych używany do wymiany danych. Chociaż XML nie jest tak prosty jak JSON, obsługuje więcej języków i ma bardziej rozbudowane narzędzia. Kod do przetwarzania XML w Javie jest na przykład wbudowany w pakiety javax.xml.

Podczas przetwarzania odpowiedzi XML należy używać odpowiedniego języka zapytań do wybierania węzłów w dokumencie XML, zamiast zakładać, że elementy znajdują się w określonych pozycjach w znacznikach XML. XPath to składnia języka do jednoznacznego opisywania węzłów i elementów w dokumencie XML. Wyrażenia XPath umożliwiają identyfikowanie określonych treści w dokumencie odpowiedzi XML.

Wyrażenia XPath

Znajomość XPath znacznie ułatwia opracowanie solidnego schematu analizowania. W tej sekcji skupimy się na tym, jak za pomocą XPath odwoływać się do elementów w dokumencie XML, co pozwoli Ci odwoływać się do wielu elementów i tworzyć złożone zapytania.

XPath używa wyrażeń do wybierania elementów w dokumencie XML, stosując składnię podobną do składni ścieżek katalogów. Te wyrażenia identyfikują elementy w drzewie dokumentu XML, które jest hierarchicznym drzewem podobnym do drzewa DOM. Zazwyczaj wyrażenia XPath są zachłanne, co oznacza, że będą dopasowywać wszystkie węzły, które spełniają podane kryteria.

Aby zilustrować nasze przykłady, użyjemy tego abstrakcyjnego kodu XML:

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

Wybór węzłów w wyrażeniach

Wyrażenia XPath wybierają węzły. Węzeł główny obejmuje cały dokument. Ten węzeł wybierasz za pomocą specjalnego wyrażenia „/”. Pamiętaj, że węzeł główny nie jest węzłem najwyższego poziomu w dokumencie XML. W rzeczywistości znajduje się on o jeden poziom wyżej niż element najwyższego poziomu i go zawiera.

Węzły elementów reprezentują różne elementy w drzewie dokumentu XML. Element <WebServiceResponse> reprezentuje na przykład element najwyższego poziomu zwrócony w naszej przykładowej usłudze powyżej. Poszczególne węzły możesz wybierać za pomocą ścieżek bezwzględnych lub względnych, co jest sygnalizowane przez obecność lub brak znaku „/” na początku ścieżki.

  • Ścieżka bezwzględna: wyrażenie „/WebServiceResponse/result” wybiera wszystkie węzły <result>, które są elementami podrzędnymi węzła <WebServiceResponse>. (Zauważ, że oba te elementy pochodzą z węzła głównego „/”).
  • Ścieżka względna z bieżącego kontekstu: wyrażenie „result” będzie pasować do wszystkich elementów <result> w bieżącym kontekście. Zwykle nie musisz się martwić kontekstem, ponieważ wyniki usługi internetowej są zwykle przetwarzane za pomocą jednego wyrażenia.

Każde z tych wyrażeń można rozszerzyć, dodając ścieżkę z symbolem wieloznacznym, która jest oznaczona podwójnym ukośnikiem („//”). Ten symbol wieloznaczny oznacza, że w ścieżce pośredniej może wystąpić zero lub więcej elementów. Na przykład wyrażenie XPath „//formatted_address” dopasuje wszystkie węzły o tej nazwie w bieżącym dokumencie. Wyrażenie //viewport//lat będzie pasować do wszystkich elementów <lat>, które mają element nadrzędny <viewport>.

Domyślnie wyrażenia XPath pasują do wszystkich elementów. Możesz ograniczyć wyrażenie, aby pasowało do określonego elementu, podając predykat, który jest ujęty w nawiasy kwadratowe ([]). Na przykład wyrażenie XPath „/GeocodeResponse/result[2] zawsze zwraca drugi wynik.

Typ wyrażenia
Węzeł główny
Wyrażenie XPath:/
Wybór:
    <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>
    
Ścieżka bezwzględna
Wyrażenie XPath:/WebServiceResponse/result
Wybór:
    <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>
    
Ścieżka z symbolem wieloznacznym
Wyrażenie XPath:/WebServiceResponse//location
Wybór:
    <location>
     <lat>37.4217550</lat>
     <lng>-122.0846330</lng>
    </location>
    
Ścieżka z predykatem
Wyrażenie XPath:/WebServiceResponse/result[2]/message
Wybór:
    <message>The secret message</message>
    
Wszystkie bezpośrednie elementy podrzędne pierwszego tagu result
Wyrażenie XPath:/WebServiceResponse/result[1]/*
Wybór:
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    
name elementu result, którego tekst type to „sample”.
Wyrażenie XPath:/WebServiceResponse/result[type/text()='sample']/name
Wybór:
    Sample XML
    

Pamiętaj, że podczas wybierania elementów zaznaczasz węzły, a nie tylko tekst w tych obiektach. Zwykle należy iterować po wszystkich pasujących węzłach i wyodrębniać tekst. Możesz też bezpośrednio dopasowywać węzły tekstowe. Więcej informacji znajdziesz w sekcji Węzły tekstowe poniżej.

Pamiętaj, że XPath obsługuje też węzły atrybutów, ale wszystkie usługi internetowe Map Google udostępniają elementy bez atrybutów, więc dopasowywanie atrybutów nie jest konieczne.

Zaznaczanie tekstu w wyrażeniach

Tekst w dokumencie XML jest określany w wyrażeniach XPath za pomocą operatora węzła tekstowego. Operator „text()” wskazuje wyodrębnianie tekstu z wskazanego węzła. Na przykład wyrażenie XPath „//formatted_address/text()” zwróci cały tekst w elementach <formatted_address>.

Typ wyrażenia
Wszystkie węzły tekstowe (w tym białe znaki)
Wyrażenie XPath://text()
Wybór:
    sample
    Sample XML

    37.4217550
    -122.0846330
    The secret message
    
Zaznaczanie tekstu
Wyrażenie XPath:/WebServiceRequest/result[2]/message/text()
Wybór:
    The secret message
    
Wybór zależny od kontekstu
Wyrażenie XPath:/WebServiceRequest/result[type/text() = 'sample']/name/text()
Wybór:
    Sample XML
    

Możesz też obliczyć wartość wyrażenia i zwrócić zbiór węzłów, a następnie iterować po tym „zbiorze węzłów”, wyodrębniając tekst z każdego węzła. W przykładzie poniżej stosujemy to podejście.

Więcej informacji o XPath znajdziesz w specyfikacji XPath W3C.

Ocena XPath w języku Java

Java ma szerokie wsparcie dla analizowania XML i używania wyrażeń XPath w pakiecie javax.xml.xpath.*. Dlatego w przykładowym kodzie w tej sekcji używamy języka Java, aby pokazać, jak obsługiwać XML i parsować dane z odpowiedzi usług XML.

Aby użyć XPath w kodzie Java, musisz najpierw utworzyć instancję klasy XPathFactory i wywołać metodę newXPath() w tej fabryce, aby utworzyć obiekt XPath . Ten obiekt może następnie przetwarzać przekazane wyrażenia XML i XPath za pomocą metody evaluate().

Podczas sprawdzania wyrażeń XPath upewnij się, że iterujesz po wszystkich możliwych „zbiorach węzłów”, które mogą zostać zwrócone. Ponieważ te wyniki są zwracane jako węzły DOM w kodzie Java, należy przechwytywać takie wielokrotne wartości w obiekcie NodeList i iterować po tym obiekcie, aby wyodrębnić tekst lub wartości z tych węzłów.

Poniższy kod pokazuje, jak utworzyć obiekt XPath, przypisać do niego kod XML i wyrażenie XPath oraz ocenić to wyrażenie, aby wydrukować odpowiednią treść.

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