Seadistamine
Lae alla Visual Studio programm järgmiselt lehelt: https://visualstudio.microsoft.com/downloads/

Võta Community Edition.
Seadistamine on next-next-next loogikaga ehk sätteid seadistades muutma ei pea.
Oluline on aga võtta ASP.NET ja veebiarenduse lisa:

Uue projekti loomine
Ava Visual Studio ja loo uus projekt.

Saame valida, millist juba valmismalli kasutame. Võtame juba loodud kontrolleri koos API otspunktiga.

Paneme nimeks “veeb”.

Loome kontrolleri malli ning võimaldame kasutada ka Swaggerit (OpenAPI).

Programm läheb käima rohelise kolmnurkse nupu kaudu.
Seejärel avatakse lokaalserveris Swagger, kus on defineeritud meile kaasa antud API otspunktid: https://localhost:7074/swagger/index.html

Port on kirja pandud “Properties” kaustas olevas failis “launchSettings.json”, profiles -> projekti_nimi -> applicationUrl taga. Soovi korral võid seda vahetada. Lihtsuse huvides vahetan selle ära:
“applicationUrl”: “https://localhost:4444;http://localhost:5119“,

Muudame kontrolleri nime WeatherController vastu (sellega seoses peame ka muutma lisaks kolm nime kasutuskohta koodis).
Ära muutes on kood järgnev:
using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherController> _logger;
public WeatherController(ILogger<WeatherController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}

Ning nüüd on kontrolleri otspunkt kättesaadav URL-lt: https://localhost:4444/Weather
Pane tähele, et suured ja väiksed tähed pole olulised, aga http://localhost:4444/Weather (jälgi https -> http) ei toimi.


Esmaste API päringute tegemine
Teeme esimesi päringuid.
Selleks loome uue faili: PrimitiividController, millega saame teha päringuid primitiivsete tüüpidega.
Uue faili loomiseks vajuta kausta “Controllers” kausta peal parema klõpsuga, vali “add” ning võta “New Item…”. Pane loodud faili nimeks PrimitiividController.cs ning vali tühja API kontrolleri mall (API Controller – Empty).


Kui soovid faili nime muuta, pead rakenduse kinni panema (kui oled juba juhuslikult käivitanud) ning muutma parema klõpsuga failil “Rename” või F2 klahvivajutusega nime.
Pane tähele, et pead kasutama kontrolleri nime API otspunktis – kui määrasid otspunktiks “hi”, siis pead seda välja kutsuma “localhost:PORT/kontrolleri_nimi/hi”
Tee esimene avalik API otspunkt, milleks on “hello-world”.
using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class PrimitiividController : ControllerBase
{
// GET: primitiivid/hello-world
[HttpGet("hello-world")]
public string HelloWorld()
{
return "Hello world at " + DateTime.Now;
}
}
}
Pane rakendus käima, ava brauser ning mine aadressile “https://localhost:4444/Primitiivid/hello-world” . Brauser peab sulle tagastama selle, mille oled lisanud “return” järele – kirje koos kuupäevaga.

Absoluutselt kõik API päringute funktsioonid on defineeritud kui “public” ning sellele järgnev tagastatav tüüp ehk määratlemine, millist liiki väärtuse oled kirjutanud “return” järele (hetkel string).
API otspunktile saab ka sisendit anda. Selleks on vaja kirjutada otspunkti järel loogelised sulud ning selle sisse kirjutada muutuja nimi. Funktsiooni sulgude sees pead sisestama muutuja, andma talle tüübi ning kasutama sama nime nagu URL-s.
using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class PrimitiividController : ControllerBase
{
// GET: primitiivid/hello-world
[HttpGet("hello-world")]
public string HelloWorld()
{
return "Hello world at " + DateTime.Now;
}
// GET: primitiivid/hello-variable/mari
[HttpGet("hello-variable/{nimi}")]
public string HelloVariable(string nimi)
{
return "Hello " + nimi;
}
}
}

Sisendite arv on piiramatu, kuid igaüht neist tuleb ka määratleda funktsiooni sulgude sees.
using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class PrimitiividController : ControllerBase
{
// GET: primitiivid/hello-world
[HttpGet("hello-world")]
public string HelloWorld()
{
return "Hello world at " + DateTime.Now;
}
// GET: primitiivid/hello-variable/mari
[HttpGet("hello-variable/{nimi}")]
public string HelloVariable(string nimi)
{
return "Hello " + nimi;
}
// GET: primitiivid/add/5/6
[HttpGet("add/{nr1}/{nr2}")]
public int AddNumbers(int nr1, int nr2)
{
return nr1 + nr2;
}
}
}

// GET: primitiivid/multiply/5/6
[HttpGet("multiply/{nr1}/{nr2}")]
public int Multiply(int nr1, int nr2)
{
return nr1 * nr2;
}

Võimalik on ka mitte midagi tagastada – sellisel juhul on tüüp “void”. Selle kasutusjuht on näiteks andmete sisestamine või logimine.
using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class PrimitiividController : ControllerBase
{
// GET: primitiivid/hello-world
[HttpGet("hello-world")]
public string HelloWorld()
{
return "Hello world at " + DateTime.Now;
}
// GET: primitiivid/hello-variable/mari
[HttpGet("hello-variable/{nimi}")]
public string HelloVariable(string nimi)
{
return "Hello " + nimi;
}
// GET: primitiivid/add/5/6
[HttpGet("add/{nr1}/{nr2}")]
public int AddNumbers(int nr1, int nr2)
{
return nr1 + nr2;
}
// GET: primitiivid/multiply/5/6
[HttpGet("multiply/{nr1}/{nr2}")]
public int Multiply(int nr1, int nr2)
{
return nr1 * nr2;
}
// GET: primitiivid/do-logs/5
[HttpGet("do-logs/{arv}")]
public void DoLogs(int arv)
{
for (int i = 0; i < arv; i++)
{
Console.WriteLine("See on logi nr " + i);
}
}
}
}

Kuigi me logidega käesolevas õppematerjalis ei tegele, selgitame siiski logimise mõtet tagarakenduses: Välja logimine (logging) on protsess, kus tarkvararakendus salvestab olulise teabe, nagu sündmused, veateated või kasutaja tegevused, spetsiaalsetesse logifailidesse või muudesse väljundkanalitesse, mis aitab tõrkeotsingul, jõudluse analüüsil ja süsteemi tegevuse jälgimisel.
Iseseisvad harjutused:
- Tee juhuslike numbrite generaator (või Random rand = new Random() abil) – sisestades otspunktile kaks arvu, tagastab programm juhusliku arvu nende vahel
using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class PrimitiividController : ControllerBase
{
private readonly Random rand = new Random();
// GET: primitiivid/random/1/10
[HttpGet("random/{min}/{max}")]
public int GetRandomNumber(int min, int max)
{
if (min > max) (min, max) = (max, min); // vahetab ümber, kui kasutaja annab valepidi
return rand.Next(min, max + 1); // +1, et max oleks kaasatud
}
}
}

- Sisesta sünniaasta otspunktile ning programm tagastab sulle lause, mis ütleb: “oled nii või naa aastat vana (arvutuslikult korrektselt), olenevalt kas sellel aastal on sünnipäev juba olnud”
using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class PrimitiividController : ControllerBase
{
[HttpGet("age/{year}/{month}/{day}")]
public string GetAge(int year, int month, int day)
{
var birthDate = new DateTime(year, month, day);
var today = DateTime.Today;
int age = today.Year - birthDate.Year;
if (birthDate.Date > today.AddYears(-age)) age--; // kui sünnipäev pole veel olnud, lahutame ühe
return $"Oled {age} aastat vana.";
}
}
}

Kuidas välja näeb kõik kood sellises osas:
using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class PrimitiividController : ControllerBase
{
private readonly Random rand = new Random();
// GET: primitiivid/hello-world
[HttpGet("hello-world")]
public string HelloWorld()
{
return "Hello world at " + DateTime.Now;
}
// GET: primitiivid/hello-variable/mari
[HttpGet("hello-variable/{nimi}")]
public string HelloVariable(string nimi)
{
return "Hello " + nimi;
}
// GET: primitiivid/add/5/6
[HttpGet("add/{nr1}/{nr2}")]
public int AddNumbers(int nr1, int nr2)
{
return nr1 + nr2;
}
// GET: primitiivid/multiply/5/6
[HttpGet("multiply/{nr1}/{nr2}")]
public int Multiply(int nr1, int nr2)
{
return nr1 * nr2;
}
// GET: primitiivid/do-logs/5
[HttpGet("do-logs/{arv}")]
public void DoLogs(int arv)
{
for (int i = 0; i < arv; i++)
{
Console.WriteLine("See on logi nr " + i);
}
}
// GET: primitiivid/random/1/10
[HttpGet("random/{min}/{max}")]
public int GetRandomNumber(int min, int max)
{
if (min > max) (min, max) = (max, min); // vahetab ümber, kui kasutaja annab valepidi
return rand.Next(min, max + 1); // +1, et max oleks kaasatud
}
// GET: primitiivid/age/2005/05/10
[HttpGet("age/{year}/{month}/{day}")]
public string GetAge(int year, int month, int day)
{
var birthDate = new DateTime(year, month, day);
var today = DateTime.Today;
int age = today.Year - birthDate.Year;
if (birthDate.Date > today.AddYears(-age)) age--; // kui sünnipäev pole veel olnud, lahutame ühe
return $"Oled {age} aastat vana.";
}
}
}
Mudeli koostamine
Teeme valmis veebipoe toote mudeli. Esmalt teeme kausta “Models” ja seejärel paneme uuele failile nimeks “Toode”.
Uue kausta loomine:
- Sulge rakendus (kui see on käima pandud)
- Ava Solutions Explorer paremalt (Visual Studio programmis)
- Parem klõps projekti nime peal
- Ava “Add” rippmenüü vaade
- Vajuta “New Folder”

6. Muuda nime kausta peal parema klõpsuga “Rename” või F2 klahvile vajutades
7. Pane kausta nimeks “Models”

