Java REST API Showdown: Which is the Best Framework on the Market?

palveluiden kehittäminen Javalla, mukaan lukien levon sovellusliittymät, ei ollut aina helppoa tai tuottoisaa ennen kuin kevät tuli ja muutti maisemaa. Siitä on kulunut monta vuotta, ja yhteisössä on syntynyt uusia puitteita.

yksi näistä kehyksistä oli Mikronaut. Sen on kehittänyt OCI, sama yritys grailsin takana, ja heidän tavoitteenaan on auttaa kehittäjiä luomaan mikropalveluja ja palvelemattomia sovelluksia.

mukana on myös Quarkus, toinen viime vuoden aikana suosiotaan kasvattanut kehys. RedHatin kehittämä Quarkus lupaa tarjota nopean käynnistyksen ja vähemmän muistin käyttöä, kaksi yleistä ongelmaa kehittäessään LEPOPALVELUJA Javassa.

kaikkien näiden valintojen kanssa on erittäin tärkeä kysymys: kuinka helppoa on luoda palvelu näillä kolmella kehyksellä?

tässä opetusohjelmassa luot turvallisen LEPOSOVELLUKSEN käyttäen Springiä, Micronautia ja Quarkusta nähdäksesi, miten ne eroavat toisistaan ja mikä niistä sopii parhaiten tarpeisiisi.

