Tiedon hakeminen avoimesta rajapinnasta
Learning objectives
- Tunnet käsitteet rajapinta ja avoin rajapinta.
- Tiedät miten rajapinnasta haetaan dataa.
- Osaat kirjoittaa ohjelman, joka hakee dataa rajapinnasta.
Käsitteellä rajapinta (API, application programming interface) tarkoitetaan rajapinnan takana olevan järjestelmän kanssa kommunikoinnin mahdollistavia palveluita kuten funktioita, metodeja ja osoitteita. Rajapintaa voi ajatella er äänlaisena välikätenä -- kun ohjelma kutsuu rajapinnan tarjoamaa palvelua, rajapinta välittää kutsun rajapinnan takana olevalle järjestelmälle.
Rajapintaan liittyvä dokumentaatio, mikäli sellainen on saatavilla, kuvaa muunmuassa rajapinnan palvelut, palveluille tehtävien kutsujen muodot, sekä rajapinnan palauttamat arvot. Rajapinnoilla ei kuitenkaan aina ole dokumentaatiota -- joskus dokumentaatio voi olla myös hyvin lyhyt.
Avoimet rajapinnat
Avoimella rajapinnalla tarkoitetaan rajapintaa, "jonka kaikki ominaisuudet ovat julkisia ja jota voi käyttää ilman rajoittavia ehtoja" (lähde: http://avoinrajapinta.fi/).
Tutustumme seuraavaksi rajapintojen käyttöön osana sovelluksiamme keskittyen verkossa oleviin rajapintoihin. Koska tehtävien tarkastamiseen käytetyltä palvelimelta ei pääse verkkoon, suurimmassa osassa tämän osan tehtävistä ei ole automaattisia testejä.
Avointen rajapintojen löytäminen
Verkossa on useita sivustoja, jotka listaavat avoimia rajapintoja. Alla on nostettu esille muutama.
Osa rajapinnoista vaatii kirjautumisen tai salaisen avaimen. Osa taas rajoittaa minkälaisista järjestelmistä rajapintaan voi tehdä pyyntöjä. Tässä materiaalissa käsiteltävät rajapinnat on valittu siten, että niiden käyttöön ei tarvitse kirjautumista, eikä niiden käyttöön ole muita rajoitteita.
Tiedon hakeminen avoimesta rajapinnasta
Tiedon hakeminen verkosta tapahtuu HttpRequest
-luokan tarjoamalla asynkronisella getString
-funktiolla. Funktio löytyy kirjastosta dart:html
.
Funktio getString
hakee sille parametrina annettua merkkijonoa vastaavan verkko-osoitteen sisällön ja palauttaa sen merkkijonona. Koska funktio on asynkroninen, tulee sitä kutsua await
-avainsanan kanssa -- tämän lisäksi await
-avainsanan sisältävä funktio merkitään async
-avainsanalla.
Alla olevassa esimerkissä haetaan osoitteessa https://dog.ceo/api/breeds/image/random
olevan rajapinnan palauttama merkkijono. Kun merkkijono on haettu, se tulostetaan käyttäjälle.
import 'dart:html';
main() async {
var osoite = 'https://dog.ceo/api/breeds/image/random';
var sisalto = await HttpRequest.getString(osoite);
print(sisalto);
}
Riippuen käyttämästämme ohjelmointiympäristön versiosta, ohjelman tulostus tulee joko konsoliin tai tulostusalueeseen. Alla on eräs mahdollinen tulostus.
program output
{"message":"https:\/\/images.dog.ceo\/breeds\/retriever-curly\/n02099429_1068.jpg","status":"success"}
Kun suoritamme ohjelman uudestaan, ohjelma tulostaa toisen merkkijonon. Tarkemmin katsottuna merkkijono on JSON-muotoinen dokumentti.
Kertaushetki
JSON-muotoisia dokumentteja käsiteltiin Data ja tieto -kurssin toisessa osassa. Mikäli termi JSON tuntuu oudolta etkä muista miten JSON-muotoinen dokumentti muunnetaan sanakirjaksi, kertaa tässä kohtaa Data ja tieto -kurssin toisen osan loppupuoli.
Osoite https://dog.ceo/api/breeds/image/random on rajapinta palveluun, joka tarjoaa satunnaisesti valitun koirakuvan osoitteen. Kuvan osoite on JSON-muotoisen dokumentin muuttujan message
arvona.
JSON-muotoisen dokumentin muuntaminen sanakirjaksi onnistuu dart:convert
-kirjaston tarjoamalla jsonDecode
-funktiolla (vastaa aiemmin käsiteltyä kutsua json.decode
). Alla olevassa esimerkissä osoitteesta https://dog.ceo/api/breeds/image/random
haetaan JSON-muotoinen merkkijono, joka muunnetaan sanakirjaksi. Tämän jälkeen tulostetaan sanakirjassa olevan muuttujan message
arvo.
import 'dart:html';
import 'dart:convert';
main() async {
var osoite = 'https://dog.ceo/api/breeds/image/random';
var sisalto = await HttpRequest.getString(osoite);
var sanakirja = jsonDecode(sisalto);
print(sanakirja['message']);
}
program output
Kun kopioimme yllä olevan osoitteen selaimen osoitekenttään ja painamme enteriä, näemme kuvan koirasta.
Kuvan näyttäminen HTML-dokumentissa
HTML-dokumenteissa kuva näytetään img
-elementin avulla. Elementin attribuutille src
määritellään osoite, josta kuva haetaan. Alla on kuvattuna kuvaelementin käyttö. Elementin attribuutin src
arvoksi on asetettu rajapinnasta haettu osoite, leveydeksi (attribuutti width
) on asetettu 100% sivun leveydestä, ja alt
-attribuutin arvoksi on asettu Koira
. Attribuutin alt
arvo näytetään selaimessa mikäli kuvaa ei voida ladata tai näyttää.
<img
src='https://images.dog.ceo/breeds/kuvasz/n02104029_2424.jpg'
width='100%'
alt='Koira'>
</img>
Selaimessa sivu näyttää seuraavalta.
Rajapintaa käyttävä selaimessa toimiva ohjelma
Edeltävässä esimerkissä käsiteltiin tiedon hakemista avoimesta rajapinnasta. Jatketaan teeman parissa ja tarkastellaan rajapintaa käyttävän selainsovelluksen toteuttamista.
Alla olevassa ohjelmointiympäristössä oleva ohjelma sisältää napin ja kuvaelementin. Kuvaelementissä näytetään oletuksena osoitteessa https://images.dog.ceo/breeds/kuvasz/n02104029_2424.jpg
oleva kuva. Napin painaminen vaihtaa kuvaa.
Kun käyttäjä painaa nappia, ohjelma tekee pyynnön osoitteessa https://dog.ceo/api/breeds/image/random
olevaan rajapintaan. Rajapinnasta palautetaan JSON-muotoinen merkkijono, joka muunnetaan sanakirjaksi. Tämän jälkeen sanakirjassa olevaan message
-avaimeen liittyvä arvo kopioidaan muuttujan kuvanOsoite
arvoksi. Lopulta muuttujan kuvanOsoite
arvo asetetaan elementtiin liittyvän metodin setAttribute
avulla kuvaelementin attribuutin src
arvoksi.
Kuvan vaihtumisen nopeus riippuu haettavan kuvan koosta ja verkkoyhteydestä. Kun kuvaelementin src
-attribuutin arvo muutetaan, selain lähtee hakemaan uutta kuvaa src
-attribuutin arvon ilmaisemasta osoitteesta. Mikäli kuva on hyvin suuri tai verkkoyhteys on hidas, kuvan lataaminen ja sitä kautta käyttäjälle näyttäminen voi kestää pitkään.
Oletuksena verkkosivut eivät salli niiden sisällön hakemista kolmannen osapuolen sivuston kautta. Huomaamme tämän seuraavaa ohjelmaa suoritettaessa.
import 'dart:html';
main() async {
var osoite = 'https://yle.fi/';
var sisalto = await HttpRequest.getString(osoite);
print(sisalto);
}
Ohjelman suorittaminen näyttää kryptisen virheviestin Uncaught Error: Instance of 'ProgressEvent'
. Virheviesti ei kerro todellisesta ongelmasta -- kyse on tässä siitä, että sivuilla ei ole sallittuna sisällön jakaminen kolmansille osapuolille (ns. cross-origin resource sharing) on kielletty tai että sisältöön ei pääse käsiksi (palvelu ei ole käynnissä).
Sana tietoturvasta
Edellisessä havainnossa on taustalla tietoturvaan ja selaimen toimintaan liittyvä ilmiö. Kun selain tekee HttpRequest.getString
-pyynnön kolmannen osapuolen palveluun (esimerkiksi Ylen sivuille), selain lähettää pyynnön mukana kirjautumistiedot mikäli käyttäjä on kirjautunut palveluun. Jos tällaiset pyynnöt sallitaan, voi ilkeämielinen ohjelmoija esimerkiksi yrittää lähettää sivun käyttäjän nimissä Ylen sivujen kommenttikenttään viestin.
Osoitteessa https://github.com/public-apis/public-apis oleva avointen rajapintojen listaus sisältää CORS-sarakkeen. Selainohjelmissa tiedon hakeminen onnistuu rajapinnoista, joiden CORS-sarakkeen arvo on Yes
.
Käsittelimme edellä melko suoraviivaista datan hakemista avoimesta rajapinnasta. Tietoa hakeva sovellus tekee pyynnön rajapintaan, muuntaa rajapinnasta saadun datan käsiteltävään muotoon, ja lopulta tekee datalla jotain. Edellä käsittelimme koirien kuvia, mutta haettava data voi olla oikeastaan lähes mitä vaan -- sama järjestys pätee silti.
Rajapinnasta saadun datan näyttäminen osissa
Tarkastellaan seuraavaksi vitsejä tarjoavan rajapinnan käyttöä. Osoitteessa https://simple-joke-api.deno.dev/random oleva rajapinta palauttaa satunnaisen vitsin JSON-muotoisena merkkijonona.
Rajapinnasta saatu dokumentti sisältää kaksi arvoa. Muuttuja "setup" sisältää vitsin alustuksen (tyypillisesti kysymys), ja muuttuja "punchline" sisältää humoristisen vastauksen kysymykseen.
Kirjoitetaan ohjelma, joka hakee rajapinnasta satunnaisen vitsin ja näyttää käyttäjälle vitsiin liittyvän kysymyksen. Käyttäjälle näytetään kysymyksen lisäksi nappi, jota painamalla ohjelma näyttää vastauksen. Vastaus näytetään vasta kun käyttäjä painaa nappia.
Aloitetaan ohjelmasta, joka hakee vitsin ja tulostaa kysymyksen ja vastauksen.
import 'dart:html';
import 'dart:convert';
main() async {
var osoite = 'https://simple-joke-api.deno.dev/random';
var sisalto = await HttpRequest.getString(osoite);
var sanakirja = jsonDecode(sisalto);
var kysymys = sanakirja['setup'];
var vastaus = sanakirja['punchline'];
print(kysymys);
print(vastaus);
}
program output
Why don't you find hippopotamuses hiding in trees? They're really good at it.
Luodaan seuraavaksi HTML-dokumentti, joka sisältää paikan kysymystekstille, napin, ja paikan vastaustekstille. Kysymysteksti ja vastausteksti ovat aluksi tyhjiä, ja nappi sisältää tekstin "Well, tell me?".
<p id='kysymys'></p>
<button id='vastausnappi'>Well, tell me?</button>
<p id='vastaus'></p>
Vitsin näyttämiseen käytetty ohjelma hakee rajapinnasta vitsin ja asettaa vitsiin liittyvän kysymyksen kysymys
-id:llä tunnistettuun tekstielementtiin. Tämän lisäksi, ohjelmaan määritellään napin painallukseen reagoiva tapahtumankäsittelijä -- tapahtumankäsittelijä näyttää vitsiin liittyvän vastauksen.
Eräs mahdollinen toteutus on kuvattuna alla olevassa ohjelmointiympäristössä.
Yllä napin painamiseen liittyvä toiminnallisuus on toteutettu anonyyminä funktiona. Tämä mahdollistaa pääsyn kysymyksen vastaukseen: mikäli tapahtuma käsiteltäisiin erillisessä funktiossa, ei vastaus
-muuttujaan pääsisi edellä kuvatulla tavalla käsiksi.
Kysely avoimeen rajapintaan
Rajapintoihin tehtävät kyselyt toteutetaan tyypillisesti osaksi osoitetta määriteltävien arvojen avulla. Tarkastellaan tätä IP-osoitteiden sijaintien selvittämiseen tarkoitetulla https://freegeoip.app/-palvelulla.
Sijaintien selvittämiseen tarkoitettuun rajapintaan tehtävät kyselyt ovat muotoa https://freegeoip.app/json/osoite
, missä osoite
on haettava IP-osoite tai verkko-osoite. Rajapinta palauttaa haettavaan osoitteeseen liittyvät tiedot.
Esimerkiksi kysely osoitteeseen https://freegeoip.app/json/fbi.gov
palauttaa seuraavanlaisen dokumentin.
{
"ip":"104.16.149.244",
"country_code":"US",
"country_name":"United States",
"region_code":"",
"region_name":"",
"city":"",
"zip_code":"",
"time_zone":"America/Chicago",
"latitude":37.751,
"longitude":-97.822,
"metro_code":0
}
Toteutetaan seuraavaksi ohjelma, joka kysyy käyttäjältä IP-osoitetta tai verkko-osoitetta. Kun käyttäjä syöttää IP-osoitteen tai verkko-osoitteen, ohjelma tulostaa käyttäjälle osoitteeseen liittyvät tiedot.
Luodaan ensin HTML-dokumentti, joka sisältää tekstikentän tiedon syöttämiseen, hakunapin, ja vastauksen näyttämiseen tarkoitetun tekstielementin.
<input type='text' id='hakuteksti' />
<button id='nappi'>Hae!</button>
<p id='vastaus'></p>
Hakujen tekemiseen käytettävässä ohjelmassa tulee olla napin painalluksia kuunteleva tapahtumankäsittelijä, toiminnallisuus tekstikentässä olevan syötteen hakemiseen, sekä toiminnallisuus rajapinnassa olevan tiedon hakemiseen ja käsittelyyn.
Kun käyttäjä painaa nappia, ohjelma hakee syötettyä tekstiä vastaavan osoitteeen rajapinnasta. Rajapinnasta saatu vastaus asetetaan tekstielementin arvoksi.
import 'dart:html';
main() {
querySelector('#nappi').onClick.listen(hae);
}
hae(e) async {
InputElement tekstikentta = querySelector('#hakuteksti');
var haku = tekstikentta.value;
var osoite = 'https://freegeoip.app/json/$haku';
var vastaus = await HttpRequest.getString(osoite);
querySelector('#vastaus').text = vastaus;
}
Rajapinnalta saatua vastausta ei muotoilla, vaan se näytetään käyttäjälle sellaisenaan. Ohjelmaa voisi jatkokehittää esimerkiksi niin, että vastaus näytettäisiin taulukossa ja että . Tällä tavalla vastauksena saatu data olisi luettavampaa ymmärrettävämpää.
Voit kokeilla sovellusta alla olevassa ohjelmointiympäristössä. Mikäli teet kyselyn sovelluksella siten, että jätät hakukentän tyhjäksi, saat selville oman IP-osoitteesi.
Rajapinnoista saadun datan tulkinta ja täsmällisyys
Rajapintojen käyttö osana omia ohjelmia vaatii pienimuotoista ymmärrystä rajapintojen tarjoamasta datasta. Esimerkiksi yllä käytetty palvelu tarjoaa haetuille IP-osoitteille ja verkko-osoitteille sijainnit maantieteellisen koordinaattijärjestelmän leveys- ja pituusasteina (latitude ja longitude).
Sijainnit eivät kuitenkaan esimerkiksi heijasta kotisi sijaintia, vaan esimerkiksi palveluntarjoajan toimiston (tai muun vastaavan paikan) sijaintia.
Rajapintojen luomiseen ja verkkoon viemiseen tutustutaan tarkemmin Aalto-yliopiston kurssilla Web Software Development.
Hi! Please help us improve the course!
Please consider the following statements and questions regarding this part of the course. We use your answers for improving the course.
I can see how the assignments and materials fit in with what I am supposed to learn.
I find most of what I learned so far interesting.
I am certain that I can learn the taught skills and knowledge.
I find that I would benefit from having explicit deadlines.
I feel overwhelmed by the amount of work.
I try out the examples outlined in the materials.
I feel that the assignments are too difficult.
I feel that I've been systematic and organized in my studying.
How many hours (estimated with a precision of half an hour) did you spend reading the material and completing the assignments for this part? (use a dot as the decimal separator, e.g 8.5)
How would you improve the material or assignments?