Uue faili loomine:
- Äsjaloodud kausta parem klõps
- Vali “Add”
- Avanenud vaates vali “New item…”

4. Vali C# class (paremal on kirjas An empty class declaration)
5. Muuda klassi nime “Toode” peale. Kui klassi genereerides nime ei vahetanud või on vaja hiljem muuta, siis:

5.a) Veendu, et rakendus ei oleks töötav
5.b) Vajuta parem klõps “Rename” või F2 klahvile
5.c) Sisesta nimi “Toode”
Mudeli paneme koosnema neljast omadusest: id, nimi, hind, aktiivne – esimene täisarvuline, teine sõnaline, kolmas komakohaga ning neljas kahendväärtuseline väärtus.
Lisame konstruktori, mille abil saame uut toodet luua.
Lisan uude mudelisse järgnev kood:
namespace veeb.Models;
public class Toode
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public bool IsActive { get; set; }
public Toode(int id, string name, double price, bool isActive)
{
Id = id;
Name = name;
Price = price;
IsActive = isActive;
}
}
Ilus oleks panna igaühele “private” ehk kapseldada väärtused ning teha võimekus neid kätte saada läbi getteri ja nendele väärtusi anda läbi setteri, kuid jätame lihtsuse huvides hetkel nii.
Iseseisev harjutus:
- Tee kasutaja mudel, milles on: täisarvuline ID, sõnaline kasutajanimi, sõnaline parool, sõnaline eesnimi, sõnaline perenimi.
lisame kaustale(Models) uus faili nimega Kasutaja

Liisame koodi:
namespace veeb.Models;
public class Kasutaja
{
public int Id { get; set; }
public string Kasutajanimi { get; set; }
public string Parool { get; set; }
public string Eesnimi { get; set; }
public string Perenimi { get; set; }
public Kasutaja(int id, string kasutajanimi, string parool, string eesnimi, string perenimi)
{
Id = id;
Kasutajanimi = kasutajanimi;
Parool = parool;
Eesnimi = eesnimi;
Perenimi = perenimi;
}
}
API päringud mudeliga
Uue faili loomiseks vajuta kausta “Controllers” kausta peal parema klõpsuga, vali “add” ning võta “New Item…”. Pane loodud faili nimeks ToodeController.cs ning vali tühja API kontrolleri mall (API Controller – Empty).

Kui soovid faili nime muuta, pead rakenduse kinni panema ning muutma parema klõpsuga failil “Rename” või F2 klahvivajutusega nime.

Nimeks pane “ToodeController.cs” .
Faili sisu on alguses järgnev:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ToodeController : ControllerBase
{
}
}
Loo siia klassi uus toode, imiteerimaks andmebaasist kättesaadavat toodet.
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class ToodeController : ControllerBase
{
private static Toode _toode = new Toode(1, "Koola", 1.5, true);
}
}
Teeme kontrollerile API otspunkti, mis annab eerakendusele (meie mõistes brauseris kättesaadavaks) vastloodud toote. Kui eesrakendus soovib andmeid välja näidata, siis tema ülesanne on sellelt otspunktile andmeid küsida ning seejärel läbi HTMLi kasutajale näidata.
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class ToodeController : ControllerBase
{
private static Toode _toode = new Toode(1, "Koola", 1.5, true);
// GET: toode
[HttpGet]
public Toode GetToode()
{
return _toode;
}
}
}
Toode on kättesaadav otspunktil “localhost:4444/toode“

Teen uus API otspunkt, millega on võimalik hinda 1 euro võrra suurendada.
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class ToodeController : ControllerBase
{
private static Toode _toode = new Toode(1, "Koola", 1.5, true);
// GET: toode
[HttpGet]
public Toode GetToode()
{
return _toode;
}
// GET: toode/suurenda-hinda
[HttpGet("suurenda-hinda")]
public Toode SuurendaHinda()
{
_toode.Price = _toode.Price + 1;
return _toode;
}
}
}
Toote muutmine on võimalik otspunktil “localhost:4444/toode/suurenda-hinda“

Iseseisvad harjutused:
- Tee uus API otspunkt, mis käivitades muudab toote aktiivsust. Kui alguses on true, siis false peale ning kui on false, siis true peale. Kasuta hüüumärgi funktsionaalsust ehk ära küsi muutujana uut väärtust.
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class ToodeController : ControllerBase
{
private static Toode _toode = new Toode(1, "Koola", 1.5, true);
// GET: toode
[HttpGet]
public Toode GetToode()
{
return _toode;
}
// GET: toode/muuda-aktiivsus
[HttpGet("muuda-aktiivsus")]
public Toode MuudaAktiivsus()
{
_toode.IsActive = !_toode.IsActive; // toggle true <-> false
return _toode;
}
}
}
Toote muutmine on võimalik otspunktil “localhost:4444/toode/muuda-aktiivsus“

- Tee uus API otspunkt, mis käivitades muudab toote nime. Võta selleks kasutusele URL muutuja ning määra tootele.
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class ToodeController : ControllerBase
{
private static Toode _toode = new Toode(1, "Koola", 1.5, true);
// GET: toode
[HttpGet]
public Toode GetToode()
{
return _toode;
}
// GET: toode/muuda-nimi/UusNimi
[HttpGet("muuda-nimi/{uusNimi}")]
public Toode MuudaNimi(string uusNimi)
{
_toode.Name = uusNimi;
return _toode;
}
}
}
Toote muutmine on võimalik otspunktil “localhost:4444/toode/muuda-nimi/{uusNimi}“
NB! Kus on {uusNimi} tee peate kõrjutada uus nimi mis tee muutade

- Tee uus API otspunkt, mis käivitades muudab toote hinda muutujana antud numbri kordseks. Võta selleks kasutusele URL muutuja ning korruta toote hinnaga läbi.
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class ToodeController : ControllerBase
{
private static Toode _toode = new Toode(1, "Koola", 1.5, true);
// GET: toode
[HttpGet]
public Toode GetToode()
{
return _toode;
}
// GET: toode/korruta-hind/3
[HttpGet("korruta-hind/{kordaja}")]
public Toode KorrutaHind(double kordaja)
{
_toode.Price = _toode.Price * kordaja;
return _toode;
}
}
}
Toote muutmine on võimalik otspunktil “localhost:4444/toode/korruta-hind/{kordaja}”
NB! Kus on {kordaja} tee peate kirjutada kordaja number

API päringud mudeli ja listiga
Kui varasemalt tegelesime ühe mudeliga, siis võtame seekord kasutusele mudelite listi. Teeme valmis toote kontrolleri, millele paneme nimeks “TootedController”.
Uue faili loomiseks vajuta kausta “Controllers” kausta peal parema klõpsuga, vali “add” ning võta “New Item…”. Pane loodud faili nimeks ToodeController.cs ning vali tühja API kontrolleri mall (API Controller – Empty).
Kui soovid faili nime muuta, pead rakenduse kinni panema ning muutma parema klõpsuga failil “Rename” või F2 klahvivajutusega nime.


using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TootedController : ControllerBase
{
}
}
Loo siia klassi uus toodete List, imiteerimaks andmebaasist kättesaadavaid tooteid. Täida list toodetega, kasutades vabalt valitud andmeid.
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
namespace veeb.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TootedController : ControllerBase
{
private static List<Toode> _tooted = new()
{
new Toode(1,"Koola", 1.5, true),
new Toode(2,"Fanta", 1.0, false),
new Toode(3,"Sprite", 1.7, true),
new Toode(4,"Vichy", 2.0, true),
new Toode(5,"Vitamin well", 2.5, true)
};
}
}
Teeme kontrollerile API otspunkti, mis annab eesrakendusele (meie mõistes brauseris kättesaadavaks) vastloodud tooted. Kui eesrakendus soovib andmeid välja näidata, siis tema ülesanne on sellelt otspunktile andmeid küsida ning seejärel läbi HTMLi kasutajale näidata.
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
namespace veeb.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TootedController : ControllerBase
{
private static List<Toode> _tooted = new()
{
new Toode(1,"Koola", 1.5, true),
new Toode(2,"Fanta", 1.0, false),
new Toode(3,"Sprite", 1.7, true),
new Toode(4,"Vichy", 2.0, true),
new Toode(5,"Vitamin well", 2.5, true)
};
// https://localhost:4444/tooted
[HttpGet]
public List<Toode> Get()
{
return _tooted;
}
}
}
Tooted on kättesaadavad otspunktil “localhost:4444/api/tooted“

Tee valmis toote kustutamine, luues uue API otspunkti “kustuta/”, millele lisatakse järjekorranumber.
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
namespace veeb.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TootedController : ControllerBase
{
private static List<Toode> _tooted = new()
{
new Toode(1,"Koola", 1.5, true),
new Toode(2,"Fanta", 1.0, false),
new Toode(3,"Sprite", 1.7, true),
new Toode(4,"Vichy", 2.0, true),
new Toode(5,"Vitamin well", 2.5, true)
};
// https://localhost:4444/tooted
[HttpGet]
public List<Toode> Get()
{
return _tooted;
}
// https://localhost:4444/tooted/kustuta/0
[HttpGet("kustuta/{index}")]
public List<Toode> Delete(int index)
{
_tooted.RemoveAt(index);
return _tooted;
}
}
}

Õige on teha HttpDelete päring, kuid lihtsuse huvides teeme hetkel HttpGet – siis on võimalik ka brauseris päringuid käivitada.
Get, Delete, Post, Put, Patch päringud on täpsemalt käsitletud alajaotuses “Parimad praktikad”.
Toote kustutamine listist on võimalik läbi otspunkti “localhost:4444/kustuta/0”. Otspunkti brauserist või Postmanist käivitades näed allesjäänud tooteid.
Teine variant on pärast toote kustutamist tagastada eesrakendusele sõnum – kui eesrakendus selle sõnumi kätte saab, siis on kustutamine olnud edukas.



