Các dịch vụ web của Google Maps Platform là một tập hợp các giao diện HTTP cho các dịch vụ của Google, cung cấp dữ liệu địa lý cho các ứng dụng bản đồ của bạn.
Hướng dẫn này mô tả một số phương pháp phổ biến hữu ích cho việc thiết lập các yêu cầu dịch vụ web và xử lý các phản hồi của dịch vụ. Hãy tham khảo hướng dẫn dành cho nhà phát triển để xem tài liệu đầy đủ về Directions API (phiên bản cũ).
Dịch vụ web là gì?
Dịch vụ web của Nền tảng Google Maps là một giao diện để yêu cầu dữ liệu API Maps từ các dịch vụ bên ngoài và sử dụng dữ liệu đó trong các ứng dụng Maps của bạn. Các dịch vụ này được thiết kế để sử dụng cùng với bản đồ, theo Các hạn chế về giấy phép trong Điều khoản dịch vụ của Google Maps Platform.
Các dịch vụ web của Maps API sử dụng các yêu cầu HTTP(S) đối với các URL cụ thể, truyền các tham số URL và/hoặc dữ liệu POST ở định dạng JSON làm đối số cho các dịch vụ. Thông thường, các dịch vụ này trả về dữ liệu trong phần nội dung phản hồi dưới dạng JSON hoặc XML để ứng dụng của bạn phân tích cú pháp và/hoặc xử lý.
Một yêu cầu điển hình của Directions API (phiên bản cũ) thường có dạng như sau:
https://maps.googleapis.com/maps/api/directions/output?parameters
trong đó output cho biết định dạng phản hồi (thường là json hoặc xml).
Lưu ý: Tất cả ứng dụng Directions API (cũ) đều phải xác thực. Tìm hiểu thêm thông tin về thông tin xác thực.
Quyền truy cập SSL/TLS
Bạn phải sử dụng HTTPS cho tất cả các yêu cầu của Google Maps Platform sử dụng khoá API hoặc chứa dữ liệu người dùng. Các yêu cầu được thực hiện qua HTTP có chứa dữ liệu nhạy cảm có thể bị từ chối.
Tạo một URL hợp lệ
Bạn có thể cho rằng URL "hợp lệ" là điều hiển nhiên, nhưng không hẳn như vậy. Ví dụ: URL được nhập vào thanh địa chỉ trong trình duyệt có thể chứa các ký tự đặc biệt (ví dụ: "上海+中國"); trình duyệt cần dịch nội bộ các ký tự đó thành một phương thức mã hoá khác trước khi truyền.
Tương tự, mọi mã tạo hoặc chấp nhận dữ liệu đầu vào UTF-8 đều có thể coi URL có ký tự UTF-8 là "hợp lệ", nhưng cũng cần dịch các ký tự đó trước khi gửi đến một máy chủ web.
Quá trình này được gọi là
mã hoá URL hoặc mã hoá bằng dấu phần trăm.
Các ký tự đặc biệt
Chúng ta cần dịch các ký tự đặc biệt vì tất cả URL đều phải tuân thủ cú pháp do quy cách Giá trị nhận dạng tài nguyên đồng nhất (URI) chỉ định. Trên thực tế, điều này có nghĩa là URL chỉ được chứa một tập hợp con đặc biệt của các ký tự ASCII: các ký hiệu chữ và số quen thuộc và một số ký tự dành riêng để sử dụng làm ký tự điều khiển trong URL. Bảng này tóm tắt các ký tự này:
| Đặt | ký tự | Cách sử dụng URL |
|---|---|---|
| Chữ và số | 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 | Chuỗi văn bản, cách sử dụng lược đồ (http), cổng (8080), v.v. |
| Không dành riêng | - _ . ~ | Chuỗi văn bản |
| Đã đặt trước | ! * ' ( ) ; : @ & = + $ , / ? % # [ ] | Ký tự điều khiển và/hoặc Chuỗi văn bản |
Khi tạo một URL hợp lệ, bạn phải đảm bảo rằng URL đó chỉ chứa những ký tự xuất hiện trong bảng. Việc điều chỉnh một URL để sử dụng bộ ký tự này thường dẫn đến hai vấn đề, một là bỏ sót và một là thay thế:
- Các ký tự mà bạn muốn xử lý nằm ngoài bộ ký tự nêu trên. Ví dụ: các ký tự bằng ngôn ngữ nước ngoài (chẳng hạn như
上海+中國) cần được mã hoá bằng các ký tự ở trên. Theo quy ước phổ biến, khoảng trắng (không được phép xuất hiện trong URL) cũng thường được biểu thị bằng ký tự dấu cộng'+'. - Các ký tự tồn tại trong bộ ký tự nêu trên dưới dạng ký tự dành riêng, nhưng cần được sử dụng theo đúng nghĩa đen.
Ví dụ:
?được dùng trong URL để cho biết phần đầu của chuỗi truy vấn; nếu muốn dùng chuỗi "? and the Mysterions", bạn cần mã hoá ký tự'?'.
Tất cả các ký tự cần được mã hoá thành URL đều được mã hoá bằng ký tự '%' và giá trị thập lục phân gồm 2 ký tự tương ứng với ký tự UTF-8 của chúng. Ví dụ: 上海+中國 trong UTF-8 sẽ được mã hoá thành URL dưới dạng %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B. Chuỗi ? and the Mysterians sẽ được mã hoá URL dưới dạng %3F+and+the+Mysterians hoặc %3F%20and%20the%20Mysterians.
Các ký tự phổ biến cần mã hoá
Sau đây là một số ký tự phổ biến phải được mã hoá:
| Ký tự không an toàn | Giá trị được mã hoá |
|---|---|
| Không gian | %20 |
| " | %22 |
| < | %3C |
| > | %3E |
| # | %23 |
| % | %25 |
| | | %7C |
Đôi khi, việc chuyển đổi một URL mà bạn nhận được từ hoạt động đầu vào của người dùng sẽ rất khó khăn. Ví dụ: người dùng có thể nhập địa chỉ là "5th&Main St." Thông thường, bạn nên tạo URL từ các phần của URL đó, coi mọi hoạt động đầu vào của người dùng là ký tự nguyên nghĩa.
Ngoài ra, URL bị giới hạn ở 16384 ký tự đối với tất cả các dịch vụ web và API web tĩnh của Nền tảng Google Maps. Đối với hầu hết các dịch vụ, bạn hiếm khi đạt đến giới hạn ký tự này. Tuy nhiên, xin lưu ý rằng một số dịch vụ có nhiều tham số có thể dẫn đến URL dài.
Sử dụng API của Google một cách lịch sự
Các ứng dụng API được thiết kế kém có thể đặt nhiều tải hơn mức cần thiết trên cả Internet và máy chủ của Google. Phần này trình bày một số phương pháp hay nhất dành cho các ứng dụng sử dụng API. Việc làm theo các phương pháp hay nhất này có thể giúp bạn tránh được trường hợp ứng dụng của mình bị chặn do vô tình sử dụng sai mục đích các API.
Thuật toán thời gian đợi luỹ thừa
Trong một số ít trường hợp, có thể xảy ra lỗi khi xử lý yêu cầu của bạn; bạn có thể nhận được mã phản hồi HTTP 4XX hoặc 5XX, hoặc kết nối TCP có thể đơn giản là không thành công ở đâu đó giữa máy khách của bạn và máy chủ của Google. Thường thì bạn nên thử lại yêu cầu vì yêu cầu tiếp theo có thể thành công khi yêu cầu ban đầu không thành công. Tuy nhiên, bạn không nên chỉ lặp lại nhiều lần việc đưa ra yêu cầu đến các máy chủ của Google. Hành vi lặp lại này có thể làm quá tải mạng giữa ứng dụng của bạn và Google, gây ra vấn đề cho nhiều bên.
Cách tốt hơn là thử lại với độ trễ tăng dần giữa các lần thử. Thông thường, độ trễ sẽ tăng theo hệ số nhân với mỗi lần thử, đây là một phương pháp được gọi là Khoảng thời gian đợi tăng theo cấp luỹ thừa.
Ví dụ: hãy xem xét một ứng dụng muốn gửi yêu cầu này đến Time Zone API:
https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510×tamp=1331161200&key=YOUR_API_KEYVí dụ sau đây về Python cho thấy cách thực hiện yêu cầu bằng cơ chế lùi luỹ thừa:
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}")
Bạn cũng nên cẩn thận để không có mã thử lại nào cao hơn trong chuỗi lệnh gọi ứng dụng dẫn đến các yêu cầu lặp lại liên tiếp.
Yêu cầu được đồng bộ hoá
Số lượng lớn các yêu cầu được đồng bộ hoá đến các API của Google có thể trông giống như một cuộc tấn công Từ chối dịch vụ phân tán (DDoS) vào cơ sở hạ tầng của Google và sẽ được xử lý tương ứng. Để tránh trường hợp này, bạn phải đảm bảo rằng các yêu cầu API không được đồng bộ hoá giữa các ứng dụng.
Ví dụ: hãy xem xét một ứng dụng hiển thị thời gian theo múi giờ hiện tại. Ứng dụng này có thể sẽ đặt một báo thức trong hệ điều hành của máy khách để đánh thức hệ điều hành đó vào đầu phút, nhờ đó thời gian hiển thị có thể được cập nhật. Ứng dụng không được thực hiện bất kỳ lệnh gọi API nào trong quá trình xử lý liên quan đến chuông báo đó.
Việc thực hiện các lệnh gọi API để phản hồi một cảnh báo cố định là không nên vì điều này dẫn đến việc các lệnh gọi API được đồng bộ hoá với thời điểm bắt đầu của phút, ngay cả giữa các thiết bị khác nhau, thay vì được phân phối đồng đều theo thời gian. Một ứng dụng được thiết kế kém sẽ tạo ra mức tăng đột biến về lưu lượng truy cập gấp 60 lần mức bình thường vào đầu mỗi phút.
Thay vào đó, một thiết kế hay có thể là đặt chuông báo thứ hai vào một thời gian được chọn ngẫu nhiên. Khi chuông báo thứ hai này đổ chuông, ứng dụng sẽ gọi mọi API cần thiết và lưu trữ kết quả. Khi muốn cập nhật màn hình hiển thị vào đầu phút, ứng dụng sẽ sử dụng các kết quả đã lưu trữ trước đó thay vì gọi lại API. Với phương pháp này, các lệnh gọi API sẽ được phân bổ đều theo thời gian. Ngoài ra, các lệnh gọi API không làm chậm quá trình kết xuất khi màn hình đang được cập nhật.
Ngoài thời điểm bắt đầu của phút, những thời điểm đồng bộ hoá phổ biến khác mà bạn cần không nhắm đến là thời điểm bắt đầu của một giờ và thời điểm bắt đầu của mỗi ngày vào lúc nửa đêm.
Xử lý câu trả lời
Phần này thảo luận về cách trích xuất các giá trị này một cách linh động từ các phản hồi của dịch vụ web.
Các dịch vụ web của Google Maps cung cấp những phản hồi dễ hiểu nhưng không thực sự thân thiện với người dùng. Khi thực hiện một truy vấn, thay vì hiển thị một tập hợp dữ liệu, có lẽ bạn muốn trích xuất một số giá trị cụ thể. Nhìn chung, bạn sẽ muốn phân tích cú pháp các phản hồi từ dịch vụ web và chỉ trích xuất những giá trị mà bạn quan tâm.
Lược đồ phân tích cú pháp mà bạn sử dụng phụ thuộc vào việc bạn đang trả về đầu ra ở định dạng XML hay JSON. Các phản hồi JSON (đã ở dạng đối tượng Javascript) có thể được xử lý trong chính Javascript trên ứng dụng. Bạn nên xử lý các phản hồi XML bằng một trình xử lý XML và một ngôn ngữ truy vấn XML để giải quyết các phần tử trong định dạng XML. Chúng tôi sử dụng XPath trong các ví dụ sau đây vì XPath thường được hỗ trợ trong các thư viện xử lý XML.
Xử lý XML bằng XPath
XML là một định dạng thông tin có cấu trúc tương đối hoàn chỉnh, được dùng để trao đổi dữ liệu. Mặc dù không nhẹ bằng JSON, nhưng XML hỗ trợ nhiều ngôn ngữ hơn và có các công cụ mạnh mẽ hơn. Ví dụ: mã để xử lý XML trong Java được tích hợp vào các gói javax.xml.
Khi xử lý các phản hồi XML, bạn nên sử dụng một ngôn ngữ truy vấn thích hợp để chọn các nút trong tài liệu XML, thay vì giả định rằng các phần tử nằm ở vị trí tuyệt đối trong mã đánh dấu XML. XPath là cú pháp ngôn ngữ để mô tả duy nhất các nút và phần tử trong tài liệu XML. Biểu thức XPath cho phép bạn xác định nội dung cụ thể trong tài liệu phản hồi XML.
Biểu thức XPath
Việc có kiến thức cơ bản về XPath sẽ giúp bạn phát triển một lược đồ phân tích cú pháp mạnh mẽ. Phần này sẽ tập trung vào cách các phần tử trong tài liệu XML được xử lý bằng XPath, cho phép bạn xử lý nhiều phần tử và tạo các truy vấn phức tạp.
XPath sử dụng biểu thức để chọn các phần tử trong tài liệu XML, sử dụng cú pháp tương tự như cú pháp dùng cho đường dẫn thư mục. Các biểu thức này xác định các phần tử trong cây tài liệu XML, đây là một cây phân cấp tương tự như cây DOM. Nhìn chung, biểu thức XPath sẽ so khớp rất nhiều dữ liệu, tức là biểu thức này sẽ so khớp tất cả các nút khớp với tiêu chí được cung cấp.
Chúng ta sẽ sử dụng XML trừu tượng sau đây để minh hoạ các ví dụ:
<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>
Chọn nút trong biểu thức
Lựa chọn XPath sẽ chọn các nút. Nút gốc bao gồm toàn bộ tài liệu. Bạn chọn nút này bằng biểu thức đặc biệt "/". Xin lưu ý rằng nút gốc không phải là nút cấp cao nhất của tài liệu XML; trên thực tế, nút gốc nằm ở cấp độ cao hơn một cấp so với phần tử cấp cao nhất này và bao gồm cả phần tử đó.
Các nút phần tử đại diện cho nhiều phần tử trong cây tài liệu XML. Ví dụ: phần tử <WebServiceResponse> đại diện cho phần tử cấp cao nhất được trả về trong dịch vụ mẫu ở trên. Bạn chọn các nút riêng lẻ thông qua đường dẫn tuyệt đối hoặc tương đối, được biểu thị bằng sự hiện diện hoặc không hiện diện của ký tự "/" ở đầu.
- Đường dẫn tuyệt đối: biểu thức "
/WebServiceResponse/result" chọn tất cả các nút<result>là phần tử con của nút<WebServiceResponse>. (Xin lưu ý rằng cả hai phần tử này đều bắt nguồn từ nút gốc "/".) - Đường dẫn tương đối từ ngữ cảnh hiện tại: biểu thức "
result" sẽ khớp với mọi phần tử<result>trong ngữ cảnh hiện tại. Nhìn chung, bạn không cần lo lắng về ngữ cảnh, vì bạn thường xử lý kết quả dịch vụ web thông qua một biểu thức duy nhất.
Bạn có thể tăng cường một trong hai biểu thức này bằng cách thêm một đường dẫn ký tự đại diện, được biểu thị bằng dấu gạch chéo kép ("//"). Ký tự đại diện này cho biết rằng có thể có 0 hoặc nhiều phần tử khớp trong đường dẫn xen kẽ. Ví dụ: biểu thức XPath "//formatted_address" sẽ khớp với tất cả các nút có tên đó trong tài liệu hiện tại.
Biểu thức //viewport//lat sẽ khớp với tất cả các phần tử <lat> có thể theo dõi <viewport> làm phần tử mẹ.
Theo mặc định, biểu thức XPath sẽ so khớp tất cả các phần tử. Bạn có thể hạn chế biểu thức khớp với một phần tử nhất định bằng cách cung cấp một vị từ, được đặt trong dấu ngoặc vuông ([]). Ví dụ: biểu thức XPath "/GeocodeResponse/result[2] luôn trả về kết quả thứ hai.
| Loại biểu thức | |
|---|---|
| Root node | Biểu thức XPath: "
/"Lựa chọn:
<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>
|
| Đường dẫn tuyệt đối | Biểu thức XPath: "
/WebServiceResponse/result"Lựa chọn:
<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>
|
| Đường dẫn có ký tự đại diện | Biểu thức XPath: "
/WebServiceResponse//location"Lựa chọn:
<location>
<lat>37.4217550</lat>
<lng>-122.0846330</lng>
</location>
|
| Đường dẫn có vị từ | Biểu thức XPath: "
/WebServiceResponse/result[2]/message"Lựa chọn:
<message>The secret message</message>
|
Tất cả các phần tử con trực tiếp của result đầu tiên |
Biểu thức XPath: "
/WebServiceResponse/result[1]/*"Lựa chọn:
<type>sample</type>
<name>Sample XML</name>
<location>
<lat>37.4217550</lat>
<lng>-122.0846330</lng>
</location>
|
name của result có văn bản type là "sample". |
Biểu thức XPath: "
/WebServiceResponse/result[type/text()='sample']/name"Lựa chọn:
Sample XML
|
Điều quan trọng cần lưu ý là khi chọn các phần tử, bạn sẽ chọn các nút chứ không chỉ chọn văn bản trong các đối tượng đó. Thông thường, bạn sẽ muốn lặp lại tất cả các nút khớp và trích xuất văn bản. Bạn cũng có thể so khớp trực tiếp các nút văn bản; hãy xem phần Nút văn bản bên dưới.
Xin lưu ý rằng XPath cũng hỗ trợ các nút thuộc tính; tuy nhiên, tất cả các dịch vụ web của Google Maps đều phân phát các phần tử không có thuộc tính, nên không cần phải so khớp các thuộc tính.
Chọn văn bản trong biểu thức
Văn bản trong tài liệu XML được chỉ định trong biểu thức XPath thông qua một toán tử nút văn bản. Toán tử "text()" này cho biết việc trích xuất văn bản từ nút được chỉ định. Ví dụ: biểu thức XPath "//formatted_address/text()" sẽ trả về tất cả văn bản trong các phần tử <formatted_address>.
| Loại biểu thức | |
|---|---|
| Tất cả các nút văn bản (kể cả khoảng trắng) | Biểu thức XPath: "
//text()"Lựa chọn:
sample
Sample XML
37.4217550
-122.0846330
The secret message
|
| Chọn văn bản | Biểu thức XPath: "
/WebServiceRequest/result[2]/message/text()"Lựa chọn:
The secret message
|
| Lựa chọn theo bối cảnh | Biểu thức XPath: "
/WebServiceRequest/result[type/text() = 'sample']/name/text()"Lựa chọn:
Sample XML
|
Ngoài ra, bạn có thể đánh giá một biểu thức và trả về một tập hợp các nút, sau đó lặp lại "tập hợp nút" đó, trích xuất văn bản từ mỗi nút. Chúng ta sẽ sử dụng phương pháp này trong ví dụ bên dưới.
Để biết thêm thông tin về XPath, hãy tham khảo Quy cách XPath của W3C.
Đánh giá XPath trong Java
Java hỗ trợ rộng rãi việc phân tích cú pháp XML và sử dụng các biểu thức XPath trong gói javax.xml.xpath.*.
Vì lý do đó, mã mẫu trong phần này sử dụng Java để minh hoạ cách xử lý XML và phân tích cú pháp dữ liệu từ các phản hồi của dịch vụ XML.
Để sử dụng XPath trong mã Java, trước tiên, bạn cần tạo một thực thể của XPathFactory và gọi newXPath() trên factory đó để tạo một đối tượng XPath
. Sau đó, đối tượng này có thể xử lý các biểu thức XML và XPath đã truyền bằng phương thức evaluate().
Khi đánh giá các biểu thức XPath, hãy đảm bảo rằng bạn lặp lại mọi "tập hợp nút" có thể được trả về. Vì các kết quả này được trả về dưới dạng các nút DOM trong mã Java, nên bạn phải nắm bắt nhiều giá trị như vậy trong một đối tượng NodeList và lặp lại đối tượng đó để trích xuất mọi văn bản hoặc giá trị từ các nút đó.
Đoạn mã sau đây minh hoạ cách tạo một đối tượng XPath, chỉ định XML và biểu thức XPath cho đối tượng đó, đồng thời đánh giá biểu thức để in nội dung có liên quan.
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"); } } }