Specyfikacja API serwisu Blip.plOstatnia modyfikacja: 30 września 2009, 10:56
Interfejs programistyczny (API) serwisu Blip.pl umożliwia tworzenie aplikacji wykorzystujących zasoby i logikę serwisu osobom trzecim. Dokładamy wszelkich starań, by tworzenie różnego rodzaju programów czy widgetów wokół Blip.pl, było jak najłatwiejsze, dlatego chętnie wysłuchamy Waszych uwag i propozycji usprawnień, które można kierować pod adres blip@blip.pl.
Dla developerów używających API stworzona została grupa dyskusyjna blip-devel, służąca wymianie doświaczeń przy pisaniu aplikacji współpracujących z Blip.pl. Jest ona dostępna także jako lista mailingowa. Gotowe aplikacje i biblioteki należy dopisywać na odpowiedniej stronie blipowego wiki, podając dane kontaktowe autora. Wiki można wykorzystać także do publikowania dokumentacji gotowych aplikacji.
Administratorzy serwisu Blip.pl rezerwują sobie prawo do wyłączenia części lub całości API, blokowania użytkowników bez podawania przyczyn i dowolnej modyfikacji zasad dostępu oraz wprowadzania ograniczeń, przede wszystkim z powodu nadużyć różnej formy.
Udostępniliśmy loga serwisu Blip.pl do wykorzystania w aplikacjach, rezerwując sobie jednocześnie prawo do odwołania domniemanej zgody na użycie loga, w przypadku gdy zostanie ono zastosowane w celach sprzecznych z interesami właścieciela serwisu Blip.pl lub łamiącymi obowiązujące prawo. Dostępne są także loga w innych formatach i innych wielkościach, zainteresowanych prosimy o kontakt mailowy.
Osoby zainteresowane szybkim stworzeniem aplikacji współpracującej z Blip.pl powinny sprawdzić, czy dla wybranego języka programowania/frameworku nie jest już dostępna gotowa biblioteka. Należy też upewnić się, czy obsługuje ona aktualną wersję API.
Architektura intefejsu programistycznego Blip.pl wykonana jest
zgodnie ze wskazaniami stylu REST, w związku z czym cała
komunikacja korzysta z protokołu HTTP, a możliwe rodzaje
interakcji reprezentowane są poprzez zasoby oraz
przyporządkowane im operacje. Aby korzystać z opisywanego w
niniejszym dokumencie API, należy obligatoryjnie ustawiać
nagłówek X-Blip-API na wartość 0.02, oraz
nagłówek Accept na typ MIME pożądanego formatu
odpowiedzi (obecnie tylko i wyłącznie application/json).
Identyfikacja klienta odbywa się poprzez nagłówek User-Agent:
(lub alternatywnie X-Blip-Application:),
który wykorzystujemy do celów statystycznych. Choć jego
ustawienie nie jest wymagane, to leży ono w interesie samego twórcy
aplikacji, pomagając w jej promocji. W nagłówku warto także umieścić
informacje pozwalające na skontaktowanie się z twórcą aplikacji.
Korzystanie z części dostępnych zasobów i operacji wymaga
wcześniejszej uwierzytelnienia. Obecnie odbywa się to zgodnie ze
schematem HTTP Basic, w którym login zostaje połączony z hasłem
poprzez znak dwukropka, a następnie wynikowy napis zostaje
przekształcony do formatu Base64 i przesłany w nagłówku
Authorization: z prefiksem Basic , oznaczającym rodzaj
uwierzytelnienia.
Dla przykładu prawidłowy nagłówek uwierzytelniający, dla
użytkownika "stefan" z hasłem "plecy.8" wygląda w następujący
sposób:
Authorization: Basic c3RlZmFuOnBsZWN5Ljg=
W przypadku zapytań PUT i POST dane przyjmowane są w postaci
parametrów zakodowanych w formie danych formularza lub w postaci
JSONa. Dane zwracane są zawsze w formacie JSON,
zakodowanym w UTF-8. Mimo to niezbędne jest ustawienie nagłówka
Accept: na application/json, w przeciwnym wypadku możemy
bowiem otrzymać po prostu HTML jednej ze stron serwisu.
Powiązania pomiędzy udostępnianymi przez API zasobami określone
są poprzez klucze z końcówką "_path" w ich reprezentacjach,
których wartościami są adresy URL umożliwiające pobranie
reprezentacji JSON danego zasobu powiązanego. I tak np.
reprezentacja zasobu user posiada klucz
background_path, którego wartością jest adres z
którego można pobrać JSON reprezentujący tło danego użytkownika.
Niekiedy powiązania pomiędzy zasobami są na tyle silne, że
prawie zawsze klient potrzebuje ich razem. W takim przypadku API
zagnieżdża reprezentację jednego zasobu w reprezentacji drugiego
- np. JSON danego statusu zawiera klucz user, pod
którym znajduje się JSON reprezentujący autora statusu.
W kilku przypadkach bywa tak, że różne operacje dostępne są pod tym samym adresem url, a rodzaj żądania jest rozpoznawany na postawie nagłówka HTTP. Zgodnie z konwencją przyjetą dla serwisów RESTowych, żądanie typu POST odpowiada tworzeniu nowego obiektu, PUT jego uaktualnieniu, DELETE usunięciu, a GET pobraniu reprezentacji w odpowiednim formacie.
W przypadku jakichkolwiek problemów z dokonywaniem zapytań
danego rodzaju, możliwe jest wykonanie zapytania POST z polem
_method ustawionym na nazwę odpowiedniego żadania
HTTP - jest to rozwiązanie niezbędne m. in. do komunikacji z
Flashem, który nie potrafi poprawnie ustawiać nagłówków HTTP dla
zapytań innych niż POST.
Należy także pamiętać, że część akcji zwracających listy
obiektów posiada parametr limit, o maksymalnej i
jednocześnie domyślnej wartości 50, oznaczającej maksymalną
ilość zasóbów do zwrócenia. Parametr offset
pozwala cofnąć się w czasie o x statusów, co umożliwia np.
implementację stronicowania w aplikacji klienckiej.
W przypadku zastosowań w których liczba requestów ma
znaczenia, dostępny jest parametr include,
pozwalający na umieszczenie reprezentacji zasobów powiązanych
bezpośrednio w reprezentacji "głównego" zasobu, zamiast klucza
z końcówką "_path" i wartością będąca urlem danego zasobu.
Zachowuje to przejrzystość domyślnej reprezentacji,
jednocześnie pozwalając twórcom aplikacji na dostosowanie
szczegółowości zwracanych danych do własnych potrzeb. I tak,
zakładając że pobieramy update, bez parametru "include" nie
otrzymamy bezpośrednio danych nagrań i obrazków z nim
powiązanych, lecz "recording_path" i "pictures_path", czyli
urle pod którymi są one dostępne. Jeśli zaś ustawimy "include"
na "recording,pictures", wszelkie dane dostępne pod tymi
urlami zostaną umieszczone bezpośrednio w treści odpowiedzi -
jako wartości powiązane z kluczami "recording" i "pictures".
Można rozwijać zasoby głębiej niż tylko na pierwszym poziomie
- np. aby otrzymać zarówno obiekt JSON odbiorcy wiadomości,
jak i jego avatara, możemy ustawić include na
"recipient,recipient[avatar]".
W przypadku korzystania z API za pomocą języka JavaScript,
kluczowe są także opcjonalne parametry variable i callback.
Jako, że Same Origin Policy przeglądarki nie zezwala na
wykonywanie żądań XmlHttpRequest do innych domen niż ta, z
której pochodzi dokument, konieczne jest użycie techniki znanej
jako JSON-P. Aby otrzymać w swojej aplikacji dane, należy:
http://api.blip.pl/updates?limit=20 zastępujemy
http://api.blip.pl/updates.json?limit=20
<script>,
odwołujący się do urla odpowiedniej akcji API, wraz z
parametrami callback, variable,
lub oboma z nich.
W zależności od podanych parametrów wyróżniamy następujące sytuacje:
callback
Otrzymane dane zostaną przekazane funkcji o nazwie będącą
wartością parametru callback jako pierwszy argument.
variable
Otrzymane dane zostaną przypisane zmiennej o nazwie będącą
wartością parametru variable.
callback oraz variable
Otrzymane dane zostaną przypisane zmiennej o nazwie będącą
wartością parametru variable, po czym zostanie wykonana
funkcja o nazwie z parametru callback. Będzie to
wywołanie pozbawione argumentów.
Jednocześnie, ustawienie paremetru callback lub
variable, skutkuje nieco inną formą odpowiedzi - wszędzie tam
gdzie w innych przypadkach zwracane jest 204 No Content,
dostajemy 200 OK i callback/przypisanie na pustą tablicę lub
ewentualnie pusty literał JSON. Jest to związane z wyżej
wymienioną techniką pobierania odpowiedzi z API, która
uniemożliwia odczytywanie kodów odpowiedzi.
O tym jaki rezultat przyniosła dana operacja najbardziej skrótowo informuje numer nagłówka HTTP zwróconej przez serwer odpowiedzi. Przyjęta została następująca konwencja:
Dla obsługi sytuacji, w których obsługa kodów HTTP jest
niemożliwa lub utrudniona, cześć wywołań w przypadku wystąpienia
błędu dodatkowo zwraca w treści odpowiedzi obiekt błędu w
formacie JSON. Szczególnie, w sytuacji kiedy ustawiony jest
parametr callback lub variable, a nie
została dokonana autoryzacja, zwracany jest następujący błąd:
{"error": {"name": "unauthorized"}}
oznaczający oczywiście brak uwierzytelnienia. Podobnie, jako że
nie da się załadować jako <script> odpowiedzi o kodzie
400, w przypadku ustawienia parametrów callback lub/i variable
zamiast niej zwrócony zostanie kod 200 i treść:
{"error": {"name": "bad_request"}}
Pod określeniem update rozumiemy dowolną wiadomość
utworzoną w ramach serwisu - zarówno status jak i wiadomość
kierowaną (od jednego użytkownika do drugiego). Zasób ten
stanowi trzon całego API, jednocześnie zasoby Status i
DirectedMessage są właściwie jedynie swego rodzaju "filtrami",
pozwalającymi wykonywać operacje tylko na wiadomościach, bądź
tylko na statusach, implementując jednak identyczny interfejs
(pomijając rozróżnianie prefiksu ">odbiorca:" przy tworzeniu
wiadomości za pomocą zasobu update). Sposób użycia każdego z
tych zasobów najprościej zrozumieć za pomocą paru prostych
zasad:
Statusy/wiadomości danego użytkownika znajdują się pod adresem url: /users/foo/[updates,statuses,directed_messages]
Statusy/wiadomości wszystkich użytkowników znajdują się pod adresem url: /[updates,statuses,directed_messages]/all
Jako skrót, pozostałe akcje znajdującę się pod /[updates,statuses,directed_messages], gdy nie został podany przedrostek "/users/[nazwauzytkownika]" przyjmują że chodzi o użytkownika obecnie zautentykowanego.
Wszystkie akcje posiadają odpowiedniki ze słowem "since", umożliwiające twórcom aplikacji dynamiczne odświeżanie treści wraz z upływem czasu (powtarzający się co jakiś czas "poll" o nową treść).
{
'id': 1,
'created_at': "2007-10-18 11:27:20",
'transport': {'id': 6, 'name': 'www'},
'body': 'foobar http://rdir.pl/jk2hg',
'type': 'Status',
'user_path': '/users/frania',
'pictures_path': '/updates/1/pictures',
'recording_path': '/updates/1/recording',
'movie_path': '/updates/12/movie'
}
Pola "pictures_path", "recording_path" i "movie_path" występują w
reprezentacji tylko i wyłącznie wtedy, kiedy do update'u dołączone są
odpowiednio obrazki, nagranie lub film.
Utworzenie statusu o treści "elo-pld"
$ curl -v -H'Accept: application/json' -H'X-Blip-api: 0.02' -u foo:s0p3l -F "update[body]=elo-pld" http://api.blip.pl/updates * About to connect() to api.blip.pl port 80 * Trying 85.232.236.216... * connected * Connected to api.blip.pl (85.232.236.216) port 80 * Server auth using Basic with user 'foo' > POST /updates HTTP/1.1 Authorization: Basic Zm9vOnMwcDNs== User-Agent: curl/7.13.1 (powerpc-apple-darwin8.0) libcurl/7.13.1 OpenSSL/0.9.7l zlib/1.2.3 Host: api.blip.pl Pragma: no-cache Accept: application/json X-Blip-api: 0.02 Content-Length: 146 Expect: 100-continue Content-Type: multipart/form-data; boundary=----------------------------49950f0df8f7
Pobranie update'u o numerze 345672
$ curl -v -H'Accept: application/json' -H'X-Blip-api: 0.02' -u foo:s0p3l http://api.blip.pl/updates/345672 * About to connect() to api.blip.pl port 80 * Trying 85.232.236.216... * connected * Connected to api.blip.pl (85.232.236.216) port 80 * Server auth using Basic with user 'foo' > GET /updates/345672 HTTP/1.1 Authorization: Basic Zm9vOnMwcDNs== User-Agent: curl/7.13.1 (powerpc-apple-darwin8.0) libcurl/7.13.1 OpenSSL/0.9.7l zlib/1.2.3 Host: api.blip.pl Pragma: no-cache Accept: application/json X-Blip-api: 0.02
{
'id': 1,
'created_at': "2007-10-18 11:27:20",
'transport': {'id': 6, 'name': 'www'},
'body': 'Witaj heniek!',
'type': 'DirectedMessage',
'user_path': '/users/jakisnadawca',
'recipient_path': '/users/jakisodbiorca'
}
{
'id': 1,
'created_at': "2007-10-18 11:27:20",
'transport': {'id': 6, 'name': 'www'},
'body': 'foobar http://rdir.pl/jk2hg',
'type': 'Status',
'user_path': '/users/frania'
}
{
'id': 1,
'created_at': "2008-08-18 17:27:20",
'transport': {'id': 6, 'name': 'www'},
'body': 'Prywatne siema!',
'type': 'PrivateMessage',
'user_path': '/users/jakisnadawca',
'recipient_path': '/users/jakisodbiorca'
}
$ curl -v -H'Accept: application/json' -H'X-Blip-api: 0.02' http://api.blip.pl/updates/363884/movie * About to connect() to api.blip.pl port 80 * Trying 85.232.236.216... * connected * Connected to api.blip.pl (85.232.236.216) port 80 > GET /updates/272707/movie HTTP/1.1 User-Agent: curl/7.13.1 (powerpc-apple-darwin8.0) libcurl/7.13.1 OpenSSL/0.9.7l zlib/1.2.3 Host: api.blip.pl Pragma: no-cache Accept: application/json X-Blip-api: 0.02
{
'url': "http://blip.pl/user_generated/movie/12.flv",
'id': 12
}
$ curl -v -H'Accept: application/json' -H'X-Blip-api: 0.02' http://api.blip.pl/updates/363884/recording * About to connect() to api.blip.pl port 80 * Trying 85.232.236.216... * connected * Connected to api.blip.pl (85.232.236.216) port 80 > GET /updates/272707/recording HTTP/1.1 User-Agent: curl/7.13.1 (powerpc-apple-darwin8.0) libcurl/7.13.1 OpenSSL/0.9.7l zlib/1.2.3 Host: api.blip.pl Pragma: no-cache Accept: application/json X-Blip-api: 0.02
{
'url': "http://blip.pl/user_generated/recordings/138.mp3",
'id': 138
}
{
'id': 12086,
'url': "http://blip.pl/user_generated/update_pictures/12086.jpg",
'update_path': "http://api.blip.pl/updates/1234"
}
Klucz update_path nie pojawia się w reprezentacji obrazków zwracanych dla już zadanego update'u (w wywołaniu /updates/321/pictures).
$ curl -v -H'Accept: application/json' -H'X-Blip-api: 0.02' http://api.blip.pl/updates/272762/pictures * About to connect() to api.blip.pl port 80 * Trying 85.232.236.216... * connected * Connected to api.blip.pl (85.232.236.216) port 80 > GET /updates/272762/pictures HTTP/1.1 User-Agent: curl/7.13.1 (powerpc-apple-darwin8.0) libcurl/7.13.1 OpenSSL/0.9.7l zlib/1.2.3 Host: api.blip.pl Pragma: no-cache Accept: application/json X-Blip-api: 0.02
{
'id': 34190,
'created_at': "2007/12/03 04:16:03",
'hit_count': 2,
'original_link': "http://info.gadu-gadu.pl/uslugi/blip",
'shortcode': "swv"
}
$ curl -v -H'Accept: application/json' -H'X-Blip-api: 0.02' http://api.blip.pl/shortlinks/all * About to connect() to api.blip.pl port 80 * Trying 85.232.236.216... * connected * Connected to api.blip.pl (85.232.236.216) port 80 > GET /updates/272707/recording HTTP/1.1 User-Agent: curl/7.13.1 (powerpc-apple-darwin8.0) libcurl/7.13.1 OpenSSL/0.9.7l zlib/1.2.3 Host: api.blip.pl Pragma: no-cache Accept: application/json X-Blip-api: 0.02
$ curl -v -H'Accept: application/json' -H'X-Blip-api: 0.02'
-u foo:s0p3l
-F "shortlink[original_link]=http://shortlink.example.com/"
http://api.blip.pl/shortlinks
* About to connect() to api.blip.pl port 80
* Trying 85.232.236.216... * connected
* Connected to api.blip.pl (85.232.236.216) port 80
* Server auth using Basic with user 'foo'
> POST /updates HTTP/1.1
Authorization: Basic Zm9vOnMwcDNs==
User-Agent: curl/7.13.1 (powerpc-apple-darwin8.0) libcurl/7.13.1
OpenSSL/0.9.7l zlib/1.2.3
Host: api.blip.pl
Pragma: no-cache
Accept: application/json
X-Blip-api: 0.02
Content-Length: 188
Expect: 100-continue
Content-Type: multipart/form-data;
boundary=----------------------------207e7a1fad9a
HTTP/1.1 200 OK
{"shortcode":"a1b2","created_at":"2009-09-30 10:05:52",
"hit_count":0,"original_link":"http://shortlink.example.com/",
"id":123}
jack.
Jeżeli uwierzytelniony użytkownik to jack to zawartość zasobu będzie
taka sama jak zasobu /dashboard. Jeżeli uwierzytelniony
będzie inny użytkownik to widoczne będą statusy, wiadomości kierowane
i powiadomienia użytkownika jack
jack to zawartość zasobu będzie
taka sama jak zasobu /dashboard/since/50. Jeżeli uwierzytelniony
będzie inny użytkownik to widoczne będą statusy, wiadomości kierowane
i powiadomienia użytkownika jack oraz zawartość zasobu
/dashboard uwierzytelnionego użytkownika.
Powyższe zasoby pobierane przez API zachowują się analogicznie do zasobów przeglądanych przez WWW.
{
'id': 2,
'login': 'wiesiek',
'background_path': '/users/sztywny/background',
'avatar_path': '/users/sztywny/avatar',
'current_status_path': '/statuses/12345'
}
'current_status' może zawierać także klucze 'pictures_path' lub
'recording_path' jeśli do statusu są dołączone obrazki lub nagranie.
Zasób subscriptions reprezentuję relację pomiędzy dwoma użytkownikami - "obserwującym" i "obserwowanym". Fakt ten należy mieć cały czas na uwadze podczas korzystania z wywołań API - niezależnie od tego jaki rodzaj subskrypcji pobieramy, zawsze zwrócony zostanie obiekt zawierający dwóch userów, a także transport poprzez który relacja ta zachodzi (obserwowanie przez kokpit, przez Jabbera itp.). Wobec powyższego, nie ma także bezpośredniej korespondencji np. pomiędzy liczbą osób obserwowanych pokazywanych na blip.pl, a liczbą obiektów zwracanych przez /subscriptions/from - jedna osoba może być obserwowana przez dwa transporty, wtedy zaś otrzymujemy dwa obiekty, z identycznymi 'tracked_user' i 'tracking_user', a różnym 'transport'.
{
'tracked_user_path': '/users/foo',
'tracking_user_path': '/users/sztywny/',
'transport': {'id': 5, 'name': 'gg'}
}
$ curl -v -H "Accept: application/json" -H "X-Blip-api: 0.02"
-u "foo:" -X PUT "http://api.blip.pl/subscriptions/bar?
subscription\[www\]=1&subscription\[im\]=0"
* About to connect() to api.blip.pl port 80 (#0)
* Trying 85.232.236.216... connected
* Connected to api.blip.pl (85.232.236.216) port 80 (#0)
* Server auth using Basic with user 'foo'
> PUT /subscriptions/bar?subscription[www]=1&subscription[im]=0 HTTP/1.1
> Authorization: Basic Zm9vOmR1cGEuOA==
> User-Agent: curl/7.17.1 (i486-pc-linux-gnu) libcurl/7.16.2
> Host: localhost
> Accept: application/json
> X-Blip-api: 0.02
>
< HTTP/1.1 200 OK
< Connection: close
< Date: Thu, 13 Dec 2007 16:08:14 GMT
< Set-Cookie: _session_id=30b65e83d42b3893156e061d69d4ab7f; path=/
< Status: 200 OK
< X-Runtime: 1.06279
< ETag: "99914b932bd37a50b983c5e7c90ae93b"
< Cache-Control: private, max-age=0, must-revalidate
< Server: Mongrel 1.0.1
< Content-Type: js; charset=utf-8
< Content-Length: 2
<
* Closing connection #0
{}
{
'id': 1234,
'url': 'http://blip.pl/user_generated/avatars/1234.jpg'
}
$ curl -v -F "avatar[file]=@avatar.jpg" -H "Accept: application/json"
-H "X-Blip-api: 0.02" -u "foo:s0p3l" -X POST
"http://api.blip.pl/avatar"
* About to connect() to api.blip.pl port 80 (#0)
* Trying 85.232.236.216... connected
* Connected to api.blip.pl (85.232.236.216) port 80 (#0)
* Server auth using Basic with user 'foo'
> POST /avatar HTTP/1.1
> Authorization: Basic Zm9vOmR1cGEuOA==
> User-Agent: curl/7.17.1 (i486-pc-linux-gnu) libcurl/7.16.2
> Host: api.blip.pl
> Accept: application/json
> X-Blip-api: 0.02
> Content-Length: 166115
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=----------------------------fae37744ca25
>
< HTTP/1.1 201 Created
< Connection: close
< Date: Thu, 13 Dec 2007 14:22:41 GMT
< Set-Cookie: _session_id=2775143d87fda1d12ed31e774dd9b0ef; path=/
< Status: 201 Created
< Location: http://api.blip.pl/users/8209/avatar
< X-Runtime: 9.55353
< Cache-Control: no-cache
< Server: Mongrel 1.0.1
< Content-Type: js; charset=utf-8
< Content-Length: 2
<
* Closing connection #0
{}
{
'id': 4321,
'url': 'http://blip.pl/user_generated/background/4321.jpg'
}
$ curl -v -F "background[file]=@background.jpg" -u "foo:s0p3l" -X POST
-H "Accept: application/json" -H "X-Blip-api: 0.02"
"http://api.blip.pl/background"
* About to connect() to api.blip.pl port 80 (#0)
* Trying 85.232.236.216... connected
* Connected to api.blip.pl (85.232.236.216) port 80 (#0)
* Server auth using Basic with user 'foo'
> POST /background HTTP/1.1
> Authorization: Basic Zm9vOmR1cGEuOA==
> User-Agent: curl/7.17.1 (i486-pc-linux-gnu) libcurl/7.16.2
> Host: api.blip.pl
> Accept: application/json
> X-Blip-api: 0.02
> Content-Length: 166119
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=----------------------------5ac4c2c49c4a
>
< HTTP/1.1 201 Created
< Connection: close
< Date: Thu, 13 Dec 2007 14:27:09 GMT
< Set-Cookie: _session_id=59539702ddcc1b006551bc4d5ab76f08; path=/
< Status: 201 Created
< Location: http://api.blip.pl/users/8210/background
< X-Runtime: 3.35142
< Cache-Control: no-cache
< Server: Mongrel 1.0.1
< Content-Type: js; charset=utf-8
< Content-Length: 2
<
* Closing connection #0
{}