Tee uus API otspunkt, millega on võimalik ühte toodet juurde lisada. Pane tähele, et pead lisama numbri kohale numbriga väärtuse, vastasel juhul tagastatakse viga.
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
namespace veeb.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TootedController : ControllerBase
{
private static List<Toode> _tooted = new()
{
new Toode(1,"Koola", 1.5, true),
new Toode(2,"Fanta", 1.0, false),
new Toode(3,"Sprite", 1.7, true),
new Toode(4,"Vichy", 2.0, true),
new Toode(5,"Vitamin well", 2.5, true)
};
// https://localhost:4444/tooted
[HttpGet]
public List<Toode> Get()
{
return _tooted;
}
// https://localhost:4444/tooted/kustuta/0
[HttpGet("kustuta/{index}")]
public List<Toode> Delete(int index)
{
_tooted.RemoveAt(index);
return _tooted;
}
[HttpGet("kustuta2/{index}")]
public string Delete2(int index)
{
_tooted.RemoveAt(index);
return "Kustutatud!";
}
[HttpGet("lisa/{id}/{nimi}/{hind}/{aktiivne}")]
public List<Toode> Add(int id, string nimi, double hind, bool aktiivne)
{
Toode toode = new Toode(id, nimi, hind, aktiivne);
_tooted.Add(toode);
return _tooted;
}
}
}

Lisamiseks on veel mitmeid variante. Kõige populaarsem lisamise viis on tegelikult POST päringu kaudu ning rakenduse kehana (body) saates, seda aga pole brauseris võimalik käivitada.
Kolmas variant on seda teha päringu parameetritena, mis nõuab API otspunkti järel ? (küsimärgi) taga võtit ja väärtust, mis on & märgiga eraldatud.
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
namespace veeb.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TootedController : ControllerBase
{
private static List<Toode> _tooted = new()
{
new Toode(1,"Koola", 1.5, true),
new Toode(2,"Fanta", 1.0, false),
new Toode(3,"Sprite", 1.7, true),
new Toode(4,"Vichy", 2.0, true),
new Toode(5,"Vitamin well", 2.5, true)
};
// https://localhost:4444/tooted
[HttpGet]
public List<Toode> Get()
{
return _tooted;
}
// https://localhost:4444/tooted/kustuta/0
[HttpGet("kustuta/{index}")]
public List<Toode> Delete(int index)
{
_tooted.RemoveAt(index);
return _tooted;
}
[HttpGet("kustuta2/{index}")]
public string Delete2(int index)
{
_tooted.RemoveAt(index);
return "Kustutatud!";
}
[HttpGet("lisa/{id}/{nimi}/{hind}/{aktiivne}")]
public List<Toode> Add(int id, string nimi, double hind, bool aktiivne)
{
Toode toode = new Toode(id, nimi, hind, aktiivne);
_tooted.Add(toode);
return _tooted;
}
[HttpGet("lisa")] // GET /tooted/lisa?id=1&nimi=Koola&hind=1.5&aktiivne=true
public List<Toode> Add2([FromQuery] int id, [FromQuery] string nimi, [FromQuery] double hind, [FromQuery] bool aktiivne)
{
Toode toode = new Toode(id, nimi, hind, aktiivne);
_tooted.Add(toode);
return _tooted;
}
}
}
Päringut on võimalik käivitada “localhost:4444/api/tooted/lisa?id=6&nimi=Koola&hind=1.5&aktiivne=true“

Võimalda iga toote hinda muuta läbi tsükli. Selleks lisa URL muutuja kaudu soovitud kurss, mis tagastab eesrakendusele uute hindadega tooted. Rakendus taaskäivitades ilmuvad taaskord originaalsed hinnad.
- localhost:4444/tooted/hind-dollaritesse/1.5

2. localhost:4444/tooted/hind-dollaritesse2/1.5

Iseseisvad harjutused:
- Tee uus API otspunkt, mis kustutab korraga kõik tooted.
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
using System.Linq;
namespace veebirakendus.Controllers;
[ApiController]
[Route("[controller]")]
public class TootedController : ControllerBase
{
private static List<Toode> _tooted = new List<Toode>{
new Toode(1,"Koola", 1.5, true),
new Toode(2,"Fanta", 1.0, false),
new Toode(3,"Sprite", 1.7, true),
new Toode(4,"Vichy", 2.0, true),
new Toode(5,"Vitamin well", 2.5, true)
};
// https://localhost:7052/tooted
[HttpGet]
public List<Toode> Get()
{
return _tooted;
}
[HttpGet("kustuta/{index}")]
public List<Toode> Delete(int index)
{
_tooted.RemoveAt(index);
return _tooted;
}
[HttpGet("kustuta2/{index}")]
public string Delete2(int index)
{
_tooted.RemoveAt(index);
return "Kustutatud!";
}
[HttpGet("lisa/{id}/{nimi}/{hind}/{aktiivne}")]
public List<Toode> Add(int id, string nimi, double hind, bool aktiivne)
{
Toode toode = new Toode(id, nimi, hind, aktiivne);
_tooted.Add(toode);
return _tooted;
}
[HttpGet("lisa")] // GET /tooted/lisa?id=1&nimi=Koola&hind=1.5&aktiivne=true
public List<Toode> Add2([FromQuery] int id, [FromQuery] string nimi, [FromQuery] double hind, [FromQuery] bool aktiivne)
{
Toode toode = new Toode(id, nimi, hind, aktiivne);
_tooted.Add(toode);
return _tooted;
}
[HttpGet("hind-dollaritesse/{kurss}")] // GET /tooted/hind-dollaritesse/1.5
public List<Toode> Dollaritesse(double kurss)
{
for (int i = 0; i < _tooted.Count; i++)
{
_tooted[i].Price = _tooted[i].Price * kurss;
}
return _tooted;
}
// või foreachina:
[HttpGet("hind-dollaritesse2/{kurss}")] // GET /tooted/hind-dollaritesse2/1.5
public List<Toode> Dollaritesse2(double kurss)
{
foreach (var t in _tooted)
{
t.Price = t.Price * kurss;
}
return _tooted;
}
// kustuta korraga kõik tooted
// GET: /tooted/kustuta-koik
[HttpGet("kustuta-koik")]
public List<Toode> KustutaKoik()
{
_tooted.Clear();
return _tooted; // tühi list
}
}

- Tee uus API otspunkt, mis muudab kõikide toodete aktiivsuse väära peale.
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
using System.Linq;
namespace veebirakendus.Controllers;
[ApiController]
[Route("[controller]")]
public class TootedController : ControllerBase
{
private static List<Toode> _tooted = new List<Toode>{
new Toode(1,"Koola", 1.5, true),
new Toode(2,"Fanta", 1.0, false),
new Toode(3,"Sprite", 1.7, true),
new Toode(4,"Vichy", 2.0, true),
new Toode(5,"Vitamin well", 2.5, true)
};
// https://localhost:7052/tooted
[HttpGet]
public List<Toode> Get()
{
return _tooted;
}
[HttpGet("kustuta/{index}")]
public List<Toode> Delete(int index)
{
_tooted.RemoveAt(index);
return _tooted;
}
[HttpGet("kustuta2/{index}")]
public string Delete2(int index)
{
_tooted.RemoveAt(index);
return "Kustutatud!";
}
[HttpGet("lisa/{id}/{nimi}/{hind}/{aktiivne}")]
public List<Toode> Add(int id, string nimi, double hind, bool aktiivne)
{
Toode toode = new Toode(id, nimi, hind, aktiivne);
_tooted.Add(toode);
return _tooted;
}
[HttpGet("lisa")] // GET /tooted/lisa?id=1&nimi=Koola&hind=1.5&aktiivne=true
public List<Toode> Add2([FromQuery] int id, [FromQuery] string nimi, [FromQuery] double hind, [FromQuery] bool aktiivne)
{
Toode toode = new Toode(id, nimi, hind, aktiivne);
_tooted.Add(toode);
return _tooted;
}
[HttpGet("hind-dollaritesse/{kurss}")] // GET /tooted/hind-dollaritesse/1.5
public List<Toode> Dollaritesse(double kurss)
{
for (int i = 0; i < _tooted.Count; i++)
{
_tooted[i].Price = _tooted[i].Price * kurss;
}
return _tooted;
}
// või foreachina:
[HttpGet("hind-dollaritesse2/{kurss}")] // GET /tooted/hind-dollaritesse2/1.5
public List<Toode> Dollaritesse2(double kurss)
{
foreach (var t in _tooted)
{
t.Price = t.Price * kurss;
}
return _tooted;
}
// kustuta korraga kõik tooted
// GET: /tooted/kustuta-koik
[HttpGet("kustuta-koik")]
public List<Toode> KustutaKoik()
{
_tooted.Clear();
return _tooted; // tühi list
}
// kõigi aktiivsus vääraks (false)
// GET: /tooted/muuda-koik-vale
[HttpGet("muuda-koik-vale")]
public List<Toode> MuudaKoikVale()
{
foreach (var t in _tooted)
t.IsActive = false;
return _tooted;
}
}

