Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: PHP: REST API ja osoitteiden jäsennys

Sivun loppuun

juhauta [28.01.2020 08:17:15]

#

Minua kiinnostaisi tietää että minkälaisella koodipätkällä oikeasti toimii parseri joka osaa ottaa get-parametrit monimutkaisista REST-osoitteista, kuten

api/search/pumas/productType/clothing/color/black,red,white/size/38-40

Sen verran olen nähnyt että .htaccess voi ohjata kaikki nuo /api/ jälkeiset "kansiot"
joita ei ole olemassa yhden php-tiedoston query stringiin. se ei ole ongelma

Mutta miten minun tulisi tunnistaa tuosta että color = black,red,white
eikä color = size, eli tuo järjestys, varsinkin kun parametri voi myös puuttua kokonaan, pelkkä explode / tuskin on kovin luotettava

Lebe80 [28.01.2020 09:27:18]

#

Itsellä herää kysymys, miksi rest apisi pitää olla sellainen, josta et saa parametrejä ulos?

Eli mikset vaan käytä suoraan get-parametrejä urlissasi, jolloin saisit ne ilman htaccess-kikkailua ulos?

Eli käytä ihan suosiolla vaan get-parametrejä urleissasi, kun kyseessä on rajapinta. "Sef-urlien" käytöllä ei saavuteta rajapintojen urleissa mitään.


api?search=pumas&productType=clothing&color­=black,red,white&size=38-40

The Alchemist [28.01.2020 10:07:30]

#

Ei kannata apinoida jotain, mitä ei ymmärrä. Silloin tulee helposti otettua mallia puhtaasta paskasta. Maailma on täynnä täysin kyvyttömiä idiootteja, joille annetaan tehtäväksi tehdä asioita, joihin heillä ei ole kompetenssia. Antamasi urlin perusteella myös tutkimasi rajapinnan tekijä on tällainen ammattitaidoton apina.

Mutta: kun alla on kunnollinen freimis, niin voit tarvittaessa vaikka parsia osoitteet itse kirjoittamallasi koodilla ja ohjata freimiksen käsittelemään pyynnöt oikein ilman hirvittävää määrää purkkakoodia. Asiassa ei silti ole mitään järkeä, sanoin vain sen olevan mahdollista tietyssä viitekehyksessä.

Kuten Lebe80 sanoi, niin ehdottomasti kannattaa käyttää HTTP-standardin mukaisia urleja ja niiden ominaisuuksia niin kuin on tarkoitettu. Polkuun tulee laittaa vain kriittiset muuttujat kuten pyydettävän resurssin ID. Muunlaiset hakutehdot ja muut muuttujat tulee laittaa query-osaan muotoon "?a=foo&b=bar".

Jos muuttuja hyväksyy listan sanoja, niin pilkku voi olla huono erotin, koska standardin mukaan se pitäisi enkoodata muotoon %2C eli urleista tulee sotkuisia. Sen sijaan jos käytät välilyöntiä, niin sen voi enkoodata siististi plusmerkiksi: ?foo=a+b+c+d. (Palvelimen päässä arvo olisi "a b c d" eli plussat muuntuvat välilyönneiksi.)

Mikäli minä olisin tekemässä rajapintaa tuotehakuun, niin urlit olisivat tällaisessa muodossa:

/api/shoes?brand=puma&color=black+red+white&size=38+39+40

(Nyt parametri productType on kiinteä osa urlia ja sen arvo on shoes, koska haetaan kenkiä.)

PHP:llä koodatessa voi myös hyödyntää PHP:n tapaa parsia foo[]-muotoiset muuttujat taulukoksi:

/api/shoes?brand=puma&color[]=black&color[]=red&color[]=white&size[]=38&size[]=39&size[]=40

Mutta kuten tästä näkee, niin syntaksi on sotkuinen ja kyselystä tulee myös pidempi kuin ns. optimoidulla versiolla.

Lebe80 [28.01.2020 11:54:04]

#

The Alchemist kirjoitti:

Mutta kuten tästä näkee, niin syntaksi on sotkuinen ja kyselystä tulee myös pidempi kuin ns. optimoidulla versiolla.

