REST API’s Pobieranie danych z serwera

0

Witam napisałem mały REST API serwer wzorując się na przykładzie poniżej, wszystko działa super, teraz potrzebuje dodać opcje żeby kilku klientów pobierało automatycznie informacje z serwera( jeżeli będą zmiany na serwerze klient wykona jakieś zadanie ).
z tego co wiem mam dwie możliwości jedna to WebSockets, druga to Ttimer ustawiony na 1000ms i pobieranie informacji z serwera przy pomocy: Procedure SendToServer();

Dodam że obecnie używam opcji drugiej która działa ale nie wiem jak się będzie to zachowywać przy większej ilości klientów.

Czy możecie mi doradzić poprawne rozwiazanie?

//Client

procedure GetFromServer();  
var
  Client: TFPHttpClient;
  Response: TStringStream;
begin

  Client := TFPHttpClient.Create(nil);
  Client.AddHeader('Authorization', 'Basic ' + EncodeStringBase64(form1.Edit2.Text + ':' + form1.Edit3.Text));
  Client.AllowRedirect := True;
  Client.RequestBody := TRawByteStringStream.Create(form1.edit4.Text);
  Response := TStringStream.Create('');              
  try
    try
      Client.Get(form1.Edit1.Text, Response);
      if client.ResponseStatusCode = 200 then
      begin
        form1.Memo1.Lines.Add(Response.DataString);
      end;                                    
    except
      on E: Exception do
        form1.Memo1.Lines.Add('Error : '+E.Message);
    end;
  finally
    Client.RequestBody.Free;
    Client.Free;
    Response.Free;
  end;
end;  

server

{
  Where     https://github.com/MFernstrom/rest-api-templates
  What      Basic Auth template
  Who       Marcus Fernström
  License   Apache 2.0
  Version   1.0
}
program BasicAuthTemplate;
{$mode objfpc}{$H+}
uses
 {$IFDEF UNIX}cthreads,
  cmem, {$ENDIF}
  SysUtils,
  strutils,
  fphttpapp,
  httpdefs,
  httproute,
  fpjson,
  base64;
 procedure validateRequest(aRequest: TRequest);
  var
    headerValue, b64decoded, username, password: string;
  begin
    headerValue := aRequest.Authorization;    if length(headerValue) = 0 then
      raise Exception.Create('This endpoint requires authentication');    if ExtractWord(1, headerValue, [' ']) <> 'Basic' then
      raise Exception.Create('Only Basic Authentication is supported');    b64decoded := DecodeStringBase64(ExtractWord(2, headerValue, [' ']));
    username := ExtractWord(1, b64decoded, [':']);
    password := ExtractWord(2, b64decoded, [':']);    // Replace this with your own logic
    if (username <> 'marcus') or (password <> '112233') then
      raise Exception.Create('Invalid API credentials');
end;
 procedure jsonResponse(aResponse: TResponse; JSON: TJSONObject; httpCode: integer);
  begin
    aResponse.Content := JSON.AsJSON;
    aResponse.Code := httpCode;
    aResponse.ContentType := 'application/json';
    aResponse.ContentLength := length(aResponse.Content);
    aResponse.SendContent;
  end;
 procedure apiEndpoint(aRequest: TRequest; aResponse: TResponse);
  var
    JSON: TJSONObject;
    httpCode: integer;
  begin
    JSON := TJSONObject.Create;
    
    try
      try
        validateRequest(aRequest);
        JSON.Add('success', True);
        JSON.Add('time', DateToStr(now));
        httpCode := 200;
      except
        on E: Exception do
        begin
          JSON.Add('success', False);
          JSON.Add('reason', E.message);
          httpCode := 401;
        end;
      end;
      jsonResponse(aResponse, JSON, httpCode);
 finally
      JSON.Free;
    end;
  end;begin
  HTTPRouter.RegisterRoute('/api', @apiEndpoint);
  Application.Port := 9080;
  Application.Threaded := True;
  Application.Initialize;
  WriteLn(format('API is ready at http://localhost:%d/', [Application.Port]));
  Application.Run;
end.
1

Radzimy oddzielić odwołanie do serwera od interfejsu użytkownika (login i hasło przekazujesz w parametrach a aktualizacja w tej chwili Memo w zdarzeniu) a samo sprawdzanie wykonywać w wątku, który będzie posiadał synchronizację i wywoływał zdarzenie pozwalające zaktualizować LCL. Serwer powinien też w nagłówkach zwracać jakiś identyfikator na podstawie, którego można się zorientować czy dane zostały zmienione (nie wiem data i czas ostatniej aktualizacji, czy jakiś unikalny token który zmienia się jeżeli dane zostaną zmienione) i to po każdym pobraniu danych powinno być sprawdzane tak aby niepotrzebnie nie wywoływać zdarzenia żądającego aktualizacji LCL.

0
kAzek napisał(a):

Serwer powinien też w nagłówkach zwracać jakiś identyfikator na podstawie, którego można się zorientować czy dane zostały zmienione (nie wiem data i czas ostatniej aktualizacji, czy jakiś unikalny token który zmienia się jeżeli dane zostaną zmienione) i to po każdym pobraniu danych powinno być sprawdzane tak aby niepotrzebnie nie wywoływać zdarzenia żądającego aktualizacji LCL.

HTTPRouter.RegisterRoute('/api/1', @ApiUpdate);

procedure apiupdate(aRequest: TRequest; aResponse: TResponse);
var
  httpCode: integer;
  changeServer:TDateTime;
begin
  try
    try
         httpCode := 200;
    except
      on E: Exception do
      begin
        httpCode := 401;
      end;
    end;
    aResponse.Code := httpCode;
    aResponse.Date:=datetostr(changeServer);
    aResponse.SendContent;
  finally
  end;
end;      

Czyli robię Endpoint np. 'ApiUpdate' a w nim tylko Request Headers data ostatniej zmiany na serwerze, klient łączy się z tym Endpointem sprawdza datę jeżeli się zimni, łączy się z innym Endpointem i pobiera potrzebne dane.

Radzimy oddzielić odwołanie do serwera od interfejsu użytkownika (login i hasło przekazujesz w parametrach a aktualizacja w tej chwili Memo w zdarzeniu) a samo sprawdzanie wykonywać w wątku, który będzie posiadał synchronizację i wywoływał zdarzenie pozwalające zaktualizować LCL.

Mógłbyś to trochę rozwinąć nie bardzo rozumiem co masz na myśli.?

1 użytkowników online, w tym zalogowanych: 0, gości: 1