Aplikacja internetowa działa w przeglądarce, a jej dane leżą na serwerze firmy albo u dostawcy usługi. Ten tekst opisuje najczęstsze zastosowania w firmach: portal klienta i panel zamówień, a także raporty. Dla każdego pokazano, co widzi użytkownik, jakie zapytanie idzie do serwera i co odpowiada baza SQL Server.
Cykl żądanie-odpowiedź w HTTP oraz sesje omawia osobny artykuł o aplikacji webowej. Tutaj nacisk pada na przykłady zastosowań i na podział odpowiedzialności między przeglądarką a serwerem z bazą danych.
Aplikacje internetowe w firmie - przykłady zastosowań
Ta sama technologia obsługuje bardzo różne role. Różnicę wyznacza to, kto korzysta z aplikacji i do których rekordów ma dostęp. Portal klienta widzi kontrahent zewnętrzny, panel zamówień obsługuje pracownik, a raporty przeglądają kierownicy.
| Zastosowanie | Kto korzysta | Typowa operacja | Dane w bazie |
|---|---|---|---|
| Portal klienta | Kontrahent zewnętrzny | Podgląd zamówień i dokumentów | Widoki tylko do odczytu, zawężone do jednego kontrahenta |
| Panel zamówień | Pracownik obsługi | Zakładanie zamówień i zmiana statusów | Tabele zamówień z historią zmian |
| Raporty | Kierownik lub dyrektor | Filtrowanie i eksport zestawień | Widoki i procedury agregujące |
Reguła: każde zastosowanie ma osobny zestaw uprawnień, bo konto kontrahenta nie może sięgać do tabel używanych przez pracownika.
Wszystkie trzy zastosowania mogą działać na jednej bazie i jednym serwerze aplikacji. Rozdzielają je role użytkowników oraz zestaw procedur udostępnianych każdej z nich. Dzięki temu nowe zastosowanie dodaje się bez przebudowy pozostałych.
Portal klienta - dostęp do własnych danych
Portal klienta zastępuje telefony i wiadomości z pytaniem o status przesyłki lub dokument. Kontrahent loguje się na własne konto i widzi zamówienia i faktury, a także listę dokumentów do pobrania. Pracownicy obsługi nie odbierają wtedy pytań, na które odpowiedź już jest w bazie.

Tożsamość kontrahenta w sesji
Najczęstszy błąd portali polega na tym, że numer kontrahenta jest częścią adresu, na przykład /zamowienia/klient/118. Zmiana liczby w pasku adresu pokazuje wtedy cudze rekordy. Poprawna wersja bierze identyfikator z sesji zapisanej na serwerze po zalogowaniu i ignoruje wszystko, co przyszło w adresie.
Zakres danych widocznych w portalu
Portal pokazuje wybrane kolumny, a nie całe tabele. Cena zakupu, marża czy uwagi wewnętrzne pozostają w bazie i nie trafiają do odpowiedzi API. Przy awizacjach dostaw ten sam wzorzec stosuje się do przewoźników, którzy widzą wyłącznie własne okna czasowe, co opisuje artykuł o module awizacji dla przewoźników.
- Logowanie - konto kontrahenta jest przypisane do jednego rekordu w tabeli kontrahentów, a hasło przechowuje się wyłącznie jako skrót.
- Zakres danych - każde zapytanie API dostaje identyfikator kontrahenta z sesji jako parametr, nigdy z formularza.
- Dokumenty - pliki PDF pobiera się przez adres z krótkim czasem ważności, a nie przez publiczny katalog.
- Powiadomienia - zmianę statusu zamówienia zapisuje się w kolejce, a wysyłką wiadomości zajmuje się osobne zadanie.
Panel zamówień - jeden ekran dla obsługi
Panel zamówień służy pracownikom, którzy zakładają i poprawiają zamówienia oraz zmieniają ich status. Lista zamówień filtruje się na serwerze, nie w przeglądarce, bo baza przy kilkudziesięciu tysiącach rekordów odda stronę wyników szybciej niż przeglądarka przefiltruje pełny zestaw. Paginację zapewnia klauzula OFFSET ... FETCH z sortowaniem po indeksowanej kolumnie.
SELECT z.NumerZamowienia, z.DataZlozenia, z.Status
FROM dbo.Zamowienie AS z
WHERE z.KlientId = @KlientId
ORDER BY z.DataZlozenia DESC, z.ZamowienieId DESC
OFFSET @Pominiete ROWS FETCH NEXT 50 ROWS ONLY;
Drugie kryterium sortowania, unikalny identyfikator, jest potrzebne dla powtarzalności wyników. Bez niego dwa zamówienia z tą samą datą mogą zamienić się miejscami między stronami i jedno z nich zniknie z listy albo pojawi się dwa razy.
| Status | Kto go ustawia | Skutek w bazie |
|---|---|---|
| Nowe | Klient przez portal lub pracownik | Nowy rekord zamówienia i wpis w historii |
| Potwierdzone | Pracownik obsługi | Rezerwacja ilości towaru |
| W realizacji | Magazyn | Powiązanie z dokumentem wydania |
| Zrealizowane | System po zamknięciu dokumentu | Zamknięcie rezerwacji, blokada edycji |
| Anulowane | Pracownik, do momentu wydania | Zwolnienie rezerwacji |
Zestaw statusów w tabeli jest przykładowy. Rzeczywisty zależy od procesu firmy, ale zasada pozostaje ta sama: każdy status ma jednoznacznego wykonawcę i konkretny skutek w danych.
Zmiana statusu w jednej transakcji
Dwóch pracowników może otworzyć to samo zamówienie i zmienić je prawie jednocześnie. Kolumna typu rowversion pozwala wykryć taką sytuację bez blokowania rekordu na czas edycji. Aplikacja odczytuje wersję razem z zamówieniem, a przy zapisie porównuje ją z aktualną. Poniższa procedura jest przykładem, nie schematem konkretnego produktu.
BEGIN TRANSACTION;
UPDATE dbo.Zamowienie
SET Status = N'w realizacji'
WHERE ZamowienieId = @ZamowienieId
AND Wersja = @Wersja; -- kolumna rowversion odczytana wraz z rekordem
IF @@ROWCOUNT = 0
BEGIN
ROLLBACK TRANSACTION;
THROW 50001, N'Zamówienie zmienił inny użytkownik.', 1;
END;
INSERT INTO dbo.ZamowienieHistoria (ZamowienieId, Status, Uzytkownik, Kiedy)
VALUES (@ZamowienieId, N'w realizacji', @Uzytkownik, SYSDATETIME());
COMMIT TRANSACTION;
Reguła: zmiana statusu i wpis do historii wykonują się w jednej transakcji, bo osobne zapisy zostawiłyby historię niezgodną ze stanem zamówienia.
Użytkownik, któremu zapis się nie udał, dostaje komunikat z prośbą o odświeżenie widoku. Nie traci pracy po cichu, a baza nie trzyma blokady przez czas, gdy ktoś wypełnia formularz.
Raporty w przeglądarce - zapytania do bazy SQL
Raport w aplikacji internetowej to zapytanie z parametrami, którego wynik przeglądarka wyświetla jako tabelę lub wykres. Filtry z ekranu, na przykład zakres dat, trafiają do serwera jako parametry procedury. Warunek dat warto zapisać jako przedział otwarty z prawej strony, bo wtedy indeks na kolumnie daty daje się wykorzystać.
SELECT CAST(z.DataZlozenia AS date) AS Dzien,
COUNT(*) AS LiczbaZamowien,
SUM(CASE WHEN z.Status = N'anulowane' THEN 1 ELSE 0 END) AS Anulowane
FROM dbo.Zamowienie AS z
WHERE z.DataZlozenia >= @Od
AND z.DataZlozenia < @Do
GROUP BY CAST(z.DataZlozenia AS date)
ORDER BY Dzien;
Funkcja CAST stoi tu w liście kolumn i w grupowaniu, ale nie w warunku WHERE. Gdyby stała w warunku, silnik musiałby przeliczyć każdy wiersz i pominąć indeks.
Raporty, które trwają dłużej niż kilka sekund, nie powinny blokować zapisów z panelu zamówień. Dwa rozwiązania to włączenie w bazie opcji READ_COMMITTED_SNAPSHOT, dzięki której odczyt korzysta z wersji wierszy zamiast blokad, oraz osobna kopia do odczytu. Zestawienia wydrukowane na papierze albo w PDF wygodnie budować w SQL Report Builder, a ich publikację w przeglądarce omawia artykuł o Reporting Services.
Eksport i raporty liczone długo
Eksport do pliku CSV lub XLSX buduje się na serwerze i zwraca jako strumień, z górnym limitem wierszy. Przy zestawieniu, które liczy się dłużej niż kilkanaście sekund, przeglądarka nie powinna czekać na odpowiedź. Serwer zapisuje zlecenie raportu w tabeli, osobne zadanie liczy wynik, a ekran co kilka sekund pyta o status i na końcu podaje odnośnik do gotowego pliku.
Taki układ chroni przed przekroczeniem limitu czasu połączenia i pozwala ograniczyć liczbę raportów liczonych jednocześnie. Kolejka zleceń jest zwykłą tabelą z kolumnami statusu i daty rozpoczęcia, więc administrator widzi w niej, który raport zajmuje serwer najdłużej.
Architektura aplikacji - przeglądarka i serwer z bazą SQL
Aplikacja z powyższych przykładów składa się z trzech warstw, a każda odpowiada za inny rodzaj decyzji. Pomyłka polega zwykle na tym, że reguła trafia do niewłaściwej warstwy, na przykład walidacja uprawnień do przeglądarki.
| Warstwa | Odpowiada za | Czego unikać |
|---|---|---|
| Przeglądarka (JavaScript) | Wyświetlanie, wstępna walidacja pól, wywołania AJAX | Zaufania do walidacji i do ukrytych pól formularza |
| API (C#) | Uwierzytelnianie, autoryzacja, reguły biznesowe, transakcje | Składania SQL z tekstu przysłanego przez użytkownika |
| Baza SQL Server | Dane, klucze obce, ograniczenia, indeksy | Logiki zależnej od tego, kto kliknął |
Kontrakt API między przeglądarką a bazą
Interfejs API tłumaczy adres i parametry na wywołanie procedury składowanej. Poniższy fragment w C# pokazuje punkt końcowy, który zwraca zamówienia kontrahenta z sesji. Identyfikator klienta pochodzi z tożsamości użytkownika, a parametry przekazuje się przez kolekcję Parameters. Dokumentację tego typu interfejsów w ASP.NET Core zawiera Microsoft Learn.
[HttpGet("api/zamowienia")]
public IActionResult Zamowienia(string status = null)
{
int klientId = BiezacyKlientId(); // metoda przykładowa, odczyt z tożsamości użytkownika
var wynik = new List<object>();
using var po = new SqlConnection(_connectionString);
using var cmd = new SqlCommand("dbo.ZamowieniaKlienta", po)
{
CommandType = CommandType.StoredProcedure
};
cmd.Parameters.Add("@KlientId", SqlDbType.Int).Value = klientId;
cmd.Parameters.Add("@Status", SqlDbType.NVarChar, 20).Value = (object)status ?? DBNull.Value;
po.Open();
using var r = cmd.ExecuteReader();
while (r.Read())
{
wynik.Add(new { Numer = r.GetString(0), Data = r.GetDateTime(1), Status = r.GetString(2) });
}
return Ok(wynik);
}
Po stronie przeglądarki wywołanie sprowadza się do jednego zapytania AJAX. Kod 401 oznacza wygasłą sesję i przekierowuje na ekran logowania, a pozostałe błędy pokazują komunikat bez szczegółów technicznych.
$.getJSON('/api/zamowienia', { status: 'otwarte' })
.done(function (dane) {
$('#lista-zamowien').html(dane.map(function (z) {
return '<tr><td>' + $('<i>').text(z.numer).html() + '</td></tr>';
}).join(''));
})
.fail(function (xhr) {
if (xhr.status === 401) { window.location = '/logowanie'; }
});
Obsługa błędów i dziennik zdarzeń
Błąd w API trafia do dziennika z numerem zgłoszenia, a użytkownik dostaje tylko ten numer i krótki komunikat. Szczegóły, takie jak treść wyjątku i parametry wywołania, zostają po stronie serwera. Dzięki numerowi pracownik pomocy technicznej znajduje wpis w dzienniku bez pytania użytkownika o zrzut ekranu.
Indeks pod zapytanie z portalu
Procedura wybiera zamówienia jednego kontrahenta posortowane od najnowszych. Indeks nieklastrowy na parze kolumn KlientId i DataZlozenia pokrywa to zapytanie, a kolumny dołączone klauzulą INCLUDE oszczędzają odczyt z tabeli. Składnię opisuje dokumentacja CREATE INDEX.
CREATE NONCLUSTERED INDEX IX_Zamowienie_Klient_Data
ON dbo.Zamowienie (KlientId, DataZlozenia DESC)
INCLUDE (NumerZamowienia, Status);

Uprawnienia użytkowników zewnętrznych i wewnętrznych
Konto techniczne, którym API łączy się z bazą, powinno mieć uprawnienia tylko do wykonywania procedur z wybranego schematu. Nie dostaje prawa odczytu tabel ani prawa do zmiany struktury. Wtedy błąd w kodzie API nie otwiera dostępu do pozostałych danych.
CREATE SCHEMA portal;
GO
CREATE ROLE rola_portalu;
GRANT EXECUTE ON SCHEMA::portal TO rola_portalu;
DENY SELECT ON SCHEMA::dbo TO rola_portalu;
Drugą warstwę ochrony może dodać mechanizm zabezpieczeń na poziomie wierszy, dostępny od SQL Server 2016. Silnik bazy dopisuje wtedy do każdego zapytania predykat filtrujący, więc pominięcie warunku w kodzie aplikacji nie ujawnia cudzych rekordów.
Podział na role obejmuje też integracje. Wymianę danych z innymi systemami firmy, na przykład z ERP, porządkuje artykuł o integrowaniu aplikacji webowych, który porównuje wywołania REST z kolejkami komunikatów.
Przykłady z produktów SoftwareStudio
SoftwareStudio z Poznania buduje aplikacje internetowe na Microsoft IIS i SQL Server. Systemy takie jak Studio WMS.net i Studio RMA.net działają w przeglądarce, co eliminuje instalację na stanowiskach pracowników. Obsługa rejestru reklamacji jest przykładem panelu, w którym status zgłoszenia przechodzi przez kolejne etapy, a każda zmiana zostaje zapisana w historii.
Dla magazynu aplikacje przeglądarkowe uzupełniają programy na terminale z systemem Android, opisane w artykule o aplikacji magazynowej na Androida. W przeszłości firma tworzyła także programy na kolektory danych. Wycenę aplikacji na zamówienie SoftwareStudio przygotowuje indywidualnie, a rozmowę o zakresie można zacząć w serwisie produktowym Studio VSS.net albo w demo online.
Wybór pierwszego zastosowania do wdrożenia
Pierwsza wersja nie musi obejmować wszystkich trzech zastosowań. Zwykle rozpoczyna się od tego, które ma najprostsze dane i najwyraźniejszy koszt bieżącej obsługi. Prosty projekt w postaci MVP trwa krócej niż rozbudowany system; harmonogram ustala się po analizie wymagań.
- Liczba użytkowników - portal dla kilkuset kontrahentów obciąża serwer inaczej niż panel dla pięciu pracowników.
- Wrażliwość danych - dane dostępne dla kontrahentów wymagają dokładniejszych testów uprawnień niż raporty wewnętrzne.
- Zależność od integracji - panel zamówień bez dostępu do kartoteki towarów z ERP działa na danych przepisywanych ręcznie.
- Mierzalność efektu - liczba telefonów o status zamówienia daje się policzyć przed wdrożeniem i po nim.
Przebieg pracy od analizy po odbiór opisuje artykuł o etapach tworzenia aplikacji internetowej, a pojęcie aplikacji projektowanej pod jeden proces omawia tekst o dedykowanych aplikacjach webowych. Warunkiem dobrego wyboru jest liczba, którą można porównać po uruchomieniu. Bez niej nie da się ocenić, czy portal odciążył obsługę.