Vaikka tuo esimerkkisi on "ihmissilmälle" sotkuinen, on se mielestäni silti parempi, sillä se on "ohjelmalle" selvempi.

Ja totta myös nuo pilkut ja viivat. Mitä standardimpi, sen vähemmän tarvitsee poikkeuksia parsintaan.

The Alchemist [28.01.2020 15:33:35]

#

Lebe80 kirjoitti:

The Alchemist kirjoitti:

Mutta kuten tästä näkee, niin syntaksi on sotkuinen ja kyselystä tulee myös pidempi kuin ns. optimoidulla versiolla.

Vaikka tuo esimerkkisi on "ihmissilmälle" sotkuinen, on se mielestäni silti parempi, sillä se on "ohjelmalle" selvempi.

Itse en näe mitään eroa siinä, millaisen syntaksin valitsee, kun jonkinlaisen esikäsittelijän siinä joutuu joka tapauksessa kirjoittamaan. Vaikka nojaisi PHP:n natiiviin taulukkomuuttujanotaatioon, niin silti syötteen joutuu validoimaan ja varmistamaan, että muuttujassa foo todellakin on taulukko eikä yksittäistä arvoa.

Täytyy ottaa huomioon sekin, että rajapinnan ns. syntaksi vaikuttaa myös siihen, miten monimutkaista sen käyttö on asiakasohjelmasta käsin. Yksinkertaisempi on silloin parempi.

Itse päädyin plusmerkin / välilyönnin käyttöön erottimena siitä syystä, että se on myös HTML:ssä natiivi syntaksi, kun arvoja luetaan attribuuteista. On myös yksinkertaisempaa kasata kysely js:llä, kun riittää taulukkomuotoisen syötteen sarjallistaminen välilyöntiä käyttäen sen sijaan, että jokainen muuttuja pitäisi yksitellen lisätä silmukkaa tms. käyttäen.

const inputValues = ['foo', 'bar', 'baz']
const formData = new FormData()

// Tapa I
formData.set('q', inputValues.join('+'))

// Tapa II
formData.append('q[]', inputValues[0])
formData.append('q[]', inputValues[1])
formData.append('q[]', inputValues[2])

Metabolix [29.01.2020 00:32:25]

#

The Alchemist kirjoitti:

Yksinkertaisempi on silloin parempi. – –

On myös yksinkertaisempaa kasata kysely js:llä, kun riittää taulukkomuotoisen syötteen sarjallistaminen välilyöntiä käyttäen – –

formData.set('q', inputValues.join('+'))

Omatekoinen yhdistely tarkoittaa myös, että valittu erotinmerkki ei saa esiintyä arvoissa. Jos tällaista takuuta ei ole, ollaankin kohta purkkakoodin äärellä, kun tehdään omaa enkoodaus- ja dekoodausviritelmää tai kun käytetään saman kyselyn eri parametreille eri ratkaisua sen mukaan, ovatko arvot taatusti yksisanaisia vai voivatko ne mielivaltaista tekstiä tietokannasta.

Lisäksi omassa ”yksinkertaisessa” ratkaisussa on uusi mahdollisuus ohjelmointivirheeseen, kuten edellä näkyy: Tarkoitus oli ilmeisesti erotella arvot välilyönnillä, joka enkoodautuu nätisti plussaksi. Kuitenkin koodissa arvot yhdistetään plussalla, joka sitten enkoodautuu %2B:ksi.

The Alchemist [29.01.2020 04:05:42]

#

Metabolix kirjoitti:

Omatekoinen yhdistely tarkoittaa myös, että valittu erotinmerkki ei saa esiintyä arvoissa. Jos tällaista takuuta ei ole, ollaankin kohta purkkakoodin äärellä, kun tehdään omaa enkoodaus- ja dekoodausviritelmää tai kun käytetään saman kyselyn eri parametreille eri ratkaisua sen mukaan, ovatko arvot taatusti yksisanaisia vai voivatko ne mielivaltaista tekstiä tietokannasta.

Sellainen nyt vaan ei ole realistinen skenaario, että pitäisi yhdistää vapaatekstihaku ja "mikä tahansa ehto täsmää" -hakumuoto (samaan parametriin). Jos tällaista yrittää tehdä, niin silloin on syytä pysähtyä miettimään, että mihin sitä on tarkoitus käyttää.

Edes PHP:n taulukkonotaatio ei poista sitä paljon todennäköisempää ongelmaa, että miten JA- ja TAI-operaattorit voi ilmoittaa kyselyssä*. Ei ole millään tavalla realistista, että voisi vain käyttää $_GET-muuttujaa tekemättä mitään sen sisältämille arvoille. Kuten sanoin jo aiemmin, niin jonkinlainen validointi on joka tapauksessa tehtävä eli omalta parsimiselta ei voi välttyä kuin laskemalla Herran huomaan.

* Itse tyytyisin tällöin parametrin toistoon: "?q=foo+bar&q=bad+baz".

(Tässä hakuehto on "(foo TAI BAR) JA (bad TAI baz)". Tämähän on suoraan sovellettavissa myös vapaatekstihakuun useilla eri hakuehdoilla.)

Joka tapauksessa HTTP-pyynnön query-osan parsiminen normalisoituun muotoon on paljon helpompaa kuin sen muuntaminen edelleen SQL-kyselyksi. Siksi en näe mitään syytä tehdä rumaa ja vaikeatulkintaista paskaa, kun samalla vaivalla saa tehtyä selkeää ja yksinkertaistakin. Se on parin tunnin homma kirjoittaa parseri melkein mille tahansa notaatiolle.

Itse koen myös paljon yksinkertaisemmaksi lähteä siitä alkuolettamasta, että kaikki syötteet ovat merkkijonoja sen sijaan, että pitäisi varautua siihen, että jokin syötteistä saattaa olla taulukko tai jopa moniulotteinen taulukko. (?foo[][]=bar).

Taustalla tässä on sellainen agenda, että mielestäni hyvä rajapinta on saavutettava myös meille ihmisasiakasohjelmille. Itse testailen paljon curlilla ja suoraan selaimen osoiteriviltäkin, enkä todellakaan halua kirjoittaa kryptistä höttöä täysin turhan takia. Ja aina kyse ei edes ole testailusta vaan saatan tarkistaa jotain tekemällä kyselyn rajapintaan sen sijaan, että loggaisin sisään shelliyhteydellä ja yrittäisin kirjautua tietokantaan...

Toinen syy valitsemalleni notaatiolle on se, että välilyönnin käyttäminen avainsanojen erottimena on myös osa HTML-standardia (class="foo bar"). Muistaakseni juuri silloin aloin käyttää välilyöntiä erottimena, kun olin tekemässä klienttiä, jossa osa kyselystä luettiin HTML-elementtien attribuuteista. Totesin, että sehän on paljon järkevämpää kuin pilkun käyttö, koska arvon voi lukea suoraan elementistä ja työntää kyselyyn tekemättä konversioita välissä, ja pilkkuhan on vain ihmisten oppima päähänpinttymä jostain ala-asteelta.

Metabolix kirjoitti:

Lisäksi omassa ”yksinkertaisessa” ratkaisussa on uusi mahdollisuus ohjelmointivirheeseen, kuten edellä näkyy: Tarkoitus oli ilmeisesti erotella arvot välilyönnillä, joka enkoodautuu nätisti plussaksi. Kuitenkin koodissa arvot yhdistetään plussalla, joka sitten enkoodautuu %2B:ksi.

Todettakoon aluksi, että bugi oli asiakasohjelman hahmotelmassa eikä rajapinnan toteutuksessa. Pidetään mielessä, mistä tässä keskustellaan.

Plussa unohtui siihen takia, että Angularin kanssa puljatessa sen oma HttpParams-luokka ei enkoodaa plusmerkkiä, mutta välilyönti kääntyy myöhemmin selaimen käsittelyssä muotoon %20.

FormData + URLSearchParams näköjään enkoodaa välilyönnin plusmerkiksi, joten se tekeekin suoraan sen mitä olen aiemmin muiden työkalujen kanssa hackailemalla yrittänyt saavuttaa...

Metabolix [29.01.2020 23:33:31]

#

The Alchemist kirjoitti:

Sellainen nyt vaan ei ole realistinen skenaario, että pitäisi yhdistää vapaatekstihaku ja "mikä tahansa ehto täsmää" -hakumuoto (samaan parametriin).

