Veebirakenduste loomine API päringute abil

Создаем проект

Iseseisvad harjutused:

  • Tee juhuslike numbrite generaator (või Random rand = new Random() abil) – sisestades otspunktile kaks arvu, tagastab programm juhusliku arvu nende vahel
  • 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”

Создаем новый файл PrimitiividController.cs:

И вписываем код в данный контроллер

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace veebMiljukova.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);
            }
        }

        // GET: primitiivid/random-number/10/20
        [HttpGet("random-number/{min}/{max}")]
        public int GetRandomNumber(int min, int max)
        {
            Random rand = new Random();
            return rand.Next(min, max + 1);
        }

        // GET: primitiivid/vanus/1990
        [HttpGet("vanus/{synniaasta}")]
        public string GetVanus(int synniaasta)
        {
            int currentYear = DateTime.Now.Year;
            int currentMonth = DateTime.Now.Month;
            int currentDay = DateTime.Now.Day;

            int vanus = currentYear - synniaasta;

            bool synnipaevOnJubaOlnud = currentMonth > 10 || (currentMonth == 10 && currentDay >= 9); 

            if (!synnipaevOnJubaOlnud)
            {
                vanus--; 
            }

            return "Oled " + vanus + " aastat vana.";
        }
    }
}

Рассмотрение функций:

Создаем папку Models и создаем в ней файл Toode.cs, Kasutaja.cs :

Toode.cs

namespace veebMiljukova.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;
        }
    }
}

Kasutaja.cs

namespace veebMiljukova.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;
        }
    }
}

Создадим новый контроллер ToodeController.cs

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using veebMiljukova.Models;

namespace veebMiljukova.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-aktiivsust
        [HttpGet("muuda-aktiivsust")]
        public Toode MuudaAktiivsust()
        {
            _toode.IsActive = !_toode.IsActive;
            return _toode;
        }

        // GET: toode/muuda-nime/{uusNimi}
        [HttpGet("muuda-nime/{uusNimi}")]
        public Toode MuudaNime(string uusNimi)
        {
            _toode.Name = uusNimi;
            return _toode;
        }

        // GET: toode/muuda-hinda-kordajaga/{kordaja}
        [HttpGet("muuda-hinda-kordajaga/{kordaja}")]
        public Toode MuudaHindaKordajaga(double kordaja)
        {
            _toode.Price *= kordaja;
            return _toode;
        }
    }
}

Создадим новый контроллер ToodedController.cs

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using veebMiljukova.Models;

namespace veebMiljukova.Controllers;
[ApiController]
[Route("[controller]")]
public class TootedController : ControllerBase // Peab pärima 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;
    }

    // API otspunkt, mis kustutab korraga kõik tooted
    [HttpGet("kustuta-koik")]
    public List<Toode> KustutaKoik()
    {
        _tooted.Clear();
        return _tooted; // Tagastab tühja nimekirja
    }

    // API otspunkt, mis muudab kõikide toodete aktiivsuse väära peale
    [HttpGet("muuda-koik-inaktiivseks")]
    public List<Toode> MuudaKoikInaktiivseks()
    {
        foreach (var toode in _tooted)
        {
            toode.IsActive = false;
        }
        return _tooted; // Tagastab uuendatud nimekirja
    }

    // API otspunkt, mis tagastab ühe toote vastavalt järjekorranumbrile
    [HttpGet("toode/{index}")]
    public ActionResult<Toode> GetToodeByIndex(int index)
    {
        if (index < 0 || index >= _tooted.Count)
        {
            return NotFound("Toode ei leitud!"); // Kasutab NotFound() meetodit
        }
        return _tooted[index];
    }

    // API otspunkt, mis tagastab kõige suurema hinnaga toote
    [HttpGet("kalleim-toode")]
    public Toode GetKalleimToode()
    {
        Toode kalleimToode = _tooted.OrderByDescending(t => t.Price).FirstOrDefault();
        return kalleimToode;
    }

    // Varasemad otspunktid
    [HttpDelete("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!";
    }

    [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;
    }

    //[HttpGet("lisa")]
    //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;
    //}
    //[HttpPost("lisa")]
    //public List<Toode> Add([FromBody] Toode toode)
    //{
    //    _tooted.Add(toode);
    //    return _tooted;
    //}

    [HttpGet("hind-dollaritesse/{kurss}")]
    public List<Toode> Dollaritesse(double kurss)
    {
        for (int i = 0; i < _tooted.Count; i++)
        {
            _tooted[i].Price = _tooted[i].Price * kurss;
        }
        return _tooted;
    }

    [HttpGet("hind-dollaritesse2/{kurss}")]
    public List<Toode> Dollaritesse2(double kurss)
    {
        foreach (var t in _tooted)
        {
            t.Price = t.Price * kurss;
        }
        return _tooted;
    }
}

