Debugowanie klientów interfejsu Google Data API: sprawdzanie ruchu w programie

Jeffrey Scudder, zespół interfejsów API Google do danych
Czerwiec 2007 r.

Wprowadzenie

Czasami nie ma nic lepszego niż sprawdzenie, co jest przesyłane przez sieć. Jest to szczególnie ważne w przypadku pisania oprogramowania, które korzysta z usług internetowych, takich jak interfejsy Google Data API, gdzie wiele operacji wiąże się z wysyłaniem żądań HTTP. Jeśli wszystko inne zawiedzie, możesz sprawdzić, czy program działa zgodnie z oczekiwaniami, wyświetlając rzeczywiste przesłane i odebrane bajty. Wiele bibliotek klienta interfejsów API danych Google ma tryb debugowania, który wyświetla ruch HTTP. Jest to szczególnie przydatne, gdy nie masz dostępu do narzędzia do przechwytywania pakietów, takiego jak WireShark lub Fiddler.

Nie zliczę, ile razy byłem przekonany, że mój program działa prawidłowo, a potem po sprawdzeniu śladu pakietu okazywało się, że jest w nim dodatkowy znak nowego wiersza lub źle nazwany nagłówek HTTP. Programowanie pod kątem usługi internetowej bez sprawdzania ruchu HTTP może przypominać próbę nawleczenia igły z zamkniętymi oczami.

Może się jednak zdarzyć, że sniffer pakietów nie będzie dostępny lub nie będzie w stanie poradzić sobie z zaszyfrowanymi pakietami. Nie martw się – możesz obejść to ograniczenie, korzystając z mechanizmów rejestrowania w programie. Korzystając z tych funkcji rejestrowania, możesz zobaczyć niektóre lub wszystkie wymieniane dane, nawet w przypadku zaszyfrowanych danych HTTPS lub zdalnie uruchamianego kodu.

Na potrzeby tego artykułu napisałem przykładowy kod diagnostyczny w 3 językach, korzystając z bibliotek klienta interfejsu Google Data API dla Javy, .NETPython. W każdym przykładzie włączam rejestrowanie lub debugowanie, uwierzytelniam się za pomocą logowania klienta, a następnie pobieram listę arkuszy kalkulacyjnych Google i wyświetlam ich tytuły.

Java

Za pomocą klas java.util.logging możesz ustawić poziomy rejestrowania (a co za tym idzie, udostępniać dane o ruchu) dla kilku kluczowych obiektów w bibliotece klienta. W poniższym przykładzie wybrałem nagłówki HTTP i aktywność analizatora XML, aby uzyskać pełny obraz tego, co jest przesyłane przez sieć.

Biblioteka klienta Google Data Java ma osobne klasy do obsługi żądań HTTP i parsowania XML, dlatego muszę utworzyć 2 obiekty Logger, po jednym dla każdej klasy: com.google.gdata.client.http.HttpGDataRequest obsługuje ruch HTTP, a com.google.gdata.util.XmlParser odpowiada za parsowanie XML.

Instancje rejestratora będą rejestrować aktywność w przypadku HttpGDataRequestXmlParser, a Ty możesz kontrolować poziom szczegółowości danych wyjściowych każdego rejestratora. Na potrzeby tej demonstracji wybrałem wyświetlanie wszystkich zdarzeń wygenerowanych przez obiekty HttpGDataRequestXmlParser.

Po utworzeniu i skonfigurowaniu rejestratorów muszę określić, co mają robić, gdy otrzymają zdarzenie z klas. Na razie chcę zapisywać wszystkie informacje o logowaniu w konsoli, więc tworzę ConsoleHandler i dodaję go do obu rejestratorów.

Oto przykładowy kod:

import com.google.gdata.client.spreadsheet.*;
import com.google.gdata.data.spreadsheet.*;
import com.google.gdata.util.*;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.logging.*;

public class PrintSpreadsheetsWithLogging {
   
   
public static void main(String [] args) throws AuthenticationException,
                                                   
ServiceException, IOException {
       
// Configure the logging mechanisms.
       
Logger httpLogger = Logger.getLogger("com.google.gdata.client.http.HttpGDataRequest");
        httpLogger
.setLevel(Level.ALL);
       
Logger xmlLogger = Logger.getLogger("com.google.gdata.util.XmlParser");
        xmlLogger
.setLevel(Level.ALL);
       
// Create a log handler which prints all log events to the console.
       
ConsoleHandler logHandler = new ConsoleHandler();
        logHandler
.setLevel(Level.ALL);
        httpLogger
.addHandler(logHandler);
        xmlLogger
.addHandler (logHandler);
       
       
SpreadsheetService service = new SpreadsheetService("testing-loggingExampleApp-1");
        service
.setUserCredentials(email, password);
     
       
// Get a list of your spreadsheets.
        URL metafeedUrl
= new URL("http://spreadsheets.google.com/feeds/spreadsheets/private/full ");
       
SpreadsheetFeed feed = service.getFeed(metafeedUrl, SpreadsheetFeed.class);
     
       
// Print the title of each spreadsheet.
       
List spreadsheets = feed.getEntries();
       
for (int i = 0; i < spreadsheets.size(); i++) {
         
SpreadsheetEntry entry = (SpreadsheetEntry)spreadsheets.get(i);
         
System.out.println("\t" + entry.getTitle().getPlainText());
       
}
   
}
}

Po uruchomieniu tego programu w konsoli zobaczysz coś takiego (wyciąłem mniej interesujące fragmenty):

Jun 7, 2007 10:24:50 AM ...HttpGDataRequest setPrivateHeader
FINER: Authorization: <Not Logged>
Jun 7, 2007 10:24:50 AM ...HttpGDataRequest setHeader
FINER: User-Agent: ...
...
Jun 7, 2007 10:25:20 AM ...HttpGDataRequest execute
FINE: 200 OK
Jun 7, 2007 10:25:20 AM ...HttpGDataRequest execute
FINER: Date: Thu, 07 Jun 2007 17:25:24 GMT
Jun 7, 2007 10:25:20 AM ...HttpGDataRequest execute
FINER: null: HTTP/1.1 200 OK
Jun 7, 2007 10:25:20 AM ...HttpGDataRequest execute
FINER: Content-Type: application/atom+xml; charset=UTF-8
Jun 7, 2007 10:25:20 AM ...HttpGDataRequest execute
FINER: Last-Modified: Thu, 07 Jun 2007 17:25:22 GMT
...
Jun 7, 2007 10:25:20 AM ...XmlParser startElement
FINE: Start element id
Jun 7, 2007 10:25:20 AM ...XmlParser endElement
FINE: End element id
...
Jun 7, 2007 10:25:20 AM ...XmlParser startElement
FINE: Start element title
Jun 7, 2007 10:25:20 AM ...XmlParser startElement
FINER: Attribute type='text'
Jun 7, 2007 10:25:20 AM ...XmlParser endElement
FINE: End element title
...
Jun 7, 2007 10:25:20 AM ...XmlParser endElement
FINE: End element entry
...
Jun 7, 2007 10:25:20 AM ...XmlParser endElement
FINE: End element feed

Te logi mogą być dość duże, więc warto bardziej selektywnie ustawiać poziomy rejestratorów. Możesz też utworzyć FileHandler zamiast ConsoleHandler, aby przechowywać dane dzienników do późniejszego wykorzystania.

Jeśli nie interesuje Cię Java, możesz spróbować .NET.

.NET

Aby przechwytywać ruch HTTP w bibliotece klienta .NET, możesz zastąpić domyślną fabrykę żądań w kliencie obiektem GDataLoggingRequestFactory.

Żądania HTTP w bibliotece .NET są tworzone przez obiekt GDataRequestFactory, który znajduje się w każdym obiekcie usługi. Zwykłe fabryki żądań nie rejestrują żadnych danych, ale klasa GDataLoggingRequestFactory, która jest podklasą klasy GDataRequestFactory, ma wbudowaną funkcję rejestrowania. Możesz określić pełną ścieżkę pliku dziennika, ustawiając parametr CombinedFileName.

Po skonfigurowaniu fabryki żądań musisz zastąpić ją w obiekcie usługi, ustawiając RequestFactory obiektu usługi. Kod może wyglądać mniej więcej tak:

using System;
using Google.GData.Client;
using Google.GData.Extensions;
using Google.GData.Spreadsheets;

namespace LogginTest
{
   
class Program
   
{
       
static void Main(string[] args)
       
{
           
SpreadsheetsService service = new SpreadsheetsService("-exampleApp-1");
            service
.setUserCredentials(email, password);

           
Google.GData.Client.GDataLoggingRequestFactory factory = new GDataLoggingRequestFactory("wise", "SpreadsheetsLoggingTest");
            factory
.MethodOverride = true;
            factory
.CombinedLogFileName = "c:\\temp\\xmllog.log";
           
Console.WriteLine("Log file name:" + factory.CombinedLogFileName);
           
            service
.RequestFactory = factory;

           
SpreadsheetQuery query = new SpreadsheetQuery();
           
SpreadsheetFeed feed = service.Query(query);

           
Console.WriteLine("Your spreadsheets:");
           
foreach (SpreadsheetEntry entry in feed.Entries)
           
{
               
Console.WriteLine(entry.Title.Text);
           
}

           
Console.ReadKey();
       
}
   
}
}

Powstały plik dziennika zawiera żądania i odpowiedzi w formacie XML. Oto skrócony przykład sformatowany za pomocą narzędzia tidy.

<?xml version='1.0' encoding='utf-8'?>

<feed xmlns='http://www.w3.org/2005/Atom'
xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
  <id>
  http://spreadsheets.google.com/feeds/spreadsheets/private/full</id>
  <updated>2007-06-07T22:05: 02.674Z</updated>
  <link rel='self' type='application/atom+xml'
  href='http://spreadsheets.google.com/feeds/spreadsheets/private/full'>

  </link>
  ...
  <entry>
    <updated>2007-03-28T17:28:57.250Z</updated>
    <category scheme=' http://schemas.google.com/spreadsheets/2006'
    term='http://schemas.google.com/spreadsheets/2006#spreadsheet'>
    <title type='text'>events</title>

    <content type='text'>events</content>
    ...
  </entry>
  <entry>
    <updated>2007-05-25T22:11:08.200Z</updated>

    <category scheme=' http://schemas.google.com/spreadsheets/2006'
    term='http://schemas.google.com/spreadsheets/2006#spreadsheet'>
    </category>
    <title type='text'>UnitTest</title>
    <content type='text'>UnitTest</content>
    ...
  </entry>

  ...
</feed>

Może jednak bardziej interesują Cię języki skryptowe i wolisz używać Pythona.

Python

Aby przechwycić ruch HTTP w bibliotece klienta w Pythonie, możesz wyświetlić w konsoli ruch nagłówka HTTP, włączając tryb debugowania w kliencie HTTP. Obiekt usługi ma element debug, który możesz ustawić na True.

Ustawienie wartości debug na true spowoduje ustawienie flagi debugowania w bazowym obiekcie HTTPRequest, który znajduje się w obiekcie usługi.

Oto przykład, który będzie odzwierciedlać nagłówki HTTP wysyłane z serwera arkuszy kalkulacyjnych, gdy poprosisz o listę arkuszy.

#!/usr/bin/python

import gdata.spreadsheet.service

client
= gdata.spreadsheet.service.SpreadsheetsService()
client
.debug = True

client
.ClientLogin(email, password)

feed
= client.GetSpreadsheetsFeed()

for entry in feed.entry:
 
print entry.title.text

W konsoli zobaczysz coś takiego:

reply: 'HTTP/1.1 200 OK\r\n'
header: Content-Type: application/atom+xml; charset=UTF-8
header: Last-Modified: Thu, 07 Jun 2007 18:22:35 GMT
header: Cache-Control: max-age=0, must-revalidate, private
header: Transfer-Encoding: chunked
...
header: Date: Thu, 07 Jun 2007 18:22:35 GMT
header: Server: GFE/1.3

Podczas wykonywania dodatkowych operacji, takich jak wstawianie lub aktualizowanie, w konsoli będą wyświetlane odpowiednie dane żądania.

Podsumowanie

W tym krótkim samouczku pokazaliśmy, jak dodać podstawową funkcję rejestrowania do programu w języku Java, .NET lub Python, który korzysta z bibliotek klienta interfejsu Google Data API. Te techniki mogą być przydatne, jeśli musisz debugować wymiany HTTP, ale nie masz dostępu do narzędzia do przechwytywania pakietów. Te przykłady to tylko wierzchołek góry lodowej. Wiele mechanizmów rejestrowania dostępnych w tych językach jest znacznie bardziej zaawansowanych niż te, które tu przedstawiamy. Jeśli chcesz dowiedzieć się więcej o logowaniu lub interfejsach Google Data API, zapoznaj się z listą materiałów poniżej.

Biblioteki klienta omówione w tym artykule znajdziesz na tych stronach:

Powiązane artykuły w bazie wiedzy:

Grupy dyskusyjne: mamy ich sporo, a wraz z wdrażaniem kolejnych interfejsów Google Data API będzie ich przybywać. Aktywnie monitorujemy grupy.

Jeśli masz pytania lub sugestie, chętnie je poznam. Dołącz do grupy dyskusyjnej i zacznij publikować posty.