TsgcWebSocketHTTPServer | 会话

HTTP 是一种无状态协议,客户端请求文件,服务器发送响应,连接随即关闭。会话允许您存储关于客户端的信息,例如可在客户端登录期间使用。会话可以在一段时间不活动后销毁,也可以在客户端注销后手动销毁。

 

配置

TsgcWebSocketHTTPServer 中有一些属性用于在服务器组件中启用/禁用会话。最重要的属性有:

 

属性 描述
SessionState T这是必须启用的第一个属性,以便使用会话。如果不启用此属性,会话将无法工作

SessionTimeout

此处必须设置大于零的值(以毫秒为单位),作为会话保持活跃的最长时间。
AutoStartSession 会话可以自动创建(AutoStartSession = true)或手动创建(AutoStartSession = false)。如果自动创建会话,服务器将使用 RemoteIP 作为唯一标识符检查是否有活跃的存储会话。
SessionClass 可选。服务器创建新会话时使用的类。将其设置为您自己的 TIdHTTPSession 子类,以在每个会话中存储您自己的数据。必须在服务器激活之前设置。
SessionList 保存活跃会话的列表。读取它可通过代码搜索、创建或移除会话。如果您需要完全控制会话的存储位置,也可以分配您自己的列表。必须在服务器激活之前分配。

 

 


TsgcWebSocketHTTPServer1.SessionState := True;
TsgcWebSocketHTTPServer1.SessionTimeout := 600000;
TsgcWebSocketHTTPServer1.AutoStartSession := False;

 

创建会话

要创建新会话,必须创建一个唯一的新会话 ID。您可以使用任何值。示例:如果客户端正在验证身份,您可以使用 user + password + remoteip 作为会话 ID。

然后,搜索会话列表以检查其是否已存在。如果不存在,则创建一个新会话。

 

创建新会话时,将调用 OnSessionStart 事件;会话关闭时,将触发 OnSessionEnd 事件。

 


procedure OnCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; 
  AResponseInfo: TIdHTTPResponseInfo);
var
  vID: String;
  oSession: TIdHTTPSession;
begin
  if ARequestInfo.Document = '/' then
    AResponseInfo.ServeFile(AContext, 'yourpathhere\index.html')
  else
  begin
    // check if user is valid
    if not ((ARequestInfo.AuthUsername = 'user') and (ARequestInfo.AuthPassword = 'pass')) then
      AResponseInfo.AuthRealm := 'Authenticate'
    else
    begin
      // create a new session id with authentication data
      vID := ARequestInfo.AuthUsername + '_' + ARequestInfo.AuthPassword + '_' + ARequestInfo.RemoteIP;
 
      // search session
      oSession := TsgcWebSocketHTTPServer1.SessionList.GetSession(vID, ARequestInfo.RemoteIP);
 
      // create new session if not exists
      if not Assigned(oSession) then
        oSession := TsgcWebSocketHTTPServer1.SessionList.CreateSession(ARequestInfo.RemoteIP, vID);
 
      AResponseInfo.ContentText := '<html><head></head><body>Authenticated</body></html>';
      AResponseInfo.ResponseNo := 200;
    end;
  end;
end;

读取当前会话

一旦会话存在,服务器就会将其附加到每个携带会话 Cookie 的请求上。请从 ARequestInfo.Session 读取它,当请求没有会话时它为 nil。


procedure OnCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo;
  AResponseInfo: TIdHTTPResponseInfo);
begin
  if Assigned(ARequestInfo.Session) then
  begin
    // Content is a TStrings you can use to store your own values
    ARequestInfo.Session.Content.Values['visits'] :=
      IntToStr(StrToIntDef(ARequestInfo.Session.Content.Values['visits'], 0) + 1);

    AResponseInfo.ContentText := 'Session ' + ARequestInfo.Session.SessionID +
      ' visits: ' + ARequestInfo.Session.Content.Values['visits'];
  end
  else
    AResponseInfo.ContentText := 'No session';
end;

使用您自己的会话类

如果您希望在每个会话中保留自己的字段,而不使用 Content 字符串列表,请创建 TIdHTTPSession 的子类,并通过 SessionClass 属性告知服务器使用它。请在服务器激活之前进行设置。

服务器会继续处理其他所有事务:生成唯一的会话 ID、发送会话 Cookie、应用 SessionTimeout 并移除过期会话。如果您希望在创建会话时初始化自己的字段,请重写虚构造函数 CreateInitialized


type
  TMySession = class(TIdHTTPSession)
  private
    FUserName: String;
    FLoginTime: TDateTime;
  public
    constructor CreateInitialized(AOwner: TIdHTTPCustomSessionList;
      const SessionID, RemoteIP: string); override;
    property UserName: String read FUserName write FUserName;
    property LoginTime: TDateTime read FLoginTime write FLoginTime;
  end;

constructor TMySession.CreateInitialized(AOwner: TIdHTTPCustomSessionList;
  const SessionID, RemoteIP: string);
begin
  inherited CreateInitialized(AOwner, SessionID, RemoteIP);
  FLoginTime := Now;
end;

// configure the server before it starts
TsgcWebSocketHTTPServer1.SessionState := True;
TsgcWebSocketHTTPServer1.SessionTimeout := 600000;
TsgcWebSocketHTTPServer1.SessionClass := TMySession;
TsgcWebSocketHTTPServer1.Active := True;

// and read it back in any request
procedure OnCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo;
  AResponseInfo: TIdHTTPResponseInfo);
begin
  if ARequestInfo.Session is TMySession then
    AResponseInfo.ContentText := TMySession(ARequestInfo.Session).UserName;
end;

使用您自己的会话列表

如果您需要完全控制会话的存储方式,例如将它们保存在数据库中或在多个服务器之间共享,请在服务器激活之前将您自己的列表分配给 SessionList 属性。从 TIdHTTPDefaultSessionList 派生子类并重写虚方法 CreateSession。服务器会从 CreateUniqueSession 内部调用它,因此仍会为您生成唯一的会话 ID。

当您分配自己的 SessionList 时,SessionClass 属性将不再适用,因为由您的列表决定要创建哪个类。


type
  TMySessionList = class(TIdHTTPDefaultSessionList)
  public
    function CreateSession(const RemoteIP, SessionID: string)
      : TIdHTTPSession; override;
  end;

function TMySessionList.CreateSession(const RemoteIP, SessionID: string)
  : TIdHTTPSession;
begin
  Result := TMySession.CreateInitialized(Self, SessionID, RemoteIP);
  SessionList.Add(Result);
end;

// assign it before the server starts
TsgcWebSocketHTTPServer1.SessionList := TMySessionList.Create(nil);
TsgcWebSocketHTTPServer1.Active := True;

关于 OnCreateSession 的说明

OnCreateSession 事件允许您自行返回会话实例,但它不会为其分配会话 ID。如果您使用普通构造函数创建会话,会话 ID 和会话 Cookie 都将为空,并且在下一次请求时永远无法再次找到该会话。请改用 SessionClass,它更简单,并会为您处理所有这些工作。

此外,请勿在 OnCreateSession 内调用 SessionList.CreateUniqueSession。该方法已经将新会话添加到列表中,而当您的处理程序返回时服务器会再次添加它,因此同一个会话最终会在列表中出现两次。