Tämä nyt taas ei liittynyt mitenkään siihen, mitä kommentoin.

Kun tuotteen väreihin lisätään "light blue", väriluettelon erottelu välilyönneillä lakkaa toimimasta. Eli omatekoinen ratkaisu vaatii aivan erityisen rajoitettua dataa. Tietysti johonkin lukuihin ja kovakoodattuihin sanoihin se sopii.

juhauta [31.01.2020 10:49:28]

#

Kiitos viesteistä!

Olin tosiaan ymmärtänyt väärin että "hieno" tapa tehdä REST api olisi pelkästään /foo/bar/baz ja eikä ollenkaan ?foo=foo&bar=bar&..

Kun luin läpi microsoftin GRAPH dokumentaatiota, niin sielä oli jopa näin

https://graph.microsoft.com/v1.0/me/calendar/events?$top=5&$select=subject,bodyPreview,isAllDay,start,end&$filter=start/dateTime ge '2017-01-01'

eli filtterissä selvästi monimutkaisempi "komento" ja selectissä pilkkulistaus.

Ei kai tuo %2C haittaa mitään jos urli menee globaalin wrapperin läpi aina ennen rajapintakyselyä..

The Alchemist [02.02.2020 06:17:24]

#

Metabolix kirjoitti:

The Alchemist kirjoitti:

Sellainen nyt vaan ei ole realistinen skenaario, että pitäisi yhdistää vapaatekstihaku ja "mikä tahansa ehto täsmää" -hakumuoto (samaan parametriin).

Tämä nyt taas ei liittynyt mitenkään siihen, mitä kommentoin.

Kun tuotteen väreihin lisätään "light blue", väriluettelon erottelu välilyönneillä lakkaa toimimasta. Eli omatekoinen ratkaisu vaatii aivan erityisen rajoitettua dataa. Tietysti johonkin lukuihin ja kovakoodattuihin sanoihin se sopii.

Muutaman backendin tehneenä en ole vielä törmännyt tällaiseen tarpeeseen, siksi sivuutin sen kommentoimatta. Vaikka en tue sitä ajatusta, että kaikki mahdolliset asiat pitäisi ajaa tietokantaan ja lukea ulos ID-numeroina, niin joka tapauksessa rajapintakyselyissä en ole koskaan päätynyt sallimaan vapaatekstiä muuta kuin kirjaimellisessa vapaatekstihaussa.

En oikein ymmärrä, miksi värivaihtoehdot yms. olisivat kannassa irrallisina tekstikenttinä eikä etukäteen määriteltyinä arvojoukkoina, jolloin niille voi määritellä myös ns. turvalliset avainsanat.

juhauta kirjoitti:

Olin tosiaan ymmärtänyt väärin että "hieno" tapa tehdä REST api olisi pelkästään /foo/bar/baz ja eikä ollenkaan ?foo=foo&bar=bar&..
Kun luin läpi microsoftin GRAPH dokumentaatiota, niin sielä oli jopa näin

https://graph.microsoft.com/v1.0/me/calendar/events?$top=5&$select=subject,bodyPreview,isAllDay,start,end&$filter=start/dateTime ge '2017-01-01'

eli filtterissä selvästi monimutkaisempi "komento" ja selectissä pilkkulistaus.

Microsoftin Graph on taas sellainen "erittäin älykäs" rajapinta, joka yrittää sallia skriptaamiseen vertautuvat ominaisuudet hakukyselyissä. Sille on omat perusteensa, mutta jokaisen rajapinnan ei kannata yrittää olla näin monimutka... monipuolinen.

Monipuolisuuden johdosta se ei myöskään käytä url-muuttujia puristisella tavalla oikein, vaan ilmeisesti kaikki suotimet pitää antaa filter-muuttujassa, ja esitystapa on ihmeellinen. Tuohan on lähes aito SQL-kysely hieman eri muodossa esitettynä.

Jos haluaa rakentaa erittäin kehittyneet hakuominaisuudet tarjoavan rajapinnan, niin restiin perustuva alusta voi olla väärä valinta. Meillä on jo avoin standardi, joka soveltuu sellaiseen paremmin: GraphQL.

https://graphql.org/


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta