Has anybody written a session example for Lazarus web? lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- egsuh · 2023-07-23 08:28 I know that there are examples on the FPC wiki written by Michael, but this is little bit tricky to use. Has anybody written a session management codes? ---------------------------------------------------------------------- Thaddy · 2023-07-23 08:49 There is an example in the fcl-web directory examples directories ---------------------------------------------------------------------- egsuh · 2023-07-23 10:18 Thank you for your information. I could find the example files and run it. But the unit websession says, unit websession deprecated; Any problem with this or are there any new approaches? ---------------------------------------------------------------------- Warfley · 2023-07-23 10:42 I usually build a simplified session system myself. Generate a random session ID, set it as a cookie and store it in a database together with all the session data you want to associate with it. If you use a database anyway this is not too much to add the session to it. If you need a bit more security you can add additional data to the session data, e.g. form a hash over Session ID (Cookie) + IP + UserAgent, etc. But if it's not such a critical system a simple cookie should suffice ---------------------------------------------------------------------- egsuh · 2023-07-23 23:56 Yes. I don’t think treating session myself is too difficult. Just checking ways to use existing ones. Using ini files does not seem too bad. Or I may change writing/reading part of inissession unit. ---------------------------------------------------------------------- Leledumbo · 2023-07-24 05:15 If you use fpWeb, go here. Otherwise, here's a purer fcl-web example: Code: Pascal [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode objfpc} uses classes, fphttpapp, httpdefs, httproute, fphttp, iniwebsession; var LSession: TCustomSession; procedure Session(ARequest: TRequest; AResponse: TResponse);var LSessionValue: String;begin if not Assigned(LSession) then begin LSession := SessionFactory.CreateSession(ARequest); if Assigned(LSession) then LSession.InitSession(ARequest, nil, nil); end; if UpCase(ARequest.Method) = 'POST' then begin if ARequest.ContentFields.Values['delete'] <> '' then begin // workaround since HTML forms don't accept methods other than GET or POST SessionFactory.DoneSession(LSession); end else if ARequest.ContentFields.Values['delete_value'] <> '' then begin LSession.RemoveVariable('SessionValue') end else LSession.Variables['SessionValue'] := ARequest.ContentFields.Values['val']; end; AResponse.SendRedirect('/');end; procedure Home(ARequest: TRequest; AResponse: TResponse);var LSessionValue: String;begin if not Assigned(LSession) then begin LSession := SessionFactory.CreateSession(ARequest); if Assigned(LSession) then LSession.InitSession(ARequest, nil, nil); // the nils are event handlers, which is not needed in this example end; LSessionValue := LSession.Variables['SessionValue']; if LSessionValue = '' then LSessionValue := '
You have no session value stored on this server
' else LSessionValue := 'Your session value: ' + LSessionValue + '
'; AResponse.Content := 'Your session ID: ' + LSession.SessionID + '
' + LSessionValue + '' + '' + '' ;end; begin HTTPRouter.RegisterRoute('/',@Home); HTTPRouter.RegisterRoute('/session',@Session); Application.Port := 9000; Application.Initialize; Application.Run;end. ---------------------------------------------------------------------- egsuh · 2023-07-25 11:03 I see the overall logic is quite simple. But there are difficulties in details. I made a simple program myself. bdbSessions is a TBufDataSet. I'd like to save session data in this file. Code: Pascal [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TSessionModule.DataModuleCreate(Sender: TObject);begin bdbSessions.LoadFromFile; bdbSessions.Active := True; TimeOut := 5; // seconds, for testing Randomize; OutText := '';end; procedure TSessionModule.DataModuleDestroy(Sender: TObject);begin bdbSessions.SaveToFile; bdbSessions.Close;end; procedure TSessionModule.DataModuleBeforeRequest(Sender: TObject; ARequest: TRequest);var V : string; C: TCookie; T: TDateTime; begin V := trim(ARequest.CookieFields.Values[SessionIDName]); if V <> '' then begin if bdbSessions.Locate('SessionID', VarArrayOf([V]), []) then begin LA := bdbSessions.FieldByName('LastAccess').AsDateTime; TimeOut := bdbSessions.FieldByName('TimeOut').AsInteger; Gap := SecondsBetween(Now, LA); if Gap >= TimeOut then begin bdbSessions.Delete; bdbSessions.Post; V := ''; end else begin // In Session --- Update current time bdbSessions.Edit; bdbSessions.FieldByName('LastAccess').AsDateTime := Now; bdbSessions.Post; end; end else V := ''; end; if V = '' then begin Randomize; SessionID := IntToStr(Random(10000)); // CreateNewSessionID; with bdbsessions do begin // Save New Session Insert; FieldByName('SessionId').AsString := SessionID; FieldByName('LastAccess').AsDateTime := Now; FieldByName('TimeOut').AsInteger := TimeOut; Post; end; // TODO: Process New Session end else begin SessionID := V; // TODO: ProcessInSession; end;end; procedure TSessionModule.DataModuleRequest(Sender: TObject; ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);var C: TCookie;begin // TimeOut := 60; // 1 minute AResponse.Cookies.Clear; with AResponse.Cookies.Add do begin Name := SessionIDName; Value:= SessionID; SameSite:= ssStrict; Expires:= Now + (TimeOut * OneSecond); end; AResponse.Content := Format('SessionID:%s, LastAccess:%s, Time gap: %d seconds', [SessionID, DateTimeToStr(LA), Gap]); Handled := True;end; Each module does not have any problem when run in Windows application mode, but difficult to check in CGI mode. Having difficulty in reading BufDataset file, and LastAccess time does not seem to be read correctly. But generally a little fine tuning would do, I expect. ---------------------------------------------------------------------- Leledumbo · 2023-07-25 12:02 Quote from: egsuh on July 25, 2023, 01:03:15 pmbdbSessions is a TBufDataSet. I'd like to save session data in this file. This, as well as other approaches, are indeed possible. Since session is no weirder than associating a cookie as a key with a bunch of items you save on the server, only creativity seems to be the limitation, as well as providing a way to CRUD both the key and data. Like iniwebsession implements websession interface, you can make something like bufdatasetwebsession and use TBufDataSet as the backend storage instead of an ini file, the remaining code I wrote doesn't have to change apart from the iniwebsession in uses clause. The way you do it without abstraction ties the implementation making it hard to switch to a different backend, but of course, you choose your own way. I object using any db as a session storage, as it's slow as hell. I sped up my current company's application when I joined them by moving the session handling from db to redis. This increases the rps significantly as redis does everything in memory, and session, by definition is a perfect entity to be stored in memory. It should always be OK for a session to be expired even before it's determined time. If it matters, something is not right in the application design. ---------------------------------------------------------------------- egsuh · 2023-07-26 02:09 Quote redis does everything in memory, and session, by definition is a perfect entity to be stored in memory. But can I use Redis in FPC/Lazarus? Actually I'm having some trouble with BufDataSet(writing to and reading from file). Not sure whether the table structure is stored securely. Instead I'm thinking using SQLite.. but this requires another installation of something, however simple it is. ---------------------------------------------------------------------- PierceNg · 2023-07-26 02:44 Quote from: egsuh on July 26, 2023, 04:09:56 amQuote redis does everything in memory, and session, by definition is a perfect entity to be stored in memory. But can I use Redis in FPC/Lazarus? Actually I'm having some trouble with BufDataSet(writing to and reading from file). Not sure whether the table structure is stored securely. Instead I'm thinking using SQLite.. but this requires another installation of something, however simple it is. There are several recent-ish Redis for Delphi repos on Github. Searching for 'redis fpc pascal' yields one result: https://github.com/ik5/redis_client.fpc, 12 years old, but if it works it works. SQLite is always worth learning for a programmer. Ultimately, depends on your objectives: If you are doing this to learn and improve your skills, then try as many things as possible. If you need to get something done, then using some existing framework should be quicker. OTOH usually the more comprehensive and powerful a framework is, the longer it takes to learn to use it well. ---------------------------------------------------------------------- egsuh · 2023-07-26 03:22 Definitely I’m not learning anything. ---------------------------------------------------------------------- Leledumbo · 2023-07-26 04:36 Quote from: egsuh on July 26, 2023, 04:09:56 amBut can I use Redis in FPC/Lazarus? There are existing clients, but the relatively new one sucks in licensing. I'm currently writing a new one I will contribute and will use the same license as other core FPC team contributed units, which should be LGPL with static linking exception. However, if you don't care about licensing, the one I linked will do (just needs a little command line parameter -k'-l