Создадим новый контроллер KasutajadController.cs

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using veebMiljukova.Models;

namespace veebMiljukova.Controllers;
[ApiController]
[Route("[controller]")]
public class KasutajadController : ControllerBase
{
    private static List<Kasutaja> _kasutajad = new List<Kasutaja>
    {
        new Kasutaja(1, "Lisadevil", "1234lisa", "Lisa", "Paulk"),
        new Kasutaja(2, "Madler", "adler@234", "Mark", "Adler"),
        new Kasutaja(3, "Moon", "lillyhk", "Lilly", "Hikkimo")
    };

    // https://localhost:7052/kasutajad
    [HttpGet]
    public List<Kasutaja> Get()
    {
        return _kasutajad;
    }

    // API otspunkt, mis kustutab kõik kasutajad
    [HttpGet("kustuta-koik")]
    public List<Kasutaja> KustutaKoik()
    {
        _kasutajad.Clear();
        return _kasutajad; // Tagastab tühja nimekirja
    }

    // API otspunkt, mis tagastab ühe kasutaja vastavalt järjekorranumbrile 
    [HttpGet("kasutaja/{index}")]
    public ActionResult<Kasutaja> GetKasutajaByIndex(int index)
    {
        int adjustedIndex = index - 1;

        if (adjustedIndex < 0 || adjustedIndex >= _kasutajad.Count)
        {
            return NotFound("Kasutaja ei leitud!"); 
        }
        return _kasutajad[adjustedIndex];
    }

    // API otspunkt, mis lisab kasutaja
    [HttpPost("lisa")]
    public List<Kasutaja> Add([FromBody] Kasutaja kasutaja)
    {
        _kasutajad.Add(kasutaja);
        return _kasutajad;
    }

    // API otspunkt, mis eemaldab kasutaja järjekорранумбри järgi 
    [HttpGet("kustuta/{index}")]
    public List<Kasutaja> Delete(int index)
    {
        int adjustedIndex = index - 1;

        if (adjustedIndex < 0 || adjustedIndex >= _kasutajad.Count)
        {
            return _kasutajad; 
        }

        _kasutajad.RemoveAt(adjustedIndex);
        return _kasutajad;
    }

    // API otspunkt, mis muudab kasutaja andmeid 
    [HttpPost("muuda/{index}")]
    public ActionResult<List<Kasutaja>> Muuda(int index, [FromBody] Kasutaja uusKasutaja)
    {
        int adjustedIndex = index - 1;

        if (adjustedIndex < 0 || adjustedIndex >= _kasutajad.Count)
        {
            return NotFound("Kasutaja ei leitud!");
        }

        _kasutajad[adjustedIndex] = uusKasutaja;
        return _kasutajad;
    }
}

Если у вас не установлен Node.js, то устанавливаем

В дальнейшем мы переходим работать в VScode/PHPShtorm (для удобства взяла PHPShtorm)

Заходим в терминал, желательно в одной папке где находится наш проект, должно примерно выглядеть так:

Команды, которые вводились в терминал:

npx create-react-app frontend //создаем папку frontend
cd frontend //переходим в созданную папку
npm start //используется для запуска react

Запускаем проект в PHPShtorm

Переходим в файл App.js и вписываем код ниже

Учитываем, что данную строку

http://localhost:5256/Tooted/kustuta/

Нужно изменить “5256” на свои числа, которые есть в ссылке в backend

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("http://localhost:5256/Tooted")
        .then(res => res.json())
        .then(json => setTooted(json));
  }, []);

  function kustuta(index) {
    fetch("http://localhost:5256/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(`http://localhost:5256/Tooted/lisa/${idRef.current.value}/${nameRef.current.value}/${priceRef.current.value}/${isActiveRef.current.checked}`, {"method": "POST", "body": JSON.stringify(uusToode)})
            .then(res => res.json())
            .then(json => setTooted(json));
    }

  function dollariteks() {
    const kurss = 1.1;
    setUsd(true);
    fetch("http://localhost:5256/Tooted/hind-dollaritesse/" + kurss, {"method": "PATCH"})
        .then(res => res.json())
        .then(json => setTooted(json));
  }

  function eurodeks() {
    const kurss = 0.9091;
    setUsd(false);
    fetch("http://localhost:5256/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>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>)}
        {isUsd === false && <button onClick={() => dollariteks()}>Muuda dollariteks</button>}
        {isUsd === true && <button onClick={() => eurodeks()}>Muuda eurodeks</button>}
      </div>
  );
}

export default App;

Чтобы frontend работал полностью, нужно чтобы был запущен проект в visual studio

Далее мы переходим работать в postman

При отправке тела в Postman используйте http://localhost:4444/lisa в качестве конечной точки API, нажмите «body» -> «raw» -> «json» и введите тело запроса в следующей форме:

{
  "id": 321,
  "name": "Red bull",
  "price": 7,
  "isActive": true
}

Нажимаем на send и получаем результат добавления

Создаем новый контроллер ParcelMachineController.cs и прописываем данный код

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace veebMiljukova.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class ParcelMachineController : ControllerBase
    {
        private readonly HttpClient _httpClient;

        public ParcelMachineController(HttpClient httpClient)
        {
            _httpClient = httpClient;
        }

        [HttpGet("Omniva")]
        public async Task<IActionResult> GetParcelMachinesOmniva()
        {
            var response = await _httpClient.GetAsync("https://www.omniva.ee/locations.json");
            var responseBody = await response.Content.ReadAsStringAsync();
            return Content(responseBody, "application/json");
        }

        //не работает сайт с расположениями
        [HttpGet("Smartpost")]
        public async Task<IActionResult> GetParcelMachinesSmartPost()
        {
            var response = await _httpClient.GetAsync("https://www.smartpost.ee/places.json");
            var responseBody = await response.Content.ReadAsStringAsync();
            return Content(responseBody, "application/json");
        }
    }
}

Дополняем Program.cs

builder.Services.AddHttpClient();

В App.js вставляем код, вместо нашего кода и тоже меняем порт

import { useEffect, useState } from 'react';
import './App.css';

function App() {
    const [pakiautomaadid, setPakiautomaadid] = useState([]);

    useEffect(() => {
        fetch("http://localhost:5256/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;

Создаем новый контроллер NordpoolController.cs

using Microsoft.AspNetCore.Mvc;
using System.Text.Json;

namespace veebMiljukova.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 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.");
            }
        }
    }
}

Прописываем новый код в frontend App.js

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("http://localhost:5256/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.cs

using Microsoft.AspNetCore.Mvc;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Text;

namespace veebMiljukova.Controllers
{
[Route(“[controller]”)]
[ApiController]
public class PaymentController : ControllerBase
{
private readonly HttpClient _httpClient;

    public PaymentController(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    [HttpGet("{sum}")]
    public async Task&lt;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.");
        }
    }
}

}

Прописываем новый код в frontend App.js

import React, { useState } from 'react';
import './App.css';

function App() {
    const [amount, setAmount] = useState('');
    const [paymentLink, setPaymentLink] = useState(null);
    const [error, setError] = useState(null);

    const makePayment = async () => {
        setError(null);
        try {
            const response = await fetch(`http://localhost:5256/Payment/${amount}`);
            if (!response.ok) {
                throw new Error(`Error Fetching: ${response.status}`);
            }
            const data = await response.json();
            setPaymentLink(data);
        } catch (err) {
            setError(err.message);
        }
    };

    const handleSubmit = (event) => {
        event.preventDefault();
        makePayment();
    };

    return (
        <div className="App">
            <h1>Make a Payment</h1>

            <form onSubmit={handleSubmit}>
                <div>
                    <label htmlFor="amount">Enter Amount: </label>
                    <input
                        id="amount"
                        type="number"
                        value={amount}
                        onChange={e => setAmount(e.target.value)}
                        required
                        min="0.01"
                        step="0.01"
                        placeholder="Enter amount in EUR"
                    />
                </div>
                <button type="submit">Pay</button>
            </form>

            {error && <p style={{ color: 'red' }}>Error: {error}</p>}

            {paymentLink ? (
                <div>
                    <h2>Payment Link:</h2>
                    <a href={paymentLink} target="_blank" rel="noopener noreferrer">
                        Complete Payment
                    </a>
                </div>
            ) : (
                <p>No payment link available</p>
            )}
        </div>
    );
}

export default App;