- Tee uus API otspunkt, mis tagastab ühe toote – vastavalt kelle järjekorranumber on lisatud URL muutujasse.
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
using System.Linq;
namespace veebirakendus.Controllers;
[ApiController]
[Route("[controller]")]
public class TootedController : ControllerBase
{
private static List<Toode> _tooted = new List<Toode>{
new Toode(1,"Koola", 1.5, true),
new Toode(2,"Fanta", 1.0, false),
new Toode(3,"Sprite", 1.7, true),
new Toode(4,"Vichy", 2.0, true),
new Toode(5,"Vitamin well", 2.5, true)
};
// https://localhost:7052/tooted
[HttpGet]
public List<Toode> Get()
{
return _tooted;
}
[HttpGet("kustuta/{index}")]
public List<Toode> Delete(int index)
{
_tooted.RemoveAt(index);
return _tooted;
}
[HttpGet("kustuta2/{index}")]
public string Delete2(int index)
{
_tooted.RemoveAt(index);
return "Kustutatud!";
}
[HttpGet("lisa/{id}/{nimi}/{hind}/{aktiivne}")]
public List<Toode> Add(int id, string nimi, double hind, bool aktiivne)
{
Toode toode = new Toode(id, nimi, hind, aktiivne);
_tooted.Add(toode);
return _tooted;
}
[HttpGet("lisa")] // GET /tooted/lisa?id=1&nimi=Koola&hind=1.5&aktiivne=true
public List<Toode> Add2([FromQuery] int id, [FromQuery] string nimi, [FromQuery] double hind, [FromQuery] bool aktiivne)
{
Toode toode = new Toode(id, nimi, hind, aktiivne);
_tooted.Add(toode);
return _tooted;
}
[HttpGet("hind-dollaritesse/{kurss}")] // GET /tooted/hind-dollaritesse/1.5
public List<Toode> Dollaritesse(double kurss)
{
for (int i = 0; i < _tooted.Count; i++)
{
_tooted[i].Price = _tooted[i].Price * kurss;
}
return _tooted;
}
// või foreachina:
[HttpGet("hind-dollaritesse2/{kurss}")] // GET /tooted/hind-dollaritesse2/1.5
public List<Toode> Dollaritesse2(double kurss)
{
foreach (var t in _tooted)
{
t.Price = t.Price * kurss;
}
return _tooted;
}
// kustuta korraga kõik tooted
// GET: /tooted/kustuta-koik
[HttpGet("kustuta-koik")]
public List<Toode> KustutaKoik()
{
_tooted.Clear();
return _tooted; // tühi list
}
// kõigi aktiivsus vääraks (false)
// GET: /tooted/muuda-koik-vale
[HttpGet("muuda-koik-vale")]
public List<Toode> MuudaKoikVale()
{
foreach (var t in _tooted)
t.IsActive = false;
return _tooted;
}
// tagasta üks toode järjekorranumbri (indeksi) järgi
// GET: /tooted/yks/0
[HttpGet("yks/{index}")]
public ActionResult<Toode> YksToode(int index)
{
if (index < 0 || index >= _tooted.Count)
return NotFound($"Indeks {index} on väljas vahemikust 0..{_tooted.Count - 1}");
return _tooted[index];
}
}

- Tee uus API otspunkt, mis tagastab ühe toote – kõige suurema hinnaga toote.
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
using System.Linq;
namespace veebirakendus.Controllers;
[ApiController]
[Route("[controller]")]
public class TootedController : ControllerBase
{
private static List<Toode> _tooted = new List<Toode>{
new Toode(1,"Koola", 1.5, true),
new Toode(2,"Fanta", 1.0, false),
new Toode(3,"Sprite", 1.7, true),
new Toode(4,"Vichy", 2.0, true),
new Toode(5,"Vitamin well", 2.5, true)
};
// https://localhost:7052/tooted
[HttpGet]
public List<Toode> Get()
{
return _tooted;
}
[HttpGet("kustuta/{index}")]
public List<Toode> Delete(int index)
{
_tooted.RemoveAt(index);
return _tooted;
}
[HttpGet("kustuta2/{index}")]
public string Delete2(int index)
{
_tooted.RemoveAt(index);
return "Kustutatud!";
}
[HttpGet("lisa/{id}/{nimi}/{hind}/{aktiivne}")]
public List<Toode> Add(int id, string nimi, double hind, bool aktiivne)
{
Toode toode = new Toode(id, nimi, hind, aktiivne);
_tooted.Add(toode);
return _tooted;
}
[HttpGet("lisa")] // GET /tooted/lisa?id=1&nimi=Koola&hind=1.5&aktiivne=true
public List<Toode> Add2([FromQuery] int id, [FromQuery] string nimi, [FromQuery] double hind, [FromQuery] bool aktiivne)
{
Toode toode = new Toode(id, nimi, hind, aktiivne);
_tooted.Add(toode);
return _tooted;
}
[HttpGet("hind-dollaritesse/{kurss}")] // GET /tooted/hind-dollaritesse/1.5
public List<Toode> Dollaritesse(double kurss)
{
for (int i = 0; i < _tooted.Count; i++)
{
_tooted[i].Price = _tooted[i].Price * kurss;
}
return _tooted;
}
// või foreachina:
[HttpGet("hind-dollaritesse2/{kurss}")] // GET /tooted/hind-dollaritesse2/1.5
public List<Toode> Dollaritesse2(double kurss)
{
foreach (var t in _tooted)
{
t.Price = t.Price * kurss;
}
return _tooted;
}
// kustuta korraga kõik tooted
// GET: /tooted/kustuta-koik
[HttpGet("kustuta-koik")]
public List<Toode> KustutaKoik()
{
_tooted.Clear();
return _tooted; // tühi list
}
// kõigi aktiivsus vääraks (false)
// GET: /tooted/muuda-koik-vale
[HttpGet("muuda-koik-vale")]
public List<Toode> MuudaKoikVale()
{
foreach (var t in _tooted)
t.IsActive = false;
return _tooted;
}
// tagasta üks toode järjekorranumbri (indeksi) järgi
// GET: /tooted/yks/0
[HttpGet("yks/{index}")]
public ActionResult<Toode> YksToode(int index)
{
if (index < 0 || index >= _tooted.Count)
return NotFound($"Indeks {index} on väljas vahemikust 0..{_tooted.Count - 1}");
return _tooted[index];
}
// tagasta kõige suurema hinnaga toode
// GET: /tooted/korgeim-hind
[HttpGet("korgeim-hind")]
public ActionResult<Toode> KorgeimHind()
{
if (_tooted.Count == 0)
return NotFound("Toodete nimekiri on tühi.");
var maxToode = _tooted.OrderByDescending(t => t.Price).First();
return maxToode;
}
}

Eesliidese implementeerimine
- Lae alla ja seadista NodeJS (installimine next-next-next loogikaga)

2. Ava käsuviip, mine mööda käsurida sobivasse kausta ning loo uus Reacti rakendus käsuga ” npx create-react-app RAKENDUSE_NIMI “


3. Ava äsja loodud kaust Visual Studio Code-s (mitte Visual Studio! Visual Studio Code on eesrakenduste kirjutamiseks sobilik). Tee võiksite avada VS Code käsuviipes kui te kirjutade code .

- Käivita rakendus, minnes esmalt käsureal õigesse kausta


5. Kuna eesrakendust käsitleme minimaalselt, on eesrakenduse toodete võtmise kood ühe hooga valmis kirjutatud.
App.js faili kirjuta järgnev sisu:
import { useEffect, useRef, useState } from 'react';
import './App.css';
function App() {
const [tooted, setTooted] = useState([]);
useEffect(() => {
fetch("https://localhost:4444/tooted")
.then(res => res.json())
.then(json => setTooted(json));
}, []);
return (
<div className="App">
{tooted.map(toode =>
<div>
<div>{toode.id}</div>
<div>{toode.name}</div>
<div>{toode.price.toFixed(2)}</div>
</div>)}
</div>
);
}
export default App;

Kui tagarakendus ei ole käima pandud, on veakood brauseri konsoolis järgnev.

CORS (Cross-Origin Resource Sharing) viga ilmneb siis, kui brauser blokeerib päringu, kuna päringu sihtkoht (server) ei ole konfigureeritud lubama päringuid sellelt päritolu allikalt.
6. Lisame järgmised read Program.cs faili võõra rakenduse meie tagarakendusele ligi lubamiseks
app.UseCors(options => options
.WithOrigins("*")
.AllowAnyMethod()
.AllowAnyHeader()
);
7. Lõplik fail on järgnev
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseCors(options => options
.WithOrigins("*")
.AllowAnyMethod()
.AllowAnyHeader()
);
app.UseAuthorization();
app.MapControllers();
app.Run();
8. Tee rakendusele restart ja andmed on nüüd käes
Näed neid brauseris, aadressil localhost:3000

9. Teeme eesrakenduses kustutamise
import { useEffect, useRef, useState } from 'react';
import './App.css';
function App() {
const [tooted, setTooted] = useState([]);
useEffect(() => {
fetch("https://localhost:4444/tooted")
.then(res => res.json())
.then(json => setTooted(json));
}, []);
function kustuta(index) {
fetch("https://localhost:4444/tooted/kustuta/" + index)
.then(res => res.json())
.then(json => setTooted(json));
}
return (
<div className="App">
{tooted.map((toode, index) =>
<div>
<div>{toode.id}</div>
<div>{toode.name}</div>
<div>{toode.price.toFixed(2)}</div>
<button onClick={() => kustuta(index)}>x</button>
</div>)}
</div>
);
}
export default App;

10. Teeme eesrakenduses lisamise
import { useEffect, useRef, useState } from 'react';
import './App.css';
function App() {
const [tooted, setTooted] = useState([]);
const idRef = useRef();
const nameRef = useRef();
const priceRef = useRef();
const isActiveRef = useRef();
useEffect(() => {
fetch("https://localhost:4444/tooted")
.then(res => res.json())
.then(json => setTooted(json));
}, []);
function kustuta(index) {
fetch("https://localhost:4444/tooted/kustuta/" + index)
.then(res => res.json())
.then(json => setTooted(json));
}
function lisa() {
fetch(`https://localhost:4444/tooted/lisa/${Number(idRef.current.value)}/${nameRef.current.value}/${Number(priceRef.current.value)}/${isActiveRef.current.checked}`)
.then(res => res.json())
.then(json => setTooted(json));
}
return (
<div className="App">
<label>ID</label> <br />
<input ref={idRef} type="number" /> <br />
<label>name</label> <br />
<input ref={nameRef} type="text" /> <br />
<label>price</label> <br />
<input ref={priceRef} type="number" /> <br />
<label>isActive</label> <br />
<input ref={isActiveRef} type="checkbox" /> <br />
<button onClick={() => lisa()}>Lisa</button>
{tooted.map((toode, index) =>
<div>
<div>{toode.id}</div>
<div>{toode.name}</div>
<div>{toode.price.toFixed(2)}</div>
<button onClick={() => kustuta(index)}>x</button>
</div>)}
</div>
);
}
export default App;

11. Teeme eesrakenduses dollaritesse konverteerimise
import { useEffect, useRef, useState } from 'react';
import './App.css';
function App() {
const [tooted, setTooted] = useState([]);
const idRef = useRef();
const nameRef = useRef();
const priceRef = useRef();
const isActiveRef = useRef();
useEffect(() => {
fetch("https://localhost:4444/tooted")
.then(res => res.json())
.then(json => setTooted(json));
}, []);
function kustuta(index) {
fetch("https://localhost:4444/tooted/kustuta/" + index)
.then(res => res.json())
.then(json => setTooted(json));
}
function lisa() {
fetch(`https://localhost:4444/tooted/lisa/${Number(idRef.current.value)}/${nameRef.current.value}/${Number(priceRef.current.value)}/${isActiveRef.current.checked}`)
.then(res => res.json())
.then(json => setTooted(json));
}
function dollariteks() {
const kurss = 1.1;
fetch("https://localhost:4444/tooted/hind-dollaritesse/" + kurss)
.then(res => res.json())
.then(json => setTooted(json));
}
return (
<div className="App">
<label>ID</label> <br />
<input ref={idRef} type="number" /> <br />
<label>name</label> <br />
<input ref={nameRef} type="text" /> <br />
<label>price</label> <br />
<input ref={priceRef} type="number" /> <br />
<label>isActive</label> <br />
<input ref={isActiveRef} type="checkbox" /> <br />
<button onClick={() => lisa()}>Lisa</button>
{tooted.map((toode, index) =>
<div>
<div>{toode.id}</div>
<div>{toode.name}</div>
<div>{toode.price.toFixed(2)}</div>
<button onClick={() => kustuta(index)}>x</button>
</div>)}
<button onClick={() => dollariteks()}>Muuda dollariteks</button>
</div>
);
}
export default App;

12. Teeme eesrakenduses ka eurodesse tagasi konverteerimise
import { useEffect, useRef, useState } from 'react';
import './App.css';
function App() {
const [tooted, setTooted] = useState([]);
const idRef = useRef();
const nameRef = useRef();
const priceRef = useRef();
const isActiveRef = useRef();
const [isUsd, setUsd] = useState(false);
useEffect(() => {
fetch("https://localhost:4444/tooted")
.then(res => res.json())
.then(json => setTooted(json));
}, []);
function kustuta(index) {
fetch("https://localhost:4444/tooted/kustuta/" + index)
.then(res => res.json())
.then(json => setTooted(json));
}
function lisa() {
fetch(`https://localhost:4444/tooted/lisa/${Number(idRef.current.value)}/${nameRef.current.value}/${Number(priceRef.current.value)}/${isActiveRef.current.checked}`)
.then(res => res.json())
.then(json => setTooted(json));
}
function dollariteks() {
const kurss = 1.1;
setUsd(true);
fetch("https://localhost:4444/tooted/hind-dollaritesse/" + kurss)
.then(res => res.json())
.then(json => setTooted(json));
}
function eurodeks() {
const kurss = 0.9091;
setUsd(false);
fetch("https://localhost:4444/tooted/hind-dollaritesse/" + kurss)
.then(res => res.json())
.then(json => setTooted(json));
}
return (
<div className="App">
<label>ID</label> <br />
<input ref={idRef} type="number" /> <br />
<label>name</label> <br />
<input ref={nameRef} type="text" /> <br />
<label>price</label> <br />
<input ref={priceRef} type="number" /> <br />
<label>isActive</label> <br />
<input ref={isActiveRef} type="checkbox" /> <br />
<button onClick={() => lisa()}>Lisa</button>
{tooted.map((toode, index) =>
<div>
<div>{toode.id}</div>
<div>{toode.name}</div>
<div>{toode.price.toFixed(2)}</div>
<button onClick={() => kustuta(index)}>x</button>
</div>)}
{isUsd === false && <button onClick={() => dollariteks()}>Muuda dollariteks</button>}
{isUsd === true && <button onClick={() => eurodeks()}>Muuda eurodeks</button>}
</div>
);
}
export default App;


Parimad praktikad
Refaktoorime ehk teeme koodi ilusaks.
- CORS vaid pordile 3000
Tahame, et meie tagarakenduse koodile pääseme vaid meie oma eesrakendusega ligi. Piirame seda ainult meie pordile.
app.UseCors(options => options
.WithOrigins("http://localhost:3000")
.AllowAnyMethod()
.AllowAnyHeader()
);
Kogu fail on seejärel järgnev:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseCors(options => options
.WithOrigins("http://localhost:3000")
.AllowAnyMethod()
.AllowAnyHeader()
);
app.UseAuthorization();
app.MapControllers();
app.Run();
- Paneme GET päringute asemel lisaks PUT, POST, PATCH, DELETE
Miks seda vaja on – et API otspunktid oleksid selgelt määratletud, mis on selle API otspunkti ülesanne
Tähendused:
- GET – andmete võtmiseks
- PUT – andmete asendamiseks
- POST – andmete lisamiseks
- PATCH – ühe omaduse muutmiseks
- DELETE – kustutamiseks
using Microsoft.AspNetCore.Mvc;
using veeb.Models;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class TootedController : ControllerBase
{
private static List<Toode> _tooted = new()
{
new Toode(1,"Koola", 1.5, true),
new Toode(2,"Fanta", 1.0, false),
new Toode(3,"Sprite", 1.7, true),
new Toode(4,"Vichy", 2.0, true),
new Toode(5,"Vitamin well", 2.5, true)
};
// GET https://localhost:4444/tooted
[HttpGet]
public List<Toode> Get()
{
return _tooted;
}
// DELETE https://localhost:4444/tooted/kustuta/0
[HttpDelete("kustuta/{index}")]
public List<Toode> Delete(int index)
{
_tooted.RemoveAt(index);
return _tooted;
}
[HttpDelete("kustuta2/{index}")]
public string Delete2(int index)
{
_tooted.RemoveAt(index);
return "Kustutatud!";
}
// POST https://localhost:4444/tooted/lisa/1/Coca/1.5/true
[HttpPost("lisa/{id}/{nimi}/{hind}/{aktiivne}")]
public List<Toode> Add(int id, string nimi, double hind, bool aktiivne)
{
Toode toode = new Toode(id, nimi, hind, aktiivne);
_tooted.Add(toode);
return _tooted;
}
[HttpPost("lisa2")]
public List<Toode> Add2(int id, string nimi, double hind, bool aktiivne)
{
Toode toode = new Toode(id, nimi, hind, aktiivne);
_tooted.Add(toode);
return _tooted;
}
// PATCH https://localhost:4444/tooted/hind-dollaritesse/1.5
[HttpPatch("hind-dollaritesse/{kurss}")]
public List<Toode> UpdatePrices(double kurss)
{
for (int i = 0; i < _tooted.Count; i++)
{
_tooted[i].Price = _tooted[i].Price * kurss;
}
return _tooted;
}
// või foreachina:
[HttpGet("hind-dollaritesse2/{kurss}")] // GET /tooted/hind-dollaritesse2/1.5
public List<Toode> Dollaritesse2(double kurss)
{
foreach (var t in _tooted)
{
t.Price = t.Price * kurss;
}
return _tooted;
}
// kustuta korraga kõik tooted
// GET: /tooted/kustuta-koik
[HttpGet("kustuta-koik")]
public List<Toode> KustutaKoik()
{
_tooted.Clear();
return _tooted; // tühi list
}
// kõigi aktiivsus vääraks (false)
// GET: /tooted/muuda-koik-vale
[HttpGet("muuda-koik-vale")]
public List<Toode> MuudaKoikVale()
{
foreach (var t in _tooted)
t.IsActive = false;
return _tooted;
}
// tagasta üks toode järjekorranumbri (indeksi) järgi
// GET: /tooted/yks/0
[HttpGet("yks/{index}")]
public ActionResult<Toode> YksToode(int index)
{
if (index < 0 || index >= _tooted.Count)
return NotFound($"Indeks {index} on väljas vahemikust 0..{_tooted.Count - 1}");
return _tooted[index];
}
// tagasta kõige suurema hinnaga toode
// GET: /tooted/korgeim-hind
[HttpGet("korgeim-hind")]
public ActionResult<Toode> KorgeimHind()
{
if (_tooted.Count == 0)
return NotFound("Toodete nimekiri on tühi.");
var maxToode = _tooted.OrderByDescending(t => t.Price).First();
return maxToode;
}
}
}

Peame muutma ka eesrakenduses kõik tüübid korrektseks nagu tagarakenduses, muidu enam ühendus ei toimi
import { useEffect, useRef, useState } from 'react';
import './App.css';
function App() {
const [tooted, setTooted] = useState([]);
const idRef = useRef();
const nameRef = useRef();
const priceRef = useRef();
const isActiveRef = useRef();
useEffect(() => {
fetch("https://localhost:4444/tooted")
.then(res => res.json())
.then(json => setTooted(json));
}, []);
function kustuta(index) { ////////////////////////
fetch("https://localhost:4444/tooted/kustuta/" + index, {"method": "DELETE"})
.then(res => res.json())
.then(json => setTooted(json));
}
function lisa() { ////////////////////////
fetch(`https://localhost:4444/tooted/lisa/
${Number(idRef.current.value)}/${nameRef.current.value}/
${Number(priceRef.current.value)}/${isActiveRef.current.checked}`, {"method": "POST"})
.then(res => res.json())
.then(json => setTooted(json));
}
function dollariteks() {
const kurss = 1.1; ////////////////////////
fetch("https://localhost:4444/tooted/hind-dollaritesse/" + kurss, {"method": "PATCH"})
.then(res => res.json())
.then(json => setTooted(json));
}
return (
<div className="App">
<label>ID</label> <br />
<input ref={idRef} type="number" /> <br />
<label>Nimi</label> <br />
<input ref={nameRef} type="text" /> <br />
<label>Hind</label> <br />
<input ref={priceRef} type="number" /> <br />
<label>Aktiivne</label> <br />
<input ref={isActiveRef} type="checkbox" /> <br />
<button onClick={() => lisa()}>Lisa</button>
{tooted.map((toode, index) =>
<div>
<div>{toode.id}</div>
<div>{toode.name}</div>
<div>{toode.price}</div>
<button onClick={() => kustuta(index)}>x</button>
</div>)}
<button onClick={() => dollariteks()}>Muuda dollariteks</button>
</div>
);
}
export default App;
3. Kui võtame vastu suure hulga muutujaid läbi URLi, võiksime sellest teha andmemudeli. Kohustuslik on see siis, kui me võtamegi URLi muutujate abil ükshaaval andmemudeli vastu (praegusel juhul võtame Toote).
[HttpPost("lisa")]
public List<Toode> Add([FromBody] Toode toode)
{
_tooted.Add(toode);
return _tooted;
}
õplik kontrolleri fail on järgnev. Oleme maha võtnud ka vähem kasutatavad API otspunktid.
Peame muutma ka eesrakenduses lisamise korrektseks (tervikuna), muidu enam ühendus ei toimi
import { useEffect, useRef, useState } from 'react';
import './App.css';
function App() {
const [tooted, setTooted] = useState([]);
const idRef = useRef();
const nameRef = useRef();
const priceRef = useRef();
const isActiveRef = useRef();
useEffect(() => {
fetch("https://localhost:4444/tooted")
.then(res => res.json())
.then(json => setTooted(json));
}, []);
function kustuta(index) {
fetch("https://localhost:4444/tooted/kustuta/" + index, {"method": "DELETE"})
.then(res => res.json())
.then(json => setTooted(json));
}
////////////////////////
function lisa() {
const uusToode = {
"id": Number(idRef.current.value),
"name": nameRef.current.value,
"price": Number(priceRef.current.value),
"isActive": isActiveRef.current.checked
}
fetch("https://localhost:4444/tooted/lisa", {"method": "POST", "body": JSON.stringify(uusToode)})
.then(res => res.json())
.then(json => setTooted(json));
}
////////////////////////
function dollariteks() {
const kurss = 1.1;
fetch("https://localhost:4444/tooted/hind-dollaritesse/" + kurss, {"method": "PATCH"})
.then(res => res.json())
.then(json => setTooted(json));
}
return (
<div className="App">
<label>ID</label> <br />
<input ref={idRef} type="number" /> <br />
<label>Nimi</label> <br />
<input ref={nameRef} type="text" /> <br />
<label>Hind</label> <br />
<input ref={priceRef} type="number" /> <br />
<label>Aktiivne</label> <br />
<input ref={isActiveRef} type="checkbox" /> <br />
<button onClick={() => lisa()}>Lisa</button>
{tooted.map((toode, index) =>
<div>
<div>{toode.id}</div>
<div>{toode.name}</div>
<div>{toode.price}</div>
<button onClick={() => kustuta(index)}>x</button>
</div>)}
<button onClick={() => dollariteks()}>Muuda dollariteks</button>
</div>
);
}
export default App;
Kui oleme muutnud päringute tüübid POST, PUT, PATCH, DELETE peale, siis enam brauser päringuid teha ei võimalda. Küll on aga võimalik teha GET päringuid.
Meil on võimalik teha ülaltoodud päringuid nüüd eesrakenduses, kuid iga kord on vaja selle töötamist kontrollida läbi pika koodijupi. Lisaks tulevad mängu potentsiaalsed vead, mida võime eesrakenduses teha.
Lahendus on programm nimega Postman. See võimaldab teha kõiki päringuid ning katsetada neid võimalikult lihtsast keskkonnas.
Lae alla Postman aadressilt https://www.postman.com/ ning katseta kõiki päringuid läbi selle programmi.

Iseseisev harjutus:
- Tee tagarakendusse uus API otspunkt, mis muudab toodet. Kasuta PUT päringu tüüpi. Eesrakenduses pole midagi lisada vaja.
// PUT https://localhost:4444/tooted/5
[HttpPut("{id}")]
public ActionResult<List<Toode>> Update(int id, [FromBody] Toode body)
{
if (body == null)
return BadRequest("Request body is missing");
var existing = _tooted.FirstOrDefault(t => t.Id == id);
if (existing == null)
return NotFound($"Toode Id={id} ei leitud.");
// Обновляем поля (Id обычно не меняем)
existing.Name = body.Name;
existing.Price = body.Price;
existing.IsActive = body.IsActive;
return Ok(_tooted);
}

API päring teise rakendusse: pakiautomaatide kättesaamine
Võtame pakiautomaadid lehelt, kus need on kõigile avalikuks tehtud: https://www.omniva.ee/locations.json
(Kui URL ei tööta, näiteks Omniva lehe aadress on muutunud, on alternatiivina lisatud pakiautomaadid järgmisele URL-le: https://omnivapakiautomaadid-default-rtdb.firebaseio.com/locations.json)
- Teeme uue kontrolleri
Pane nimeks ParcelMachineController.


using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ParcelMachineController : ControllerBase
{
}
}
- Teeme API otspunkti
API otspunkt on kättesaadav “https://localhost:4444/parcelmachine“.
Meile tagastatakse sõna “PAKIAUTOMAADID”.
using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class ParcelMachineController : ControllerBase
{
[HttpGet]
public string GetParcelMachines()
{
return "PAKIAUTOMAADID";
}
}
}

- Lisame HttpClienti
Selleks, et võõrasse rakendusse päringuid teha, peame seda tegema HttpClient abil.
Lisa järgnev lõik Program.cs faili, mis võimaldab rakendusega HttpClient võimalused siduda.
builder.Services.AddHttpClient();
Lõplik Program.cs fail on järgnev:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHttpClient();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseCors(options => options
.WithOrigins("*")
.AllowAnyMethod()
.AllowAnyHeader()
);
app.UseAuthorization();
app.MapControllers();
app.Run();
- Lisa HttpClient võimekus ka kontrollerisse
Selle mõiste nimi on “Sõltuvuse süstimine” (Dependency injection). Lisame uude loodavasse kontrollerisse ka HttpClienti, seda läbi konstruktori.
Selle jaoks oli vajalik 3. samm, ilma milleta praegune lisamine ei toimiks.
HttpClient abil saame võimekuse teha päringuid internetti.
using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class ParcelMachineController : ControllerBase
{
private readonly HttpClient _httpClient;
public ParcelMachineController(HttpClient httpClient)
{
_httpClient = httpClient;
}
[HttpGet]
public string GetParcelMachines()
{
return "PAKIAUTOMAADID";
}
}
}
- Teeme päringu teise rakenduse API otspunktile
Võtame HttpClienti kasutusele muutuja _httpClient abil ja teeme Get päringu Omniva API otspunktile.
Me peame tegema seda asünkroonselt, sest rakendus vajab päringu teostamiseks aega. Ka funktsiooni päises peame rõhutama, et tegemist on asünkroonse funktsiooniga.
Tagastame muutuja responseBody sisse asünkroonselt kõik pakiautomaadid, kuid eesrakendusele tagastame sellegipoolest sõna “PAKIAUTOMAADID”.
using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class ParcelMachineController : ControllerBase
{
private readonly HttpClient _httpClient;
public ParcelMachineController(HttpClient httpClient)
{
_httpClient = httpClient;
}
[HttpGet]
public async Task<string> GetParcelMachines()
{
var response = await _httpClient.GetAsync("https://www.omniva.ee/locations.json");
var responseBody = await response.Content.ReadAsStringAsync();
return "PAKIAUTOMAADID";
}
}
}
- Tagastame eesrakendusele pakiautomaadid
Tagastame kogu saadud sisu eesrakendusele. Ütleme, et tegemist on JSON kujuga “application/json” kirje abil.
IActionResult tagastuses tähendab HttpClient päringu vastust.
using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class ParcelMachineController : ControllerBase
{
private readonly HttpClient _httpClient;
public ParcelMachineController(HttpClient httpClient)
{
_httpClient = httpClient;
}
[HttpGet]
public async Task<IActionResult> GetParcelMachines()
{
var response = await _httpClient.GetAsync("https://www.omniva.ee/locations.json");
var responseBody = await response.Content.ReadAsStringAsync();
return Content(responseBody, "application/json");
}
}
}
- Mine brauserisse ja kontrolli, kas kõik pakiautomaadid on tagaprogrammis kättesaadavad
Pakiautomaadid on kättesaadavad: https://localhost:4444/parcelmachine

8. Näita pakiautomaate Reactis
/*1. osa
import { useEffect, useRef, useState } from 'react';
import './App.css';
function App() {
const [tooted, setTooted] = useState([]);
const idRef = useRef();
const nameRef = useRef();
const priceRef = useRef();
const isActiveRef = useRef();
useEffect(() => {
fetch("https://localhost:4444/tooted")
.then(res => res.json())
.then(json => setTooted(json));
}, []);
function kustuta(index) {
fetch("https://localhost:4444/tooted/kustuta/" + index, {"method": "DELETE"})
.then(res => res.json())
.then(json => setTooted(json));
}
////////////////////////
function lisa() {
const uusToode = {
"id": Number(idRef.current.value),
"name": nameRef.current.value,
"price": Number(priceRef.current.value),
"isActive": isActiveRef.current.checked
}
fetch("https://localhost:4444/tooted/lisa", {"method": "POST", "body": JSON.stringify(uusToode)})
.then(res => res.json())
.then(json => setTooted(json));
}
////////////////////////
function dollariteks() {
const kurss = 1.1;
fetch("https://localhost:4444/tooted/hind-dollaritesse/" + kurss, {"method": "PATCH"})
.then(res => res.json())
.then(json => setTooted(json));
}
return (
<div className="App">
<label>ID</label> <br />
<input ref={idRef} type="number" /> <br />
<label>Nimi</label> <br />
<input ref={nameRef} type="text" /> <br />
<label>Hind</label> <br />
<input ref={priceRef} type="number" /> <br />
<label>Aktiivne</label> <br />
<input ref={isActiveRef} type="checkbox" /> <br />
<button onClick={() => lisa()}>Lisa</button>
{tooted.map((toode, index) =>
<div>
<div>{toode.id}</div>
<div>{toode.name}</div>
<div>{toode.price}</div>
<button onClick={() => kustuta(index)}>x</button>
</div>)}
<button onClick={() => dollariteks()}>Muuda dollariteks</button>
</div>
);
}
export default App;*/
import { useEffect, useState } from 'react';
import './App.css';
function App() {
const [pakiautomaadid, setPakiautomaadid] = useState([]);
useEffect(() => {
fetch("https://localhost:4444/parcelmachine")
.then(res => res.json())
.then(json => setPakiautomaadid(json));
}, []);
return (
<div className="App">
<select>
{pakiautomaadid.map(automaat =>
<option>
{automaat.NAME}
</option>)}
</select>
</div>
);
}
export default App;

