API loomine – tagarakendus / ASP.net Core web App’iga
Mis on tagarakendus?
Backendi ehk serveripoolne osa rakenduses vastutab andmete töötlemise, rakendusloogika ja andmebaasiga suhtlemise eest. Kui kasutaja suhtleb veebirakendusega (näiteks sisestab andmeid või vajutab nuppe), saadab frontend päringud serverisse, kus backend töötleb need päringud ja tagastab vajalikud andmed.
Backendi põhifunktsioonid:
- Päringute töötlemine: Backend saab kliendilt (frontendilt) päringuid, töötleb need ja saadab vastused tagasi.
- Andmete haldamine: Backend suhtleb andmebaasidega, et luua, lugeda, uuendada ja kustutada andmeid (CRUD operatsioonid).
- Äriloogika: Kogu rakenduse peamine loogika (näiteks arvutused, andmete valideerimine, autoriseerimine) on realiseeritud backendis.
- Turvalisus: Backend tagab andmete turvalisuse, kasutajate autentimise ja autoriseerimise ning kaitseb erinevate ohtude, nagu SQL-süstide ja XSS-rünnakute eest.
- API (rakendusliides): Backend pakub tavaliselt API-d, mille kaudu saavad frontend ja teised rakendused serveriga suhelda.
Backendi arendustehnoloogiad:
Backendi arendamiseks kasutatakse erinevaid programmeerimiskeeli, nagu JavaScript (Node.js), Python (Django, Flask), PHP, Java (Spring), Ruby (Ruby on Rails) ja teised.
Mis on ASP.net Core web App?
ASP.NET Core Web App on Microsofti välja töötatud raamistik veebirakenduste loomiseks. See on avatud lähtekoodiga ja platvormiülene raamistik, mis tähendab, et seda saab kasutada mitmesugustel operatsioonisüsteemidel, nagu Windows, macOS ja Linux. ASP.NET Core’i eesmärk on pakkuda kiiret, skaleeritavat ja paindlikku lahendust tänapäevaste veebirakenduste ja API-de arendamiseks.
ASP.NET Core Web App põhijooned:
- Platvormiülesus: ASP.NET Core’i rakendusi saab käitada mitmes keskkonnas (Windows, macOS, Linux), mis võimaldab paindlikkust ja laiemat juurdepääsetavust.
- Suure jõudlusega: ASP.NET Core on optimeeritud jõudluse jaoks ja sobib hästi suure koormusega ja skaleeritavate rakenduste loomiseks.
- Modulaarne ülesehitus: ASP.NET Core on modulaarne, mis tähendab, et arendajad saavad lisada ainult vajalikke komponente ja sõltuvusi, mis muudab rakenduse kergemaks ja lihtsamaks hallata.
- Sisseehitatud sõltuvuste süstimine (Dependency Injection): ASP.NET Core toetab sõltuvuste süstimist, mis muudab koodi paremini hallatavaks ja testitavaks.
- Razor Pages ja MVC (Model-View-Controller): ASP.NET Core toetab Razor Pages’i ja MVC mustreid, mis pakuvad erinevaid lähenemisi veebirakenduste arendamiseks ja muudavad koodi paremini struktureerituks.
- API-de loomine: ASP.NET Core sobib hästi ka RESTful API-de loomiseks, mida saab kasutada nii mobiili- kui ka veebirakendustes andmete haldamiseks.
Kasutusalad:
ASP.NET Core Web App sobib eriti hästi:
- Dünaamiliste veebisaitide loomiseks
- RESTful API-de jaoks
- Mitmekülgsete ärirakenduste arendamiseks
- Pilverakenduste jaoks, mis töötavad näiteks Azure’is või AWS-is
ASP.NET Core on tänapäevase veebiarenduse võimas tööriist, mis ühendab kiiruse, skaleeritavuse ja platvormiüleste lahenduste eelised, tehes selle arendajate seas populaarseks valikuks.
Appi loomine

Appi käivitamine