Sisällysluettelo

  • edellytykset Java REST API: llesi
  • luo Okta-tili käyttäjien hallintaan

  • luo tokeneita käyttäen OpenID Connect debuggeria
  • Rakenna Java REST API Mikronautilla
    • kehitä Java-palveluasi
  • Rakenna Java REST API Quarkuksella

  • Rakenna Java REST API kanssa Spring Boot
  • final thoughts on REST API Java: micronaut, Quarkus, ja Spring Boot
  • Jos haluat, voit katsoa tämän opetusohjelman Screencastina. 👇

    edellytykset Java REST API: llesi

    Tämä opetusohjelma käyttää Maven 3+. Varmista, että se on asennettu ja käytettävissä ennen kuin jatkat. Voit toki käyttää myös Gradea, mutta YMMV.

    rakennat sovelluksia, jotka todentavat pyynnöt OAuth 2.0: n avulla, jotka on suojattu Okta-sovelluksella. Eikö sinulla ole Okta-tiliä? Ei hätää, uuden luominen vie alle minuutin. Lisäksi Okta tukee standardeja, kuten JWT, OAuth 2.0 ja OIDC. Tarjoamme tukea tunnetuille kehyksille, kuten Java EE, Spring Boot ja Spring Security. Pahus, meillä on jopa Maven plugin, joka automatisoi kaiken sinulle.

    pyörää ei tarvitse keksiä uudelleen!

    luo Okta-tili käyttäjien hallintaan

    avaa pääte ja suorita seuraava komento:

    mvn com.okta:okta-maven-plugin:register

    sinua pyydetään syöttämään seuraavat tiedot:

    • etunimi
    • sukunimi

    • Sähköposti
    • yritys

    kun olet vastannut kysymyksiin, saat sähköpostin, jolla aktivoit upouuden tilisi. Kun olet aktivoinut tilisi, suorita seuraava komento:

    mvn com.okta:okta-maven-plugin:spring-boot

    Tämä komento luo sinulle sovelluksen, jossa on Auth code flow ja Spring Securityn redirect URI Oktalle.

    Okta Mavenin luoma sovellus

    muistaaksesi sen paremmin, voit luoda saman sovelluksen manuaalisesti:

    • mene Okta: n kehittäjien kotisivuille ja kirjaudu tilillesi.
    • klikkaa sovelluksia > Lisää sovellus > Web > Seuraava.

    näet seuraavan ruudun:

    Okta uusi sovellus

    ennen kuin jatkat, tee seuraavat muutokset sovellukseen:

    • login redirect URIs:
      • http://localhost:8080/login/oauth2/code/okta
      • https://oidcdebugger.com/debug
    • Avustustyyppi sallittu
      • valtuutuskoodi
      • implisiittinen (Hybridi)

    implisiittinen avustustyyppi (ID ja pääsy token sallittu tarkistettu) on tarpeen hakea access token selaimessasi.

    kentät, joita ei ole mainittu edellä, voivat pitää oletusarvonsa.

    kun olet saanut sen valmiiksi, napsauta Valmis. Sovellus on valmis!

    seuraava vaihe on opetella luomaan kelvollinen poletti sen avulla.

    luo tokeneita OpenID Connect-Debuggerin avulla

    Okta mahdollistaa käyttäjiensä hallinnan pilvessä sen sovellusliittymien avulla. Sen avulla voit myös suojata sovelluksia OAuth 2.0 ja OpenID Connect (alias, OIDC). OAuth 2.0 tarjoaa mekanismin delegoidulle valtuutukselle, mikä tarkoittaa, että sinun ei tarvitse tallentaa käyttäjätunnuksia sovellukseesi. Sen sijaan, voit siirtää että OAuth 2.0 tarjoaja (Okta, tässä tapauksessa). OIDC tarjoaa identiteettikerroksen OAuth 2.0: n päälle ja siksi Oktan kaltaisia yrityksiä kutsutaan ”identiteetin tarjoajiksi” eli Maansisäisiksi henkilöiksi.

    olet rekisteröinyt sovelluksesi Oktaan ja voit nyt luoda Tokenin päästäksesi siihen. Yksi kirjautumisohjaus Uri olet rekisteröinyt on OpenID Connect verkkosivuilla.

    pyynnöt validoidaan tokenilla. Voit luoda tämän Tokenin käyttämällä OpenID Connect-debuggeria. Tämä sivusto tarjoaa sinulle helpon tavan luoda käyttäjätiedot okta-sovelluksen käyttäjille.

    mene https://oidcdebugger.com ja täytä seuraavat tiedot:

    • Authorize URI: https://{yourOktaDomain}/oauth2/default/v1/authorize
    • Redirect URI: https://oidcdebugger.com/debug
    • Client ID: {yourOktaClientId}
    • Scope: openid email profile tate: devnonce: (pidä oletusarvo)

    • response type:token

    {yourOktaDomain} tilisi kotisivun oikeasta yläkulmasta:

    okta homepage

    Jos haluat löytää okta-asiakastunnuksesi, noudata alla olevia ohjeita:

    • Siirry sovelluksiin
    • Valitse oma Verkkosovellukseni
    • valitse Yleiset

    asiakastunnukset löytyvät asiakkaan tunnistetiedot-osiosta:

    asiakkaan tunnistetiedot

    kun olet täyttänyt kaikki kentät, valitse Lähetä pyyntö. Sinut ohjataan Okta-kirjautumissivulle.

    kun olet onnistuneesti todennettu, sinut ohjataan uudelleen Oidc-debuggeriin, jossa näet luodun Tokenin:

    Generated Token

    käytät tätä tokenia tukevan pääsyn palveluihin, joita aiot rakentaa.

    nyt kun sinulla on Okta-tili ja osaat luoda tokeneita Okta-sovelluksella, aletaan vertailla kehyksiä!

    Rakenna Java REST API Micronautin kanssa

    ensimmäinen askel Micronaut-palvelun kehittämiseen on ladata SDKMAN!. SDKMAN! on työkalu useiden SDK: iden rinnakkaisten versioiden hallintaan, jota käytät Micronaut-asiakasohjelman asentamiseen.

    voit ladata SDKMANIN! suorittamalla seuraavan komennon:

    curl -s https://get.sdkman.io | bash

    nyt Micronautin voi asentaa itse. Suorita seuraava komento päätteessä:

    sdk install micronaut

    kun komento on suoritettu, on tietokoneellasi saatavilla uusin Micronaut-versio.

    olet valmis aloittamaan sovelluksen kehittämisen!

    kehitä Java-palveluasi

    mene hakemistoon, johon haluat sovelluksen luoda, ja suorita seuraava komento:

    mn create-app com.okta.rest.app --build maven

    Tämä komento luo projektin, jonka perusrakenne on Mikronaut-projekti. Micronaut käyttää oletuksena Gradea, mutta koska käytät--build maven, se käyttää sen sijaan Mavenia.

    seuraava vaihe on lisätä turvakirjastot projektin sisälle. Muokkaa pom.xml tiedostoa ja lisää seuraavat riippuvuudet:

    <dependency> <groupId>io.micronaut</groupId> <artifactId>micronaut-security</artifactId></dependency><dependency> <groupId>io.micronaut</groupId> <artifactId>micronaut-security-jwt</artifactId></dependency>

    nämä riippuvuudet mahdollistavat tietoturvan – erityisesti OAuth 2.0: n JWT: n kanssa – projektin sisällä. Nyt kun sinulla on kaikki riippuvuudet paikallaan, voit aloittaa päätepisteen luomisen.

    Create the following class in src/main/java/com/okta/rest/controller:

    package com.okta.rest.controller;import io.micronaut.http.MediaType;import io.micronaut.http.annotation.Controller;import io.micronaut.http.annotation.Get;import io.micronaut.http.annotation.Produces;import io.micronaut.security.annotation.Secured;import io.micronaut.security.rules.SecurityRule;import java.security.Principal;@Controller("/hello")public class HelloController { @Get @Secured(SecurityRule.IS_AUTHENTICATED) @Produces(MediaType.TEXT_PLAIN) public String hello(Principal principal) { return "Hello, " + principal.getName() + "!"; }}

    @Controllerannotation indicates to micronaut this component will receives in the /hello Path.

    luokalla on vain yksi menetelmä, jonka nimi on hello()@Get Huomautus osoittaa, että menetelmä vastaanottaa HTTP GET-pyyntöjä. Tarvitset @Produces, koska Micronaut ’ n oletuspalautustyyppi on JSON-olio. Koska olet palauttamassa yksinkertaista tekstiä, sinun täytyy nimenomaisesti määritellä nämä tiedot menetelmässä.

    viimeinen huomautus on @Secured. Se vain kertoo Micronaut, että tämä menetelmä on saatavilla vain todennetut käyttäjät.

    sinulla on nyt ohjain, joka on suojattu, mutta et ole vielä määrittänyt suojausasetusta. Määritetään Micronaut muodostamaan yhteys Okta-sovellukseen.

    nimeä src/main/resources/application.ymlapplication.properties ja lisää seuraava suojauskokoonpano:

    micronaut.security.enabled=truemicronaut.security.token.jwt.enabled=truemicronaut.security.token.jwt.signatures.jwks.okta.url=https://{yourOktaDomain}/oauth2/default/v1/keys

    korvaa {yourOktaDomain} okta-tilisi arvolla.

    yllä oleva kokoonpano mahdollistaa tietoturvan OAuth 2.0: n avulla. Vakuutat, että OAuth 2.0-asiakkaasi tulee Okta: sta, määrittämällä liikkeeseenlaskijan Okta-organisaatiostasi.

    mahdollistat myös JSON web tokenien eli JWTs: n käytön. Koska haluat lukea tiedot Okta, sinun täytyy ilmoittaa, mistä voit löytää jwks (JSON Web Key Set) vahvistaa JWT allekirjoitukset.

    on aika testata palvelusi! Aloita sovellus suorittamalla seuraava komento:

    ./mvnw mn:run

    sovelluksen ollessa käynnissä, suorita seuraava komento:

    curl -X GET -I http://localhost:8080/hello

    yllä oleva komento tuottaa samanlaisen tuloksen kuin tämä:

    HTTP/1.1 401 UnauthorizedDate: Tue, 8 Jan 2019 15:47:36 GMTtransfer-encoding: chunkedconnection: close

    kuten näkyy, pyyntö ei mennyt läpi. Jotta se toimisi, sinun on syötettävä Oidc-Vianetsintätyökalun noutama OAuth 2.0 access token. Määritä pääsymerkki TOKEN muuttuja komentotulkassasi.

    TOKEN=eyJraWQiOiJxOE1QMjFNNHZCVmxOSkxGbFFWNlN...

    suorita alla oleva komento:

    curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/hello

    nyt se toimii odotetusti! Tällä kertaa tervehdyksen saa vastaukseksi:

    näet, että Micronaut vaatii hyvin vähän koodia luodakseen turvallisen REST API: n. Jos laskit koodirivit, huomaat, että ~24% on riippuvuuksia XML: ssä (8 riviä), Java-koodi on vain 22 riviä koodia ja suojauskokoonpano kestää 3 riviä. Micronaut ’ n sisäänrakennettu OAuth 2.0-tuki on helppo integroida Oktaan, ja heillä on dokumentaatiossaan jopa Octan opas.

    hienoa! Nyt katsotaan, miten luot saman sovelluksen Quarkuksen avulla.

    Rakenna Java REST API Quarkuksen kanssa

    kehittääksesi sovellustasi Quarkuksen avulla tarvitset vain Mavenin asennettuna, muita riippuvuuksia ei tarvita.

    aloitetaan sovelluksen luominen! Siirry hakemistoon, jossa haluat luoda sen, ja suorita seuraava komento:

    mvn io.quarkus:quarkus-maven-plugin:1.8.1.Final:create \ -DprojectGroupId=com.okta.rest \ -DprojectArtifactId=quarkus \ -DclassName="com.okta.rest.quarkus.HelloResource" \ -Dpath="/hello" \ -Dextensions="jwt"

    yllä oleva komento luo projektin Quarkus Maven-liitännäistä käyttäen. Se luo resurssin nimeltä HelloResource, joka vastaanottaa pyyntöjä /hello polku. Lisäät myös quarkuksen JWT-laajennuksen projektiin.

    kun olet luonut projektin, muokkaa src/java/com/okta/rest/quarkus/HelloResource.java ja lisää käyttäjätiedot hello() menetelmä:

    package com.okta.rest.quarkus;import io.quarkus.security.Authenticated;import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.Produces;import javax.ws.rs.core.Context;import javax.ws.rs.core.MediaType;import javax.ws.rs.core.SecurityContext;import java.security.Principal;@Path("/hello")public class HelloResource { @GET @Path("/") @Authenticated @Produces(MediaType.TEXT_PLAIN) public String hello(@Context SecurityContext context) { Principal userPrincipal = context.getUserPrincipal(); return "Hello, " + userPrincipal.getName() + "!"; }}

    yllä oleva luokka käyttäytyy samalla tavalla kuin Micronaut ’ ssa luotu. Se lukee käyttäjän tiedot pyynnössä syntyneen Tokenin perusteella ja palauttaa löytyvälle käyttäjälle tervehdysviestin.

    et ole vieläkään määrittänyt Quarkusta liikkeeseenlaskijasi ja Okta: n avainten kanssa, joten tehdään niin.

    Edit src/main/resources/application.properties ja lisätään seuraava koodi:

    mp.jwt.verify.publickey.location=https://{yourOktaDomain}/oauth2/default/v1/keysmp.jwt.verify.issuer=https://{yourOktaDomain}/oauth2/default

    tehty! Quarkus-versio sovelluksestasi on valmis testattavaksi. Siirry projektikansioon ja suorita seuraava komento:

    ./mvnw compile quarkus:dev

    yllä oleva komento käynnistää sovelluksen.

    ensimmäinen vaihe on varmistaa, että saat 401 - Unauthorized, kun et käytä oikeita tunnuksia.

    Suorita päätteessä seuraava komento:

    curl -X GET -I http://localhost:8080/hello

    odotetusti tuloksena on HTTP 401-vastaus:

    HTTP/1.1 401 Unauthorizedwww-authenticate: Bearer {token}Content-Length: 0

    Jos suoritat tämän saman pyynnön, mukaan lukien oidc-Vianetsintätyökalun token, sen tulee palauttaa tervehdyssanoma.

    suorita seuraava komento:

    curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/hello

    se toimi kuin taikakalu! Minun tapauksessani tulos oli:

    Quarkus vaatii vielä vähemmän koodiriviä kuin Mikronaut! Se luo sovelluksen riippuvuudet mukana, on 25 riviä Java-koodia, ja vain 2 riviä kokoonpano. Kyllä, lines of code on typerä vertailu, mutta se osoittaa myös, miten nämä puitteet vaativat hyvin vähän koodia kehittää turvallisia sovelluksia.

    kaksi alas, yksi vielä! Nyt kun olet voinut toteuttaa sovelluksen Micronaut ja Quarkus, lopetetaan luomalla sama sovellus Spring Boot.

    Rakenna Java REST API Spring Bootilla

    Spring Bootilla ei ole edellytyksiä aloittaa sovelluksen luomista, joten aloitetaan luomalla projekti!

    avaa päätteesi ja suorita seuraava komento:

    curl https://start.spring.io/starter.zip -d language=java \ -d bootVersion=2.3.4.RELEASE \ -d dependencies=web,okta \ -d packageName=com.okta.rest \ -d name=spring-boot \ -d type=maven-project \ -o spring-boot.zip

    yllä oleva komento Luo spring-boot.zip tiedoston, jossa on Mavenia käyttävä Jousikäynnistyssovellus. Voit purkaa tiedoston spring-boot hakemistoon alla olevaa komentoa käyttäen.

    unzip spring-boot.zip -d spring-boot

    nyt toteutat pyynnön vastaanottavan ohjaimen.

    Create a com.okta.rest.controller package and a HelloController class in it:

    package com.okta.rest.controller;import org.springframework.security.core.annotation.AuthenticationPrincipal;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import java.security.Principal;@RestControllerpublic class HelloController { @GetMapping("/hello") public String hello(@AuthenticationPrincipal Principal principal) { return "Hello, " + principal.getName() + "!"; }}

    tässä asetelma on hyvin samanlainen kuin muissa kehyksissä. Luokalle merkitään merkintä@RestController, jotta Spring saa tietää, että luokassa tulee pyyntöjä.@GetMappingvastaanottaa HTTP GET-pyyntöjä/hellopath. Todennetun käyttäjän hakemiseen käytetään@AuthenticationPrincipal merkintää.

    muista kehyksistä poiketen sinun ei tarvitse määrittää, että tämä päätepiste on todennettu, koska Spring hallitsee jo tätä tietoa kokoonpanoistaan.

    viimeinen vaihe on lisätä liikkeeseenlaskijan tiedot, jotta Spring Securityn OIDC-Tuki voi automaattisesti löytää päätepisteet, joiden kanssa se tarvitsee kommunikoida.

    Edit src/main/resources/applications.properties ja lisää seuraava kokoonpano:

    okta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default

    kokeillaan! Aloita Spring Boot-sovellus Mavenin avulla.

    ./mvnw spring-boot:run

    sitten, avaa pääte ja suorita alla oleva komento:

    curl -X GET -I http://localhost:8080/hello

    vastaus on HTTP 401-virhe, koska et sisällyttänyt tunnusta:

    HTTP/1.1 401Set-Cookie: JSESSIONID=316DCFD55C302A8D69EFD865411DFA77; Path=/; HttpOnlyWWW-Authenticate: BearerX-Content-Type-Options: nosniffX-XSS-Protection: 1; mode=blockCache-Control: no-cache, no-store, max-age=0, must-revalidatePragma: no-cacheExpires: 0X-Frame-Options: DENYContent-Length: 0Date: Thu, 09 Jan 2020 15:46:34 GMT

    testaa se uudelleen, nyt kulkee Token:

    curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/hello

    se toimi! Kuten muissakin palveluissa, tämän komennon tulos on seuraava:

    Jousikäynnistyskellot PIENIMMÄLLÄ koodimäärällä: 17 riviä Java ja vain 1 rivi kokoonpano! Kevät on aina ollut erinomainen helpottamaan kehittäjien elämää, joten tämä ei tule yllätyksenä.

    That ’ s it! Toteutit perus Java REST API kaikissa kolmessa kehyksessä!

    Final Thoughts on REST API With Java: Micronaut, Quarkus, and Spring Boot

    when it comes to developing your REST API, all three frameworks did the job well. Vain vähän koodia ja joitakin asetuksia, voit kehittää turvallisen sovelluksen Okta ja OAuth 2.0.

    kevät on ollut olemassa jo vuosia, se on laajalti suosittu, ja sillä on monia piirteitä ekosysteeminsä ympärillä. Henkilökohtaisesti uskon edelleen, että se on paras vaihtoehto, kun ohjelmointi Java.

    Micronaut ja Quarkus kasvattavat suosiotaan ja saavat vauhtia Java-yhteisön sisällä. Jos sinulla on edessään suorituskykyä ongelmia, tai ehkä jos olet kipeä muutos, voit antaa yksi niistä yrittää ja nähdä, miten se menee.

    suorituskyky on useimmin korostettu vertailukohta näiden kolmen viitekehyksen välillä. Jos etsit nopeaa käynnistystä palvelemattomassa ympäristössä tai kykyä luoda natiivikuvia Gralvm: llä, Micronaut ja Quarkus toimivat todennäköisesti hyvin. Ihan vain huvin vuoksi, näiden sovellusten aloitusajat ovat seuraavat (perustuen kolmen yrityksen keskiarvoon):

    • Micronaut: 474ms
    • Quarkus: 1132ms
    • Spring Boot: 1014ms

    sain nämä numerot ajamalla jokaisen framework ’ s Maven goals for development.

    • Micronaut: ./mvnw mn:run
    • Quarkus: ./mvnw compile quarkus:dev
    • Jousisaappaat: ./mvnw spring-boot:run

    näitä komentoja ei ole optimoitu nopeuteen, joten paketoin jokaisen sovelluksen ./mvnw package ja aloitin ne java -jar.

    • Micronaut: 596ms
    • Quarkus: 658ms
    • Spring Boot: 1878ms

    HUOM: nämä luvut laskettiin vuoden 2019 MacBook Prolle, jossa oli 2,4 GHz 8-ytiminen Intel Core i9-suoritin ja 64 Gt RAM-muistia. OpenJDK 15: tä käytettiin ilman JAVA_OPTS – asetusta.

    Jos etsit vielä nopeampia käynnistysaikoja, voit käyttää Gralvm: ää. Sen sijaan, että olisin itse tehnyt ajoitustestejä, katsoin jokaisen projektin dokumentaatiota.

    • Micronaut: 12ms ensimmäisen Micronaut Graal-sovelluksen luomisen mukaan.
    • kvarkki: 14ms kvarkin ja Graalin mukaan: Käynnistyshorroksessa yliääninopeudella, Subatominenkoko infoqissa. Quarkus docs ei listaa käynnistysaikaa.
    • Spring Boot: 44ms mukaan Spring Graal Native 0.6.0 julkaistu.

    loppujen lopuksi pystyt kehittämään turvallisen sovelluksen tuottavasti riippumatta siitä, minkä valinnan teet.

    Haluatko vilkaista lähdekoodia? Löydät sen GitHub osoitteessa okta-java-rest-api-comparison-example.

    Haluatko oppia lisää Javasta, REST-sovellusliittymistä ja suojatuista sovelluksista? Tässä muutamia muita blogimme julkaisuja, joita saatat pitää hyödyllisinä:

    • Katso GraalVM Käännä Java binääreiksi
    • OAuth 2.0 Java Guide: Suojaa sovelluksesi 5 minuutissa
    • Java Microservices with Spring Boot and Spring Cloud
    • kuinka kehittää Quarkus-sovellus Java-ja Oidc-todennuksella

    Lisää tämänkaltaisia viestejä, Seuraa @oktadev Twitterissä. Julkaisemme myös säännöllisesti screencasteja YouTube-kanavallemme!

    Changelog:

    • 23.9.2020: päivitetty Micronaut 2.0.2, Quarkus 1.8.1 ja Spring Boot 2.3.4. Katso koodimuutokset esimerkkisovelluksessa GitHubissa. Muutokset tähän viestiin ovat nähtävissä oktadeveloper / okta-blogissa#423.
    • 21.toukokuuta 2020: lisätty lähtöajat java -jar ja gralvm: llä juokseminen. Tämän artikkelin muutoksia voi tarkastella oktadeveloper / okta-blogissa#304.
    • 20.toukokuuta 2020: päivitetty Micronaut 1.3.5, Quarkus 1.4.2 ja Spring Boot 2.3.0. Katso koodimuutokset esimerkkisovelluksessa GitHubissa. Tämän artikkelin muutoksia voi tarkastella oktadeveloper / okta-blogissa#301.
    • Jan 30, 2020: päivitetty optimoimaan Mikronaut Micronaut-tiimin palautteen perusteella. Myös uudelleen lasketut aloitusajat perustuvat keskimäärin kolmeen yritykseen. Katso koodimuutokset esimerkkisovelluksessa GitHubissa. Tämän artikkelin muutoksia voi tarkastella oktadeveloper / okta-blogissa#176.

    Related Posts

    Vastaa

    Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *