123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- unit restdata;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, httpdefs;
- Procedure HandleRest(aRequest : TRequest; AResponse : TResponse);
- implementation
- uses jsonparser,fpjson;
- Var
- Packet : TJSONObject;
- LastID : integer;
- Function Data : TJSONArray;
- begin
- Result:=Packet.Arrays['Data'];
- end;
- Function MetaData : TJSONObject;
- begin
- Result:=Packet.Objects['metaData'];
- end;
- Procedure LoadRestData;
- Var
- F : TFileStream;
- D : TJSONData;
- I : Integer;
- begin
- F:=TFileStream.Create('countries.json',fmOpenRead or fmShareDenyWrite);
- try
- D:=GetJSON(F);
- if not (D is TJSONObject) then
- D.Free
- else
- Packet:=D as TJSONObject;
- MetaData.Arrays['fields'].Add(TJSONObject.Create(['name','id','type','int']));
- For I:=0 to Data.Count-1 do
- Data.Objects[i].Add('id',I+1);
- LastID:=Data.Count;
- finally
- F.Free;
- end;
- end;
- Procedure DoneRestData;
- begin
- Packet.Free;
- end;
- Procedure SendRecords(aFirst,aLast : integer; AResponse : TResponse);
- Var
- J: TJSONObject;
- A : TJSONArray;
- I : Integer;
- begin
- A:=TJSONArray.Create;
- J:=TJSONObject.Create(['metaData',MetaData.Clone,'Data',A]);
- try
- I:=AFirst;
- While (I<=ALast) and (I<Data.Count) do
- begin
- A.Add(Data[i].Clone);
- Inc(I);
- end;
- AResponse.Content:=J.FormatJSON();
- AResponse.ContentLength:=Length(AResponse.Content);
- AResponse.ContentType:='application/json';
- AResponse.SendContent;
- finally
- J.Free;
- end;
- end;
- Function IndexOfID(ID : Integer) : Integer;
- begin
- Result:=Data.Count-1;
- While (Result>=0) and (Data.Objects[Result].Integers['id']<>ID) do
- Dec(Result);
- end;
- Procedure NotFound(AResponse : TResponse);
- begin
- AResponse.Code:=404;
- AResponse.CodeText:='Not found';
- AResponse.SendResponse;
- end;
- Procedure InvalidParam(AResponse : TResponse; AContent : String = '');
- begin
- AResponse.Code:=400;
- AResponse.CodeText:='Bad request';
- AResponse.COntent:=AContent;
- AResponse.ContentLength:=Length(AContent);
- AResponse.SendResponse;
- end;
- Function GetRequestID(aRequest : TRequest) : Integer;
- Var
- P : String;
- I : integer;
- begin
- P:=ARequest.PathInfo;
- if (P<>'') and (P[1]='/') then
- Delete(P,1,1);
- I:=Pos('/',P);
- if I=0 then
- I:=Length(P);
- Delete(P,1,I);
- if (P<>'') then
- Result:=StrToIntDef(P,-1)
- else
- Result:=-2;
- end;
- Procedure GetRecords(aRequest : TRequest; AResponse : TResponse);
- Var
- ID,First,Last : Integer;
- begin
- First:=0;
- Last:=Data.Count-1;
- ID:=GetRequestID(ARequest);
- if ID=-1 then
- begin
- Writeln('Bad GET request: ',aRequest.PATHINFO);
- InvalidParam(AResponse);
- exit;
- end;
- if (ID<>-2) then
- begin
- First:=IndexOfID(ID);
- if First=-1 then
- begin
- NotFound(AResponse);
- exit;
- end;
- Last:=First;
- SendRecords(First,Last,AResponse);
- end
- else
- begin
- First:=StrToIntDef(ARequest.QueryFields.Values['Offset'],First);
- if (ARequest.QueryFields.Values['Limit']<>'') then
- Last:=First+StrToIntDef(ARequest.QueryFields.Values['Limit'],20)-1; // Default page size of 20
- Writeln('Count ',Data.Count,', Offset: ',First,', Last: ',Last);
- SendRecords(First,last,AResponse);
- end;
- end;
- Function GetObject(aRequest : TRequest; Out AReason : String) : TJSONObject;
- Var
- D : TJSONData;
- C,I : Integer;
- begin
- Result:=Nil;
- if Not SameText(aRequest.ContentType,'application/json') then
- Exit;
- try
- D:=GetJSON(aRequest.Content,true);
- if (D is TJSONObject) then
- begin
- Result:=D as TJSONObject;
- C:=Result.Count-1;
- D:=Nil;
- end
- else
- begin
- FreeAndNil(D);
- end;
- I:=C;
- While Assigned(Result) and (I>=0) do
- begin
- if (Pos(Result.Names[I]+';','id;Name;Population;')=0) then
- begin
- AReason:='Invalid property: '+Result.Names[I];
- FreeAndNil(Result);
- end;
- Dec(I);
- end;
- except
- On E : Exception do
- AReason:=E.ClassName+': '+E.Message;
- end;
- end;
- Procedure CreateRecord(aRequest : TRequest; AResponse : TResponse);
- Var
- O,D: TJSONObject;
- I : integer;
- R : String;
- begin
- I:=GetRequestID(ARequest);
- if I<>-2 then
- begin
- Writeln('Bad POST request: ',aRequest.PathInfo);
- R:='No ID allowed for POST';
- InvalidParam(AResponse,R);
- exit;
- end;
- O:=GetObject(ARequest,R);
- if (O=Nil) then
- begin
- Writeln('Bad POST request: ',aRequest.Content,' : ',R);
- InvalidParam(AResponse,R);
- exit;
- end;
- try
- D:=TJSONObject.Create;
- I:=Data.Add(D);
- D.Add('id',LastID);
- D.add('Name',O.Strings['Name']);
- D.add('Population',O.Int64s['Population']);
- Inc(LastID);
- SendRecords(I,I,AResponse);
- finally
- O.Free;
- end;
- end;
- Procedure UpdateRecord(aRequest : TRequest; AResponse : TResponse);
- Var
- ID,Idx : Integer;
- O,D : TJSONObject;
- R : String;
- begin
- ID:=GetRequestID(ARequest);
- if ID<0 then
- begin
- R:='Need ID for PUT';
- InvalidParam(AResponse,R);
- exit;
- end;
- IDx:=IndexOfID(ID);
- if IDX=-1 then
- begin
- NotFound(AResponse);
- exit;
- end;
- O:=GetObject(ARequest,R);
- if (O=Nil) then
- begin
- Writeln('Bad PUT request: ',aRequest.Content,' : ',R);
- InvalidParam(AResponse,R);
- exit;
- end;
- D:=Data.Objects[Idx];
- if O.IndexOfName('Name')<>-1 then
- D.Strings['Name']:=O.Strings['Name'];
- if O.IndexOfName('Population')<>-1 then
- D.Int64s['Population']:=O.Int64s['Population'];
- SendRecords(Idx,Idx,AResponse);
- end;
- Procedure DeleteRecord(aRequest : TRequest; AResponse : TResponse);
- Var
- ID,Idx : Integer;
- R : String;
- begin
- ID:=GetRequestID(ARequest);
- if ID<0 then
- begin
- R:='Need ID for DELETE';
- InvalidParam(AResponse,R);
- exit;
- end;
- ID:=GetRequestID(ARequest);
- IDx:=IndexOfID(ID);
- if IDX=-1 then
- begin
- NotFound(AResponse);
- exit;
- end;
- Data.Delete(Idx);
- AResponse.Code:=204;
- AResponse.CodeText:='No content';
- AResponse.SendResponse;
- end;
- Procedure HandleRest(aRequest : TRequest; AResponse : TResponse);
- begin
- Writeln('Method : ',ARequest.Method);
- Case UpperCase(ARequest.Method) of
- 'GET': GetRecords(ARequest,AResponse);
- 'POST': CreateRecord(ARequest,AResponse);
- 'PUT': UpdateRecord(ARequest,AResponse);
- 'DELETE': DeleteRecord(ARequest,AResponse);
- else
- Raise EHTTP.CreateHelp('Method not allowed',405);
- end;
- end;
- initialization
- LoadRestData;
- finalization
- DoneRestData;
- end.
|