App kasutab Swaggeri’t et käivitada päringuid
Mis on Swagger?
Swagger on tööriistade ja spetsifikatsioonide kogum, mis aitab arendajatel dokumenteerida, disainida, arendada ja testida RESTful API-sid. See muudab API-de loomise ja integreerimise lihtsamaks, pakkudes selget ja struktureeritud dokumentatsiooni, mida nii arendajad kui ka API kasutajad saavad hõlpsalt mõista ja kasutada.
Swaggeri põhifunktsioonid:
- API dokumentatsioon: Swagger võimaldab automaatselt genereerida API-de dokumentatsiooni, pakkudes kasutajatele ja arendajatele selge ülevaate sellest, kuidas API töötab ja milliseid lõpp-punkte, parameetreid ja andmetüüpe see toetab.
- Swagger UI: See on visuaalne tööriist, mis kuvab API dokumentatsiooni veebilehel, võimaldades kasutajatel API lõpp-punkte interaktiivselt uurida ja testida. See aitab arendajatel ja klientidel hõlpsalt API-d katsetada otse veebilehelt, sisestades päringuid ja vaadates vastuseid reaalajas.
- OpenAPI spetsifikatsioon: Swagger põhineb OpenAPI spetsifikatsioonil (endine Swagger Specification), mis on avatud formaat RESTful API-de kirjeldamiseks JSON või YAML formaadis. See on tööstusstandard, mis muudab API-d kergesti loetavaks ja dokumenteerituks, võimaldades ühtlustatud struktuuri.
- Automatiseerimine: Paljud raamistikud (näiteks ASP.NET Core ja Spring Boot) võimaldavad automaatselt luua Swaggeri dokumentatsiooni otse koodist, mis vähendab käsitsi kirjutamise vajadust ja hoiab dokumentatsiooni alati ajakohasena.
- Koodi genereerimine: Swaggeri abil saab genereerida API kliendi koodi erinevatesse programmeerimiskeeltesse, mis kiirendab API integratsiooni erinevates rakendustes ja platvormidel.
Miks kasutada Swaggerit?
- Selge dokumentatsioon: API-dokumentatsioon on alati kättesaadav ja värske, mis aitab arendajatel ja klientidel API-d kiiresti mõista ja kasutada.
- Lihtne testimine: Swagger UI võimaldab lõpp-punkte ja päringuid otse veebibrauseris testida.
- Koostöö lihtsustamine: Selge ja struktureeritud dokumentatsioon muudab API-de kasutamise ja arendamise lihtsamaks erinevatele meeskonnaliikmetele ja partneritele.
Swagger on seega väärtuslik tööriist igale meeskonnale, kes töötab RESTful API-dega, pakkudes nii dokumentatsiooni, testimisvõimalusi kui ka arendusprotsessi lihtsustamist.
Alustame
PrimitivesController
Päring mis kuvab tervituse
using Microsoft.AspNetCore.Mvc;
namespace DotskinWebApi.Controllers
{
[Route("[controller]")]
[ApiController]
public class PrimitivesController : ControllerBase
{
// GET: primitives/hello-world
[HttpGet("hello-world")]
public string HelloWorld()
{
return "Tere päevast, praegu on " + DateTime.Now;
}
}
}
Tulemus:

Päring mis kuvab tervituse nimega
// GET: primitives/hello-variable/mari
[HttpGet("hello-variable/{name}")]
public string HelloVariable(string name)
{
return "Tere " + name;
}
Tulemus:

Päring mis lisab kaks numbrit
// GET: primitives/add/5/6
[HttpGet("add/{nr1}/{nr2}")]
public int AddNumbers(int nr1, int nr2)
{
return nr1 + nr2;
}
Tulemus:

Päring mis korrutab kaks numbrit
// GET: primitiivid/multiply/5/6
[HttpGet("multiply/{nr1}/{nr2}")]
public int Multiply(int nr1, int nr2)
{
return nr1 * nr2;
}
Tulemus:

Päring mis loeb taotluste arvu
// GET: primitives/do-logs/5
[HttpGet("do-logs/{arv}")]
public void DoLogs(int amount)
{
for (int i = 0; i < amount; i++)
{
Console.WriteLine("See on logi nr " + i);
}
}
Tulemus:

Harjutused
Tee juhuslike numbrite generaator (või Random rand = new Random() abil) – sisestades otspunktile kaks arvu, tagastab programm juhusliku arvu nende vahel
// GET: primitives/rand-num/5/10
[HttpGet("rand-num/{nr1}/{nr2}")]
public int RandNum(int nr1, int nr2)
{
Random random= new Random();
return random.Next(nr1, nr2);
}
Tulemus:

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”
// GET: primitives/birthday/2006/01
[HttpGet("birthday/{year}/{month}")]
public string Birthday(int year, int month)
{
int years = (month < DateTime.Now.Month) ? DateTime.Now.Year - year : DateTime.Now.Year - year - 1;
return "Olete " + years.ToString() + " aastat vana";
}
Tulemus:


Mudelid
Product
namespace DotskinWebApi.Models
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public bool IsActive { get; set; }
public Product(int id, string name, double price, bool isActive)
{
Id = id;
Name = name;
Price = price;
IsActive = isActive;
}
}
}
User
namespace DotskinWebApi.Models
{
public class User
{
public int Id { get; set; }
public string UserName { get; set;}
public string Password { get; set;}
public string FirstName { get; set;}
public string LastName { get; set; }
public User(int id, string userName, string password, string firstName, string lastName) {
Id = id;
UserName = userName;
Password = password;
FirstName = firstName;
LastName = lastName;
}
}
}
ProductController
Päring mis tagastab toode
using DotskinWebApi.Models;
using Microsoft.AspNetCore.Mvc;
namespace DotskinWebApi.Controllers
{
[Route("[controller]")]
[ApiController]
public class ProductController : ControllerBase
{
private static Product _product = new Product(1, "Koola", 1.5, true);
// GET: product
[HttpGet]
public Product GetProduct()
{
return _product;
}
}
}
Tulemus:

Päring mis tõustab toote hinda
// GET: product/increase-price
[HttpGet("increase-price")]
public Product IncreasePrice()
{
_product.Price = _product.Price + 1;
return _product;
}
Tulemus:

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
// GET: product/change-activity
[HttpGet("change-activity")]
public Product ChangeActivity()
{
_product.IsActive = !_product.IsActive;
return _product;
}
Tulemus:

Tee uus API otspunkt, mis käivitades muudab toote nime. Võta selleks kasutusele URL muutuja ning määra tootele
// GET: product/change-name/testname
[HttpGet("change-name/{name}")]
public Product ChangeName(string name)
{
_product.Name = name;
return _product;
}
Tulemus:

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
// GET: product/multiply-price/3
[HttpGet("multiply-price/{num}")]
public Product MultiplyPrice(int num)
{
_product.Price = _product.Price*num;
return _product;
}
Tulemus:

ProductsController
Toodete loendi tagastamise päring
using DotskinWebApi.Models;
using Microsoft.AspNetCore.Mvc;
namespace DotskinWebApi.Controllers
{
[Route("[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
private static List<Product> _products = new()
{
new Product(1,"Koola", 1.5, true),
new Product(2,"Fanta", 1.0, false),
new Product(3,"Sprite", 1.7, true),
new Product(4,"Vichy", 2.0, true),
new Product(5,"Vitamin well", 2.5, true)
};
// GET: products
[HttpGet]
public List<Product> Get()
{
return _products;
}
}
}
Tulemus:

Taotlege toote kustutamist ID-ga
// GET: products/delete/0
[HttpGet("delete/{index}")]
public List<Product> Delete(int index)
{
_products.RemoveAt(index-1);
return _products;
}
Tulemus:

Taotlege toote kustutamist ID-ga ja tagastab tulemust
// GET: products/delete2/0
[HttpGet("delete2/{index}")]
public string Delete2(int index)
{
_products.RemoveAt(index-1);
return "Kustutatud!";
}

Taotlus, mis loob sisestatud andmete põhjal uue toote
// GET: products/add/6/testtoode/5.0/true
[HttpGet("add/{id}/{name}/{price}/{isActive}")]
public List<Product> Add(int id, string name, double price, bool isActive)
{
Product product = new Product(id, name, price, isActive);
_products.Add(product);
return _products;
}
Tulemus:

Taotlus, mis loob sisestatud andmete põhjal uue toote, aga POST url tüppiga
// GET products/add?id=1&name=piim&price=4.5&isactive=false
[HttpGet("add2")]
public List<Product> Add2([FromQuery] int id, [FromQuery] string name, [FromQuery] double price, [FromQuery] bool isActive)
{
Product product = new Product(id, name, price, isActive);
_products.Add(product);
return _products;
}
Tulemus:

Päring, mis tagastab kõik hinnad dollarites sissejuhatava kursi alusel
// GET products/price-in-dollars/1.5
[HttpPatch("price-in-dollars/{course}")]
public List<Product> InDollars(double course)
{
for (int i = 0; i < _products.Count; i++)
{
_products[i].Price = _products[i].Price * course;
}
return _products;
}
Tulemus:

Sama, aga foreach-iga
// GET products/price-in-dollars2/1.5
[HttpGet("price-in-dollars2/{course}")]
public List<Product> InDollars2(double course)
{
foreach (var p in _products)
{
p.Price = p.Price * course;
}
return _products;
}
Tulemus:

Harjutused
Tee uus API otspunkt, mis kustutab korraga kõik tooted
// GET products/delete-all
[HttpGet("delete-all")]
public List<Product> DeleteAll()
{
_products.Clear();
return _products;
}
Tulemus:

Tee uus API otspunkt, mis muudab kõikide toodete aktiivsuse väära peale
// GET products/all-activity-to-false
[HttpGet("all-activity-to-false")]
public List<Product> ChangeAllActivityToFalse()
{
foreach (var p in _products)
{
p.IsActive= false;
}
return _products;
}
Tulemus:

Tee uus API otspunkt, mis tagastab ühe toote – vastavalt kelle järjekorranumber on lisatud URL muutujasse
// GET products/product/2
[HttpGet("product/{Num}")]
public Product GetProductByNumber(int Num)
{
return _products.ElementAtOrDefault(Num-1);
}
Tulemus:

Tee uus API otspunkt, mis tagastab ühe toote – kõige suurema hinnaga toote
// GET products/most-expensive
[HttpGet("most-expensive")]
public Product GetTheMostExpensiveProduct()
{
return _products.OrderByDescending(product => product.Price).FirstOrDefault();
}
Tulemus:

React appi loomine – eesrakendus
Mis on react?
Reacti põhiomadused:
React on JavaScripti teek, mis on loodud kasutajaliideste (UI) arendamiseks. See võimaldab luua dünaamilisi ja interaktiivseid veebirakendusi, kus liides reageerib kasutaja tegevustele.
- Komponendipõhine lähenemine: React jagab liidese väikesteks, iseseisvateks komponentideks, millest igaüks vastutab oma UI osa eest. See muudab koodi struktureeritumaks ja lihtsustab hooldamist.
- Virtuaalne DOM: React kasutab virtuaalset DOM-i, et optimeerida lehe uuendusi. Selle asemel, et uuesti joonistada kogu liidest andmete muutumisel, uuendab React ainult neid elemente, mis on muutunud.
- JSX (JavaScript XML): JSX on JavaScripti süntaksi laiendus, mis võimaldab kirjutada HTML-sarnast koodi otse JavaScriptis. See lihtsustab komponentide arendamist ja muudab koodi loetavamaks.
- Ühesuunaline andmevoog: Reactis liiguvad andmed vanemkomponentidest tütarkomponentidesse, kasutades
props
-e. See tagab parema seisundi haldamise ja hoiab ära ettearvamatud andmete muutused. - Seisund (State): Komponendid saavad salvestada ja muuta oma seisundit
state
abil, mis võimaldab liidest hõlpsasti uuendada sõltuvalt kasutaja tegevustest.
React on populaarseks saanud tänu sellele, et see võimaldab keeruliste liideste arendamist kiiresti ja tõhusalt, pakkudes suurepärast jõudlust ja mugavust arendajatele.
Appi loomine
npx create-react-app react-web-api
Appi käivitamine
npm start
Meie toodete kuvamine pealehel
//src/App.js
import { useEffect, useRef, useState } from 'react';
import './App.css';
function App() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch("https://localhost:7188/products")
.then(res => res.json())
.then(json => setProducts(json));
}, []);
return (
<div className="App">
{products.map((product) =>
<div>
<div>{product.id}</div>
<div>{product.name}</div>
<div>{product.price}</div>
</div>)}
</div>
);
}
export default App;
Program.cs järgmine kood
app.UseCors(options => options
.WithOrigins("*")
.AllowAnyMethod()
.AllowAnyHeader()
);
Tulemus:

Värskendame koodi nii, et sellel oleks kustutamise ja lisamise nupud
import { useEffect, useRef, useState } from 'react';
import './App.css';
function App() {
const [products, setProducts] = useState([]);
const idRef = useRef();
const nameRef = useRef();
const priceRef = useRef();
const isActiveRef = useRef();
const [isUsd, setUsd] = useState(false);
useEffect(() => {
fetch("https://localhost:7188/products")
.then(res => res.json())
.then(json => setProducts(json));
}, []);
function _delete(index) {
fetch("https://localhost:7188/products/delete/" + index)
.then(res => res.json())
.then(json => setProducts(json));
}
function add() {
fetch(`https://localhost:7188/products/add/${Number(idRef.current.value)}/${nameRef.current.value}/${Number(priceRef.current.value)}/${isActiveRef.current.checked}`)
.then(res => res.json())
.then(json => setProducts(json));
}
function inDollars() {
const cource = 1.1;
setUsd(true);
fetch("https://localhost:7188/products/price-in-dollars/" + cource)
.then(res => res.json())
.then(json => setProducts(json));
}
function inEuro() {
const cource = 0.9091;
setUsd(false);
fetch("https://localhost:7188/products/price-in-dollars/" + cource)
.then(res => res.json())
.then(json => setProducts(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={() => add()}>Lisa</button>
{products.map((product, index) =>
<div>
<div>{product.id}</div>
<div>{product.name}</div>
<div>{product.price}</div>
<button onClick={() => _delete(index)}>x</button>
</div>)}
<button onClick={() => inDollars()}>Muuda dollariteks</button>
</div>
);
}
export default App;
Tulemus:


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()
);
Paneme GET päringute asemel lisaks PUT, POST, PATCH, DELETE
Tähendused:
- GET – andmete võtmiseks
- PUT – andmete asendamiseks
- POST – andmete lisamiseks
- PATCH – ühe omaduse muutmiseks
- DELETE – kustutamiseks
// DELETE: products/delete/0
[HttpDelete("delete/{index}")]
public List<Product> Delete(int index)
{
_products.RemoveAt(index-1);
return _products;
}
// DELETE: products/delete2/0
[HttpDelete("delete2/{index}")]
public string Delete2(int index)
{
_products.RemoveAt(index-1);
return "Kustutatud!";
}
// POST: products/add/6/testtoode/5.0/true
[HttpPost("add/{id}/{name}/{price}/{isactive}")]
public List<Product> Add(int id, string name, double price, bool isActive)
{
Product product = new Product(id, name, price, isActive);
_products.Add(product);
return _products;
}
// POST products/add?id=1&name=piim&price=4.5&isactive=false
[HttpPost("add2")]
public List<Product> Add2([FromQuery] int id, [FromQuery] string name, [FromQuery] double price, [FromQuery] bool isActive)
{
Product product = new Product(id, name, price, isActive);
_products.Add(product);
return _products;
}
// PATCH products/price-in-dollars/1.5
[HttpPatch("price-in-dollars/{course}")]
public List<Product> InDollars(double course)
{
for (int i = 0; i < _products.Count; i++)
{
_products[i].Price = _products[i].Price * course;
}
return _products;
}
function _delete(index) {
fetch("https://localhost:7188/products/delete/" + index, {"method": "DELETE"}) /////////
.then(res => res.json())
.then(json => setProducts(json));
}
function add() {
fetch(`https://localhost:7188/products/add/${Number(idRef.current.value)}
${nameRef.current.value}/${Number(priceRef.current.value)}/
${isActiveRef.current.checked}`, {"method": "POST"}) ///////
.then(res => res.json())
.then(json => setProducts(json));
}
function inDollars() {
const cource = 1.1;
setUsd(true);
fetch("https://localhost:7188/products/price-in-dollars/" + cource, {"method": "PATCH"})////////
.then(res => res.json())
.then(json => setProducts(json));
}
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("add")]
public List<Product> Add([FromBody] Product product)
{
_products.Add(product);
return _products;
}
function add() {
const newProduct = {
"id": Number(idRef.current.value),
"name": nameRef.current.value,
"price": Number(priceRef.current.value),
"isActive": isActiveRef.current.checked
}
fetch("https://localhost:7188/products/add", {"method": "POST", "body": JSON.stringify(newProduct)})
.then(res => res.json())
.then(json => setTooted(json));
}
Taotluste saatmiseks saate kasutada ka Postimeest
Postman on tööriist, mis võimaldab arendajatel testida ja siluda API-dega tehtavaid päringuid. Sellega saab saata päringuid, vaadata vastuseid ja automatiseerida testimist.
Valime meetod ja vajutame Send


Harjutused
Tee tagarakendusse uus API otspunkt, mis muudab toodet. Kasuta PUT päringu tüüpi. Eesrakenduses pole midagi lisada vaja.
// PUT products/update
[HttpPut("update")]
public ActionResult<Product> Update([FromBody] Product product)
{
var updatingProduct = _products.FirstOrDefault(p => p.Id == product.Id);
if (updatingProduct == null)
{
return NotFound();
}
updatingProduct.Name = product.Name;
updatingProduct.Price = product.Price;
updatingProduct.IsActive = product.IsActive;
return Ok(updatingProduct);
}
Tulemus:

ParcelMachineController
Päring, mis tagastab seda API-d kasutades omniva meiliautomaatide loend
using Microsoft.AspNetCore.Mvc;
namespace DotskinWebApi.Controllers
{
[Route("[controller]")]
[ApiController]
public class ParcelMachineController : ControllerBase
{
private readonly HttpClient _httpClient;
public ParcelMachineController(HttpClient httpClient)
{
_httpClient = httpClient;
}
//ParcelMachine/omniva
[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");
}
}
}
import { useEffect, useState } from 'react';
import './App.css';
function App() {
const [pakiautomaadid, setPakiautomaadid] = useState([]);
useEffect(() => {
fetch("https://localhost:7188/parcelmachine/get")
.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;
Tulemus:

Ja sama smartposti API-ga
//ParcelMachine/smartpost
[HttpGet]
public async Task<IActionResult> GetParcelMachines()
{
// Küsime andmed SmartPost API-st
var response = await _httpClient.GetAsync("https://www.smartpost.ee/places.json");
var responseBody = await response.Content.ReadAsStringAsync();
return Content(responseBody, "application/json");
}
NordPoolController
Päringuga võtame järgmise 24h hinnad
using Microsoft.AspNetCore.Mvc;
namespace DotskinWebApi.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");
}
}
}
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:7188/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;
Tulemus:

Eesrakendus soovib API otspunkti konfigureerida selliseks, et saadetakse kaasa “ee”, “lv”, “lt” või “fi” parameeter ning näidatakse selle riigi hindasid
import { useEffect, useState } from 'react';
import './App.css';
function App() {
const [prices, setPrices] = useState([]);
const [chosenCountry, setChosenCountry] = useState("ee");
useEffect(() => {
fetch("https://localhost:7188/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;
[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.");
}
}
Tulemus:


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
[HttpGet("{country}/{start}/{end}")]
public async Task<IActionResult> GetNordPoolPrices(
string country,
string start,
string end)
{
var response = await _httpClient.GetAsync(
$"https://dashboard.elering.ee/api/nps/price?start={start}&end={end}");
var responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
var jsonDoc = JsonDocument.Parse(responseBody);
var dataProperty = jsonDoc.RootElement.GetProperty("data");
string prices;
switch (country)
{
case "ee":
prices = dataProperty.GetProperty("ee").ToString();
Console.WriteLine(responseBody);
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.");
}
}
import { useRef } from 'react';
import { useEffect, useState } from 'react';
import './App.css';
function App() {
const [prices, setPrices] = useState([]);
const [chosenCountry, setChosenCountry] = useState("ee");
const [start, setStart] = useState("");
const [end, setEnd] = useState("");
const startRef = useRef();
const endRef = useRef();
useEffect(() => {
if (start !== "" && end !== "") {
fetch("https://localhost:7188/nordpool/" + chosenCountry + "/" + start + "/" + end)
.then(res => res.json())
.then(json => {
setPrices(json);
});
}
}, [chosenCountry, start, end]);
function updateStart() {
const startIso = new Date(startRef.current.value).toISOString();
setStart(startIso);
}
function updateEnd() {
const endIso = new Date(endRef.current.value).toISOString();
setEnd(endIso);
}
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>
<input ref={startRef} onChange={updateStart} type="datetime-local" />
<input ref={endRef} onChange={updateEnd} type="datetime-local" />
{prices.length > 0 &&
<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>
<td style={{position: "absolute", left: "30px"}}>{chosenCountry}</td>
{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;
PaymentController
See kood teeb ühekordset makset EveryPay makselüüsis. Kui kasutaja saadab summa, siis luuakse makseandmed (sh kasutajanimi, summa, kordumatu tellimuse viide ja kliendi URL), mis saadetakse maksesüsteemile. Kui makse õnnestub, tagastatakse makselink, vastasel juhul veateade
[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.");
}
}
Tulemus:

Käime lingiga




Edasiareng / Oma rakendus – Veebipood nimega ‘DotShop’
Mida see sisaldab?
DotShop — see on mugav ja kaasaegne veebipood, mis võimaldab kasutajatel hõlpsasti veebis oste sooritada. Pood sisaldab järgmisi olulisi funktsioone:
1. Toodete nimekiri:
• Kataloog toodetega, mis sisaldab üksikasjalikke kirjeldusi ja infot laoseisu kohta.
• Võimalus lisada tooteid ostukorvi.
2. Ostukorv:
• Vaade valitud toodetele koos koguse ja hinnaga.
• Võimalus muuta ostukorvi sisu enne tellimuse esitamist.
3. Registreerimine ja süsteemi sisselogimine:
• Kasutajad saavad registreeruda, luua konto või logida süsteemi sisse.
• Autoriseerimine avab juurdepääsu lisafunktsioonidele, nagu tellimuste esitamine ja ajaloo vaatamine.
4. Tellimuse esitamine makse imiteerimisega:
• Registreeritud kasutajad saavad esitada tellimusi läbi lihtsa ja mugava kasutajaliidese.
• Makseprotsess sisaldab edukat makse simuleerimist.
5. Tellimuste vaatamine:
• Kasutaja saab oma profiilis jälgida tellimuste ajalugu, üksikasju ja olekut.
6. Mitmekeelsus:
• Veebilehe liides on saadaval kolmes keeles: eesti, inglise ja vene keeles.
• Kasutajad saavad valida endale sobiva keele mugavamaks kasutamiseks.
Mudeli värskendus
Toote mudeli täiendamine:
Uued võtmeväljad:
• Stock (laoseis) — haldab toodete kogust laos.
• Category (kategooria) — võimaldab tooteid filtreerida ja grupeerida kategooriate alusel.
• PricePerUnit (ühiku hind) — hind ühe ühiku kohta (nt kg või tükk).
• Unit (ühik: kg/item) — määrab, millisel kujul hind esitatakse.
• HasBottle (kas sisaldab pudelit) — loogiline väli, mis näitab, kas toode nõuab lisatasu (nt 10 senti pudeli eest).
using System.Text.Json.Serialization;
namespace DotskinWebApi.Models
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public double PricePerUnit { get; set; }
public double AmountInStock { get; set; }
public string Unit { get; set; }
public string Category { get; set; }
public bool HasBottle { get; set; }
public bool IsActive { get; set; }
public string ImageUrl { get; set; }
public List<OrderItem> OrderItems { get; set; } = new List<OrderItem>();
public Product(int id, string name, double pricePerUnit, bool isActive, string unit, bool hasBottle, string imageUrl, double amountInStock, string category)
{
Id = id;
Name = name;
PricePerUnit = pricePerUnit;
IsActive = isActive;
Unit = unit;
HasBottle = hasBottle;
ImageUrl = imageUrl;
AmountInStock = amountInStock;
Category = category;
}
}
}
• Kasutusvajadus:
Need väljad võimaldavad dünaamilist hinnakalkulatsiooni frontendis, toodete sorteerimist ja lisatasude arvestamist, et tagada täpsem arvutus ostukorvis.
Uute mudelite loomine
Tellimuse mudel:
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
namespace DotskinWebApi.Models
{
public class Order
{
public int Id { get; set; }
public DateOnly Date { get; set; } = DateOnly.FromDateTime(DateTime.Now);
public double TotalPrice { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
[JsonIgnore]
public User User { get; set; }
public List<OrderItem> OrderItems { get; set; } = new List<OrderItem>();
}
}
• Kasutusvajadus ja toimimine:
• Salvestab tellimuse andmeid, nagu koguhind (TotalPrice), kuupäev (Date) ja kasutaja (User).
• Seos OrderItem mudeliga võimaldab salvestada üksikasjalikku teavet tellimuse iga toote kohta.
• Seda kasutatakse tellimuste ajaloo lehe koostamiseks ja nende töötlemiseks.
Tellimuse elemendi mudel:
public class OrderItem
{
public int Id { get; set; }
[ForeignKey("Order")]
public int OrderId { get; set; }
[ForeignKey("Product")]
public int ProductId { get; set; }
public double Quantity { get; set; }
public double TotalPrice => Product.PricePerUnit * Quantity;
[JsonIgnore]
public Order Order { get; set; }
[JsonIgnore]
public Product Product { get; set; }
}
Kasutusvajadus ja toimimine:
• Salvestab teavet iga tellimuse elemendi kohta, nagu kogus (Quantity) ja viide konkreetsele tootele (Product).
• Arvutab iga toote hinna dünaamiliselt omaduse TotalPrice kaudu.
Miks on see vajalik:
• Tootemudelisse lisatud väljad tagavad, et hinnad arvutatakse õigesti vastavalt toote kogusele ja vajadusel lisatasudele (nt pudeli eest).
• Tellimuse ja tellimuse elemendi mudelid struktureerivad andmeid, mis võimaldab kasutajatel näha tellimuste ajalugu, töötada ostukorviga ja hallata makseid.
• Kasutades võtmeid, nagu ForeignKey, luuakse selged seosed tabelite vahel, mis lihtsustab andmetöötlust serveri poolel.
Töövoog:
1. Toodete lisamine ostukorvi:
• Kasutaja lisab toote ostukorvi, kus andmed salvestatakse OrderItem mudelisse.
2. Tellimuse loomine:
• Ostukorv suletakse ja tellimus salvestatakse Order mudelisse.
3. Hinna arvutamine:
• TotalPrice arvutatakse automaatselt, summeerides kõigi tellimuse elementide hinna (OrderItem.TotalPrice).
See süsteem on paindlik ja toetab tulevasi laiendusi, sealhulgas mitmekeelsust ja erinevate toodete kategooriate haldamist.
Klass hinna arvutamiseks koos käibemaksuga