Iseseisev harjutus:
- Võta kõik pakiautomaadid eraldi API otspunktis ka SmartPost lehelt: https://www.smartpost.ee/places.json
Smartposti json fail ei töötab selle pärast ma ei saan seda teha
API päring teise rakendusse: Elering hindade kättesaamine
Võtame Elering lehelt NordPool elektrihinnad, kus need on kõigile avalikuks tehtud: https://dashboard.elering.ee/api/nps/price
Kõiki Elering päringuid on võimalik näha veebilehel https://dashboard.elering.ee/assets/api-doc.html
- Teeme uue kontrolleri
Controllers kaustale parem klõps -> Add -> New Item -> API Controller – Empty
Nimeks paneme NordpoolController


using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class NordpoolController : ControllerBase
{
}
}
- Lisame HttpClient klassi kontrollerile
Sellega lisame võimekuse teha teise rakendusse päringuid
using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class NordpoolController : ControllerBase
{
private readonly HttpClient _httpClient;
public NordpoolController(HttpClient httpClient)
{
_httpClient = httpClient;
}
}
}
- Võtame kõik hinnad API otspunktilt
Päringuga võtame järgmise 24h hinnad
using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class NordpoolController : ControllerBase
{
private readonly HttpClient _httpClient;
public NordpoolController(HttpClient httpClient)
{
_httpClient = httpClient;
}
[HttpGet]
public async Task<IActionResult> GetNordpoolPrices()
{
var response = await _httpClient.GetAsync("https://dashboard.elering.ee/api/nps/price");
var responseBody = await response.Content.ReadAsStringAsync();
return Content(responseBody, "application/json");
}
}
}
4. Mine brauserisse ja kontrolli, kas kõik Nordpooli hinnad on tagaprogrammis kättesaadavad
https://localhost:4444/nordpool

Näita NordPooli hindu Reactis
import { useEffect, useState } from 'react';
import './App.css';
function App() {
const [fi, setFi] = useState([]);
const [ee, setEe] = useState([]);
const [lv, setLv] = useState([]);
const [lt, setLt] = useState([]);
useEffect(() => {
fetch("https://localhost:4444/nordpool")
.then(res => res.json())
.then(json => {
setFi(json.data.fi);
setEe(json.data.ee)
setLv(json.data.lv)
setLt(json.data.lt)
});
}, []);
return (
<div>
<table style={{marginLeft: "100px"}}>
<thead>
<th style={{border: "1px solid #ddd", padding: "12px", backgroundColor: "#04AA6D"}}>Ajatempel</th>
<th style={{border: "1px solid #ddd", padding: "12px", backgroundColor: "#04AA6D"}}>Hind</th>
</thead>
<tbody>
<div style={{position: "absolute", left: "30px"}}>Soome</div>
{fi.map(data =>
<tr key={data.timestamp}>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{new Date(data.timestamp * 1000).toISOString()}</td>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{data.price}</td>
</tr>)}
<div style={{position: "absolute", left: "30px"}}>Eesti</div>
{ee.map(data =>
<tr key={data.timestamp}>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{new Date(data.timestamp * 1000).toISOString()}</td>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{data.price}</td>
</tr>)}
<div style={{position: "absolute", left: "30px"}}>Läti</div>
{lv.map(data =>
<tr key={data.timestamp}>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{new Date(data.timestamp * 1000).toISOString()}</td>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{data.price}</td>
</tr>)}
<div style={{position: "absolute", left: "30px"}}>Leedu</div>
{lt.map(data =>
<tr key={data.timestamp}>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{new Date(data.timestamp * 1000).toISOString()}</td>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{data.price}</td>
</tr>)}
</tbody>
</table>
</div>
);
}
export default App;

API päring teise rakendusse: Elering päringu modifitseerimine
Jätkame peatükis 11 tehtut.
- Eesrakendus soovib API otspunkti konfigureerida selliseks, et saadetakse kaasa “ee”, “lv”, “lt” või “fi” parameeter ning näidatakse selle riigi hindasid. Teeme.
Eesrakendus on järgnev:
import { useEffect, useState } from 'react';
import './App.css';
function App() {
const [prices, setPrices] = useState([]);
const [chosenCountry, setChosenCountry] = useState("ee");
useEffect(() => {
fetch("https://localhost:4444/nordpool/" + chosenCountry)
.then(res => res.json())
.then(json => {
setPrices(json);
});
}, [chosenCountry]);
return (
<div>
<button onClick={() => setChosenCountry("fi")}>Soome</button>
<button onClick={() => setChosenCountry("ee")}>Eesti</button>
<button onClick={() => setChosenCountry("lv")}>Läti</button>
<button onClick={() => setChosenCountry("lt")}>Leedu</button>
<table style={{marginLeft: "100px"}}>
<thead>
<th style={{border: "1px solid #ddd", padding: "12px", backgroundColor: "#04AA6D"}}>Ajatempel</th>
<th style={{border: "1px solid #ddd", padding: "12px", backgroundColor: "#04AA6D"}}>Hind</th>
</thead>
<tbody>
<div style={{position: "absolute", left: "30px"}}>{chosenCountry}</div>
{prices.map(data =>
<tr key={data.timestamp}>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{new Date(data.timestamp * 1000).toISOString()}</td>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{data.price}</td>
</tr>)}
</tbody>
</table>
</div>
);
}
export default App;
Seega tagarakenduse peame tegema selliseks, mis parameetri kaasa võtaks:
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class NordpoolController : ControllerBase
{
private readonly HttpClient _httpClient;
public NordpoolController(HttpClient httpClient)
{
_httpClient = httpClient;
}
[HttpGet("{country}")]
public async Task<IActionResult> GetNordPoolPrices(string country)
{
var response = await _httpClient.GetAsync("https://dashboard.elering.ee/api/nps/price");
var responseBody = await response.Content.ReadAsStringAsync();
var jsonDoc = JsonDocument.Parse(responseBody);
var dataProperty = jsonDoc.RootElement.GetProperty("data");
if (country == "ee")
{
var prices = dataProperty.GetProperty("ee").ToString();
return Content(prices, "application/json");
}
else if (country == "lv")
{
var prices = dataProperty.GetProperty("lv").ToString();
return Content(prices, "application/json");
}
else if (country == "lt")
{
var prices = dataProperty.GetProperty("lt").ToString();
return Content(prices, "application/json");
}
else if (country == "fi")
{
var prices = dataProperty.GetProperty("fi").ToString();
return Content(prices, "application/json");
}
else
{
return BadRequest("Invalid country code.");
}
}
}
}
Või kasutades if/else asemel switch/case kuju:
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class NordpoolController : ControllerBase
{
private readonly HttpClient _httpClient;
public NordpoolController(HttpClient httpClient)
{
_httpClient = httpClient;
}
[HttpGet("{country}")]
public async Task<IActionResult> GetNordPoolPrices(string country)
{
var response = await _httpClient.GetAsync("https://dashboard.elering.ee/api/nps/price");
var responseBody = await response.Content.ReadAsStringAsync();
var jsonDoc = JsonDocument.Parse(responseBody);
var dataProperty = jsonDoc.RootElement.GetProperty("data");
string prices;
switch (country)
{
case "ee":
prices = dataProperty.GetProperty("ee").ToString();
return Content(prices, "application/json");
case "lv":
prices = dataProperty.GetProperty("lv").ToString();
return Content(prices, "application/json");
case "lt":
prices = dataProperty.GetProperty("lt").ToString();
return Content(prices, "application/json");
case "fi":
prices = dataProperty.GetProperty("fi").ToString();
return Content(prices, "application/json");
default:
return BadRequest("Invalid country code.");
}
}
}
}
Katseta: https://localhost:4444/nordpool/lv

Pane tähele, et suurte tähtedega ei tööta: https://localhost:4444/nordpool/FI

2. Eesrakendus näitab kuupäevasid peale vajutades näidatakse selle kuupäeva hindasid. Meie ülesanne on eesrakendusel seda võimaldada, võttes eesrakendusest vastu kuupäeva ning saate selle Elering API otspunktile.
App.js:
/*1. osa
import { useEffect, useRef, useState } from 'react';
import './App.css';
function App() {
const [tooted, setTooted] = useState([]);
const idRef = useRef();
const nameRef = useRef();
const priceRef = useRef();
const isActiveRef = useRef();
useEffect(() => {
fetch("https://localhost:4444/tooted")
.then(res => res.json())
.then(json => setTooted(json));
}, []);
function kustuta(index) {
fetch("https://localhost:4444/tooted/kustuta/" + index, {"method": "DELETE"})
.then(res => res.json())
.then(json => setTooted(json));
}
////////////////////////
function lisa() {
const uusToode = {
"id": Number(idRef.current.value),
"name": nameRef.current.value,
"price": Number(priceRef.current.value),
"isActive": isActiveRef.current.checked
}
fetch("https://localhost:4444/tooted/lisa", {"method": "POST", "body": JSON.stringify(uusToode)})
.then(res => res.json())
.then(json => setTooted(json));
}
////////////////////////
function dollariteks() {
const kurss = 1.1;
fetch("https://localhost:4444/tooted/hind-dollaritesse/" + kurss, {"method": "PATCH"})
.then(res => res.json())
.then(json => setTooted(json));
}
return (
<div className="App">
<label>ID</label> <br />
<input ref={idRef} type="number" /> <br />
<label>Nimi</label> <br />
<input ref={nameRef} type="text" /> <br />
<label>Hind</label> <br />
<input ref={priceRef} type="number" /> <br />
<label>Aktiivne</label> <br />
<input ref={isActiveRef} type="checkbox" /> <br />
<button onClick={() => lisa()}>Lisa</button>
{tooted.map((toode, index) =>
<div>
<div>{toode.id}</div>
<div>{toode.name}</div>
<div>{toode.price}</div>
<button onClick={() => kustuta(index)}>x</button>
</div>)}
<button onClick={() => dollariteks()}>Muuda dollariteks</button>
</div>
);
}
export default App;*/
/* 2. osa
import { useEffect, useState } from 'react';
import './App.css';
function App() {
const [pakiautomaadid, setPakiautomaadid] = useState([]);
useEffect(() => {
fetch("https://localhost:4444/parcelmachine")
.then(res => res.json())
.then(json => setPakiautomaadid(json));
}, []);
return (
<div className="App">
<select>
{pakiautomaadid.map(automaat =>
<option>
{automaat.NAME}
</option>)}
</select>
</div>
);
}
export default App; */
/*3. osa
import { useEffect, useState } from 'react';
import './App.css';
function App() {
const [fi, setFi] = useState([]);
const [ee, setEe] = useState([]);
const [lv, setLv] = useState([]);
const [lt, setLt] = useState([]);
useEffect(() => {
fetch("https://localhost:4444/nordpool")
.then(res => res.json())
.then(json => {
setFi(json.data.fi);
setEe(json.data.ee)
setLv(json.data.lv)
setLt(json.data.lt)
});
}, []);
return (
<div>
<table style={{marginLeft: "100px"}}>
<thead>
<th style={{border: "1px solid #ddd", padding: "12px", backgroundColor: "#04AA6D"}}>Ajatempel</th>
<th style={{border: "1px solid #ddd", padding: "12px", backgroundColor: "#04AA6D"}}>Hind</th>
</thead>
<tbody>
<div style={{position: "absolute", left: "30px"}}>Soome</div>
{fi.map(data =>
<tr key={data.timestamp}>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{new Date(data.timestamp * 1000).toISOString()}</td>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{data.price}</td>
</tr>)}
<div style={{position: "absolute", left: "30px"}}>Eesti</div>
{ee.map(data =>
<tr key={data.timestamp}>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{new Date(data.timestamp * 1000).toISOString()}</td>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{data.price}</td>
</tr>)}
<div style={{position: "absolute", left: "30px"}}>Läti</div>
{lv.map(data =>
<tr key={data.timestamp}>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{new Date(data.timestamp * 1000).toISOString()}</td>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{data.price}</td>
</tr>)}
<div style={{position: "absolute", left: "30px"}}>Leedu</div>
{lt.map(data =>
<tr key={data.timestamp}>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{new Date(data.timestamp * 1000).toISOString()}</td>
<td style={{border: "1px solid #ddd", padding: "8px"}}>{data.price}</td>
</tr>)}
</tbody>
</table>
</div>
);
}
export default App; */
import { useEffect, useRef, useState } from "react";
function App() {
const [prices, setPrices] = useState([]);
const [error, setError] = useState("");
const [chosenCountry, setChosenCountry] = useState("ee");
const [start, setStart] = useState("");
const [end, setEnd] = useState("");
const startRef = useRef(null);
const endRef = useRef(null);
useEffect(() => {
if (start && end) {
setError("");
fetch(`https://localhost:4444/nordpool/${chosenCountry}/${start}/${end}`)
.then(async (res) => {
const text = await res.text();
if (!res.ok) {
throw new Error(text || `HTTP ${res.status}`);
}
return JSON.parse(text);
})
.then(setPrices)
.catch((e) => setError(e.message));
}
}, [chosenCountry, start, end]);
function updateStart() {
const v = startRef.current?.value;
if (v) setStart(new Date(v).toISOString());
}
function updateEnd() {
const v = endRef.current?.value;
if (v) setEnd(new Date(v).toISOString());
}
return (
<div>
<div style={{ marginBottom: 8 }}>
<button onClick={() => setChosenCountry("fi")}>Soome</button>
<button onClick={() => setChosenCountry("ee")}>Eesti</button>
<button onClick={() => setChosenCountry("lv")}>Läti</button>
<button onClick={() => setChosenCountry("lt")}>Leedu</button>
</div>
<input ref={startRef} onChange={updateStart} type="datetime-local" />
<input ref={endRef} onChange={updateEnd} type="datetime-local" />
{error && <p style={{ color: "crimson" }}>Viga: {error}</p>}
{prices.length > 0 && (
<table style={{ marginLeft: 100, borderCollapse: "collapse" }}>
<caption style={{ textAlign: "left", padding: 4 }}>
{chosenCountry.toUpperCase()}
</caption>
<thead>
<tr>
<th style={{ border: "1px solid #ddd", padding: 12, background: "#04AA6D" }}>Ajatempel</th>
<th style={{ border: "1px solid #ddd", padding: 12, background: "#04AA6D" }}>Hind</th>
</tr>
</thead>
<tbody>
{prices.map((row) => (
<tr key={row.timestamp}>
<td style={{ border: "1px solid #ddd", padding: 8 }}>
{new Date(row.timestamp * 1000).toISOString()}
</td>
<td style={{ border: "1px solid #ddd", padding: 8 }}>{row.price}</td>
</tr>
))}
</tbody>
</table>
)}
</div>
);
}
export default App;
NordpoolController.cs:
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
using System.Linq;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class NordpoolController : ControllerBase
{
private readonly HttpClient _httpClient;
public NordpoolController(HttpClient httpClient)
{
_httpClient = httpClient;
}
[HttpGet("{country}/{start}/{end}")]
public async Task<IActionResult> GetNordPoolPrices(string country, string start, string end)
{
// на всякий случай экранируем параметры
var url = $"https://dashboard.elering.ee/api/nps/price?start={Uri.EscapeDataString(start)}&end={Uri.EscapeDataString(end)}";
var response = await _httpClient.GetAsync(url);
var responseBody = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
return StatusCode((int)response.StatusCode, new
{
message = "Upstream error from Elering",
url,
body = responseBody
});
}
using var jsonDoc = JsonDocument.Parse(responseBody);
var root = jsonDoc.RootElement;
if (!root.TryGetProperty("data", out var dataEl) || dataEl.ValueKind != JsonValueKind.Object)
{
return BadRequest(new
{
message = "Elering response doesn't contain expected 'data' object.",
url,
raw = root.ToString()
});
}
var key = country.ToLowerInvariant();
if (!dataEl.TryGetProperty(key, out var countryArray) || countryArray.ValueKind != JsonValueKind.Array)
{
var available = string.Join(",", dataEl.EnumerateObject().Select(p => p.Name));
return NotFound(new
{
message = $"Country '{key}' not found in Elering response.",
availableKeys = available
});
}
// отдаем ровно массив по стране
return Content(countryArray.GetRawText(), "application/json");
}
// Доп. удобный эндпоинт на всякий: текущая цена по стране (EE/LV/LT/FI)
[HttpGet("{country}/current")]
public async Task<IActionResult> GetCurrent(string country)
{
var url = $"https://dashboard.elering.ee/api/nps/price/{country.ToUpperInvariant()}/current";
var response = await _httpClient.GetAsync(url);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
return StatusCode((int)response.StatusCode, new { message = "Upstream error from Elering", url, body });
using var doc = JsonDocument.Parse(body);
if (!doc.RootElement.TryGetProperty("data", out var dataEl))
return BadRequest(new { message = "No 'data' in Elering response.", raw = doc.RootElement.ToString() });
return Content(dataEl.GetRawText(), "application/json");
}
}
}

API päring teise rakendusse: Makse
Liisame uus kontrooler nimega PaymentController:


using Microsoft.AspNetCore.Mvc;
namespace veeb.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class PaymentController : ControllerBase
{
}
}
Makse teostamiseks on vajalik järgmine kood:
using Microsoft.AspNetCore.Mvc;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Text;
namespace veeb.Controllers
{
[Route("[controller]")]
[ApiController]
public class PaymentController : ControllerBase
{
private readonly HttpClient _httpClient;
public PaymentController(HttpClient httpClient)
{
_httpClient = httpClient;
}
[HttpGet("{sum}")]
public async Task<IActionResult> MakePayment(string sum)
{
var paymentData = new
{
api_username = "e36eb40f5ec87fa2",
account_name = "EUR3D1",
amount = sum,
order_reference = Math.Ceiling(new Random().NextDouble() * 999999),
nonce = $"a9b7f7e7as{DateTime.Now}{new Random().NextDouble() * 999999}",
timestamp = DateTime.Now,
customer_url = "https://maksmine.web.app/makse"
};
var json = JsonSerializer.Serialize(paymentData);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", "ZTM2ZWI0MGY1ZWM4N2ZhMjo3YjkxYTNiOWUxYjc0NTI0YzJlOWZjMjgyZjhhYzhjZA==");
var response = await client.PostAsync("https://igw-demo.every-pay.com/api/v4/payments/oneoff", content);
if (response.IsSuccessStatusCode)
{
var responseContent = await response.Content.ReadAsStringAsync();
var jsonDoc = JsonDocument.Parse(responseContent);
var paymentLink = jsonDoc.RootElement.GetProperty("payment_link");
return Ok(paymentLink);
}
else
{
return BadRequest("Payment failed.");
}
}
}
}
API otspunkt on kättesaadav “https://localhost:4444/Payment/1.5“

Saame näiteks sellis URL: “https://payment.sandbox.lhv.ee/lp/83xicp5kpf/8Qpnoe2Eqe“
Läheme selle URL siise ja saame:

Võtame mis tahes panga, ma võttan Swedbank:






