Delphi Web Server 流程分析

通过向导 "Web Server Application" (选择 "Stand-alone GUI Application") 创建一个 WebServer Demo。

主单元代码:

......

  private
    FServer: TIdHTTPWebBrokerBridge;
    procedure StartServer;

.............

Delphi的网络组件是基于INDY的,这里的FServer是TIdHTTPWebBrokerBridge,就是WebServer了。是什么呢?

直接"Ctrl+Click"浏览代码,直接报错:

Delphi Web Server 流程分析_第1张图片

哎........

手动打开单元 "IdHTTPWebBrokerBridge.pas"(路径:"D:\Program Files (x86)\Embarcadero\Studio\22.0\source\Indy10\Protocols"):

TIdHTTPWebBrokerBridge = class(TIdCustomHTTPServer),

先看看继承:

TIdHTTPWebBrokerBridge->TIdCustomHTTPServer->TIdCustomTCPServer->TIdComponent...

是从TCPServer来的。

再看主单元的StartServer方法,是如何启动Server的:

procedure TForm1.StartServer;
begin
  if not FServer.Active then
  begin
    FServer.Bindings.Clear;
    FServer.DefaultPort := StrToInt(EditPort.Text);
    FServer.Active := True;
  end;
end;

设置Active=True启动,(这里可以"Ctrl+Click" "Active"属性),直接到"IdCustomTCPServer.pas"的 " TIdCustomTCPServer"类,看代码:

SetActive -> Startup():


procedure TIdCustomTCPServer.Startup;
var
  LScheduler: TIdScheduler;
  LIOHandler: TIdServerIOHandler;
  {$IFDEF CanCreateTwoBindings}
  LBinding: TIdSocketHandle;
  {$ENDIF}
begin
  // Set up bindings
  if Bindings.Count = 0 then begin
                                                                        
    // Binding object that supports both IPv4 and IPv6 on the same socket...

    {$IFDEF CanCreateTwoBindings}LBinding := {$ENDIF}Bindings.Add; // IPv4 or IPv6 by default

    {$IFDEF CanCreateTwoBindings}
                                                                                  
    case LBinding.IPVersion of
      Id_IPv4: begin
        if GStack.SupportsIPv6 then begin
          Bindings.Add.IPVersion := Id_IPv6;
        end;
      end;
      Id_IPv6: begin
        if GStack.SupportsIPv4 then begin
          Bindings.Add.IPVersion := Id_IPv4;
        end;
      end;
    end;
    {$ENDIF}
  end;

  // Setup IOHandler
  LIOHandler := FIOHandler;
  if not Assigned(LIOHandler) then begin
    LIOHandler := TIdServerIOHandlerStack.Create(Self);
    SetIOHandler(LIOHandler);
    FImplicitIOHandler := True;
  end;
  LIOHandler.Init;

  // Set up scheduler
  LScheduler := FScheduler;
  if not Assigned(FScheduler) then begin
    LScheduler := TIdSchedulerOfThreadDefault.Create(Self);
    SetScheduler(LScheduler);
    FImplicitScheduler := True;
    // Useful in debugging and for thread names
    LScheduler.Name := Name + 'Scheduler';   {do not localize}
  end;
  LScheduler.Init;

  StartListening;
end;

这里添加了ip6支持,然后设置IO句柄(用于https,加密,压缩等),然后是设置Scheduler(用于设置线程或纤程)。然后开始监听端口StartListening(),StartListening()主要是检查监听线程数量和创建监听线程,默认监听线程数量=15:

const
  IdListenQueueDefault = 15;

procedure TIdCustomTCPServer.InitComponent;

..........

FListenQueue := IdListenQueueDefault;

..................

这个数量太小了,现在的一般台式机都可以轻松支持200以上,所以建议开始时设置,

procedure TForm1.FormCreate(Sender: TObject);
begin
  FServer := TIdHTTPWebBrokerBridge.Create(Self);
  FServer.ListenQueue := 200;
end;

StartListening():

procedure TIdCustomTCPServer.StartListening;
var
  LListenerThreads: TIdListenerList;
  LListenerThread: TIdListenerThread;
  I: Integer;
  LBinding: TIdSocketHandle;
  LName: string;
begin
  LListenerThreads := FListenerThreads.LockList;
  try
    // Set up any sockets that are not already listening
    I := LListenerThreads.Count;
    try
      while I < Bindings.Count do begin
        LBinding := Bindings[I];
        LBinding.AllocateSocket;
        // do not overwrite if the default. This allows ReuseSocket to be set per binding
        if FReuseSocket <> rsOSDependent then begin
          LBinding.ReuseSocket := FReuseSocket;
        end;
        DoBeforeBind(LBinding);
        LBinding.Bind;
        LBinding.UseNagle := FUseNagle;
        Inc(I);
      end;
    except
      Dec(I); // the one that failed doesn't need to be closed
      while I >= 0 do begin
        Bindings[I].CloseSocket;
        Dec(I);
      end;
      raise;
    end;

    if I > LListenerThreads.Count then begin
      DoAfterBind;
    end;

    // Set up any threads that are not already running

    LName := Name;
    if LName = '' then begin
      LName := 'IdCustomTCPServer'; {do not localize}
    end;

    for I := LListenerThreads.Count to Bindings.Count - 1 do
    begin
      LBinding := Bindings[I];
      LBinding.Listen(FListenQueue);
      LListenerThread := TIdListenerThread.Create(Self, LBinding);
      try
        LListenerThread.Name := LName + ' Listener #' + IntToStr(I + 1); {do not localize}
        LListenerThread.OnBeforeRun := DoBeforeListenerRun;
                                                            
        //http://www.midnightbeach.com/jon/pubs/2002/BorCon.London/Sidebar.3.html
        LListenerThread.Priority := tpListener;
        LListenerThreads.Add(LListenerThread);
      except
        LBinding.CloseSocket;
        FreeAndNil(LListenerThread);
        raise;
      end;
      LListenerThread.Start;
    end;
  finally
    FListenerThreads.UnlockList;
  end;
end;

设置完线程,然后启动线程,就开始端口监听了,工作就转到了监听线程TIdListenerThread:

TIdListenerThread = class(TIdThread)

父类TIdThread处理一般情况,子类只要实现Run()抽象方法:


procedure TIdListenerThread.Run;
var
  LContext: TIdServerContext;
  LIOHandler: TIdIOHandler;
  LPeer: TIdTCPConnection;
  LYarn: TIdYarn;
begin
  Assert(Server<>nil);
  Assert(Server.IOHandler<>nil);

  LContext := nil;
  LPeer := nil;
  LYarn := nil;
  try
    // GetYarn can raise exceptions
    LYarn := Server.Scheduler.AcquireYarn;

                                                                       
    // the user to reject connections before they are accepted.  Somehow
    // expose an event here for the user to decide with...
    
    LIOHandler := Server.IOHandler.Accept(Binding, Self, LYarn);
    if LIOHandler = nil then begin
      // Listening has finished
      Stop;
      Abort;
    end else begin
      // We have accepted the connection and need to handle it
      LPeer := TIdTCPConnection.Create(nil);
      {$IFDEF USE_OBJECT_ARC}
      // under ARC, the TIdTCPConnection.IOHandler property is a weak reference.
      // TIdServerIOHandler.Accept() returns an IOHandler with no Owner assigned,
      // so lets make the TIdTCPConnection become the Owner in order to keep the
      // IOHandler alive when this method exits.
      //
                                                                           
      //
      LPeer.InsertComponent(LIOHandler);
      {$ENDIF}
      LPeer.IOHandler := LIOHandler;
      LPeer.ManagedIOHandler := True;
    end;

    // LastRcvTimeStamp := Now;  // Added for session timeout support
    // ProcessingTimeout := False;

    // Check MaxConnections
    if (Server.MaxConnections > 0) and (not Server.Contexts.IsCountLessThan(Server.MaxConnections)) then begin
      FServer.DoMaxConnectionsExceeded(LIOHandler);
      LPeer.Disconnect;
      Abort;
    end;

    // Create and init context
    LContext := Server.FContextClass.Create(LPeer, LYarn, Server.Contexts);
    LContext.FServer := Server;
    // We set these instead of having the context call them directly
    // because they are protected methods. Also its good to keep
    // Context indepent of the server as well.
    LContext.OnBeforeRun := Server.ContextConnected;
    LContext.OnRun := Server.DoExecute;
    LContext.OnAfterRun := Server.ContextDisconnected;
    LContext.OnException := Server.DoException;
    //
    Server.ContextCreated(LContext);
    //
    // If all ok, lets start the yarn
    Server.Scheduler.StartYarn(LYarn, LContext);
  except
    on E: Exception do begin
      // RLebeau 1/11/07: TIdContext owns the Peer by default so
      // take away ownership here so the Peer is not freed twice
      if LContext <> nil then begin
        TIdServerContextAccess(LContext).FOwnsConnection := False;
      end;
      FreeAndNil(LContext);
      FreeAndNil(LPeer);
      // Must terminate - likely has not started yet
      if LYarn <> nil then begin
        Server.Scheduler.TerminateYarn(LYarn);
      end;
      // EAbort is used to kick out above and destroy yarns and other, but
      // we dont want to show the user
                                                              
      // To ignore EIdConnClosedGracefully, for instance...
      if not (E is EAbort) then begin
        Server.DoListenException(Self, E);
      end;
    end;
  end;
end;

.......

    // Create and init context
    LContext := Server.FContextClass.Create(LPeer, LYarn, Server.Contexts);
    LContext.FServer := Server;
    // We set these instead of having the context call them directly
    // because they are protected methods. Also its good to keep
    // Context indepent of the server as well.
    LContext.OnBeforeRun := Server.ContextConnected;
    LContext.OnRun := Server.DoExecute;
    LContext.OnAfterRun := Server.ContextDisconnected;
    LContext.OnException := Server.DoException;
    //
    Server.ContextCreated(LContext);
    //
    // If all ok, lets start the yarn
    Server.Scheduler.StartYarn(LYarn, LContext);

通过上下文的事件关联到了Server的执行方法。主要是LContext.OnRun := Server.DoExecute;

TIdCustomTCPServer.DoExecute 没做什么事,就检查了tcpConnected,具体工作在子类。

TIdCustomHTTPServer.DoExecute,这个方法是个复杂的处理(代码太长了,不贴了),主要是循环处理各种HTTP 方法,解析请求头,判断请求类别,归类参数等等,然后调用DoCommandGet(子类通过此方法来具体处理),最后给客户端返回响应Response,直到连接断开。

我们的子类是TIdHTTPWebBrokerBridge,看看其DoCommandGet():


procedure TIdHTTPWebBrokerBridge.DoCommandGet(AThread: TIdContext;
 ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
  if FWebModuleClass <> nil then begin
    // FWebModuleClass, RegisterWebModuleClass supported for backward compatability
    RunWebModuleClass(AThread, ARequestInfo, AResponseInfo)
  end else
  begin
    {$IFDEF HAS_CLASSVARS}
    TIdHTTPWebBrokerBridgeRequestHandler.FWebRequestHandler.Run(AThread, ARequestInfo, AResponseInfo);
    {$ELSE}
    IndyWebRequestHandler.Run(AThread, ARequestInfo, AResponseInfo);
    {$ENDIF}
  end;
end;

开始时,FWebModuleClass = nil;

所以执行的是:

TIdHTTPWebBrokerBridgeRequestHandler.FWebRequestHandler.Run(AThread, ARequestInfo, AResponseInfo);


type
  TIdHTTPWebBrokerBridgeRequestHandler = class(TWebRequestHandler)
  {$IFDEF HAS_CLASSVARS}
  private
   class var FWebRequestHandler: TIdHTTPWebBrokerBridgeRequestHandler;
  {$ENDIF}
  public
    constructor Create(AOwner: TComponent); override;
    {$IFDEF HAS_CLASSVARS}
      {$IFDEF HAS_CLASSDESTRUCTOR}
    class destructor Destroy;
      {$ENDIF}
    {$ENDIF}
    destructor Destroy; override;
    procedure Run(AThread: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
  end;

这个辅助类是从TWebRequestHandler继承的,其类变量FWebRequestHandler的赋值是通过独立局部函数:
function IdHTTPWebBrokerBridgeRequestHandler: TWebRequestHandler;
begin
  {$IFDEF HAS_CLASSVARS}
  if not Assigned(TIdHTTPWebBrokerBridgeRequestHandler.FWebRequestHandler) then
    TIdHTTPWebBrokerBridgeRequestHandler.FWebRequestHandler := TIdHTTPWebBrokerBridgeRequestHandler.Create(nil);
  Result := TIdHTTPWebBrokerBridgeRequestHandler.FWebRequestHandler;
  {$ELSE}
  if not Assigned(IndyWebRequestHandler) then
    IndyWebRequestHandler := TIdHTTPWebBrokerBridgeRequestHandler.Create(nil);
  Result := IndyWebRequestHandler;
  {$ENDIF}
end;

一般的运用都有编译开关HAS_CLASSVARS,所以执行的是:

  if not Assigned(TIdHTTPWebBrokerBridgeRequestHandler.FWebRequestHandler) then
    TIdHTTPWebBrokerBridgeRequestHandler.FWebRequestHandler := TIdHTTPWebBrokerBridgeRequestHandler.Create(nil);
  Result := TIdHTTPWebBrokerBridgeRequestHandler.FWebRequestHandler;

此函数是在单元的初始化调用,并赋值给了WebReq单元的全局变量WebRequestHandlerProc。

initialization
  WebReq.WebRequestHandlerProc := IdHTTPWebBrokerBridgeRequestHandler;

所以TIdHTTPWebBrokerBridgeRequestHandler.FWebRequestHandler.Run()就相当于

TIdHTTPWebBrokerBridgeRequestHandler.Run()

这里的类变量FWebRequestHandler是为了实现单例模式。

通过如此处理,就把TIdHTTPWebBrokerBridge.DoCommandGet()转到了TIdHTTPWebBrokerBridgeRequestHandler.Run()来处理,这里用了桥接模式。

也就是从TIdCustomHTTPServer转接到了TWebRequestHandler。

注:

TIdHTTPWebBrokerBridge->TIdCustomHTTPServer

TIdHTTPWebBrokerBridgeRequestHandler->TWebRequestHandler

具体看看TIdHTTPWebBrokerBridgeRequestHandler.Run():


procedure TIdHTTPWebBrokerBridgeRequestHandler.Run(AThread: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
  LRequest: TIdHTTPAppRequest;
  LResponse: TIdHTTPAppResponse;
begin
  try
    LRequest := TIdHTTPAppRequest.Create(AThread, ARequestInfo, AResponseInfo);
    try
      LResponse := TIdHTTPAppResponse.Create(LRequest, AThread, ARequestInfo, AResponseInfo);
      try
        // WebBroker will free it and we cannot change this behaviour
        AResponseInfo.FreeContentStream := False;
        HandleRequest(LRequest, LResponse);
      finally
        FreeAndNil(LResponse);
      end;
    finally
      FreeAndNil(LRequest);
    end;
  except
    // Let Indy handle this exception
    raise;
  end;
end;

这里通过监听线程获得的XXXInfo参数构造了Request和Response,并调用了父类的HandleRequest(Web.WebReq.pas):


function TWebRequestHandler.HandleRequest(Request: TWebRequest;
  Response: TWebResponse): Boolean;
var
  I: Integer;
  LWebModule: TComponent;
  LWebAppServices: IWebAppServices;
  LGetWebAppServices: IGetWebAppServices;
  LComponent: TComponent;
begin
  Result := False;
  LWebModule := ActivateWebModules;
  if Assigned(LWebModule) then
  try
    try
      if Supports(IInterface(LWebModule), IGetWebAppServices, LGetWebAppServices) then
        LWebAppServices := LGetWebAppServices.GetWebAppServices;
      if LWebAppServices = nil then
        for I := 0 to LWebModule.ComponentCount - 1 do
        begin
          LComponent := LWebModule.Components[I];
          if Supports(LComponent, IWebAppServices, LWebAppServices) then
            if LWebAppServices.Active then
              break
            else
              LWebAppServices := nil;
        end;
      if LWebAppServices = nil then
        LWebAppServices := TDefaultWebAppServices.Create;
      LWebAppServices.InitContext(LWebModule, Request, Response);
      try
        try
          Result := LWebAppServices.HandleRequest;
        except
          ApplicationHandleException(LWebAppServices.ExceptionHandler);
        end;
      finally
        LWebAppServices.FinishContext;
      end;
      if Result and not Response.Sent then
        Response.SendResponse;
    except
      ApplicationHandleException(LWebAppServices.ExceptionHandler);
    end;
  finally
    DeactivateWebModules(LWebModule);
  end;
end;

第一步:

LWebModule := ActivateWebModules;

这个是获取当前活动的Modules。

题外话:Delphi的WebBroker技术经过了多年的发展,随着大环境的发展和变化,其实现方式也不断扩展和进步。系统维护一个Module Pools,单有一个新的客户端请求Request时,就分配一个Modules。

文档:

TWebRequestHandler maintains the Web modules in an application and creates request and response objects when the application receives HTTP request messages.

TWebRequestHandler keeps a pool of active Web modules. In response to a request from the application, TWebRequestHandler creates a request object and assigns it to one of the active Web modules

ActivateWebModules()代码:


function TWebRequestHandler.ActivateWebModules: TComponent;
begin
  if (FMaxConnections > 0) and (FAddingActiveModules >= FMaxConnections) then
    raise EWebBrokerException.CreateRes(@sTooManyActiveConnections);

  FCriticalSection.Enter;
  try
    FAddingActiveModules := FActiveWebModules.Count + 1;
    try
      if (FMaxConnections > 0) and (FActiveWebModules.Count >= FMaxConnections) then
        raise EWebBrokerException.CreateRes(@sTooManyActiveConnections);
      if FInactiveWebModules.Count > 0 then
      begin
        Result := FInactiveWebModules[0];
        FInactiveWebModules.Extract(Result);
        FActiveWebModules.Add(Result);
      end
      else
      begin
        if WebModuleClass <> nil then
        begin
          Result := WebModuleClass.Create(nil);
          FActiveWebModules.Add(Result);
        end
        else
          raise EWebBrokerException.CreateRes(@sNoDataModulesRegistered);
      end;
    finally
      FAddingActiveModules := 0;
    end;
  finally
    FCriticalSection.Leave;
  end;
end;

Module的最大数量=MaxConnections。

这里的WebModuleClass,可以是TWebModule类,如果是旧项目升级,也可以是TDataMudule+TWebDispatcher, DEMO里WebModule单元的全局变量:

var
  WebModuleClass: TComponentClass = TWebModule1;

确定了这个WebModuleClass的类型,所以这里是传类别,不是实例。

WebModuleClass,("if WebModuleClass <> nil then"),这个属性的赋值,我们没有明显的看到在哪里对FWebModuleClass进行赋值,实际这个属性是在外部进行赋值的,查看Demo的项目代码:

program WebServerTest;
{$APPTYPE GUI}

uses
  Vcl.Forms,
  Web.WebReq,
  IdHTTPWebBrokerBridge,
  FormUnit1 in 'FormUnit1.pas' {Form1},
  WebModuleUnit1 in 'WebModuleUnit1.pas' {WebModule1: TWebModule};

{$R *.res}

begin
  if WebRequestHandler <> nil then
    WebRequestHandler.WebModuleClass := WebModuleClass;
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

  if WebRequestHandler <> nil then
    WebRequestHandler.WebModuleClass := WebModuleClass;

这里的WebRequestHandler是个“变量型函数”,定义在Web.WebReq.pas:

  function WebRequestHandler: TWebRequestHandler;

..............
function WebRequestHandler: TWebRequestHandler;
begin
  if Assigned(WebRequestHandlerProc) then
    Result := WebRequestHandlerProc
  else
    Result := nil;
end;

梳理下:

首先IdHTTPWebBrokerBridge.pas的初始化部分:

initialization
  WebReq.WebRequestHandlerProc := IdHTTPWebBrokerBridgeRequestHandler; 

WebReq.pas声明函数指针WebRequestHandlerProc:

var
  WebRequestHandlerProc: function: TWebRequestHandler = nil;

使得WebReq单元的WebRequestHandlerProc函数指针指向IdHTTPWebBrokerBridgeRequestHandler(),

执行  if WebRequestHandler <> nil then,调用了function WebRequestHandler: TWebRequestHandler,然后调用IdHTTPWebBrokerBridgeRequestHandler(),获得了TWebRequestHandler的实例,然后设置此实例的WebModuleClasss属性值:

WebRequestHandler.WebModuleClass := WebModuleClass;

此实例间接保存在全局函数指针WebRequestHandlerProc。

第二步:

判断WebModuleClass本身是否支持IGetWebAppServices,如果不支持,就检查WebModuleClass里的组件是否支持IWebAppServices,比如前面说的TDataMudule+TWebDispatcher,就通过TWebDispatcher获得。

没有组件支持IWebAppServices,就进行下一步,直接创建了默认的WebServices:

LWebAppServices := TDefaultWebAppServices.Create;

注: 我们可以试试,如果拖一个TWebDispatcher到TWebModule界面,就提示错误信息:“TWebDispatcher”组件只能添加到TDataModule或TForm,服务器只能有一个WebDispatcher,文档里也强调了,服务器只能有一个TWebModule类,这个是强制要求。

获取LWebAppServices后,初始化LWebAppServices的上下文:

LWebAppServices.InitContext(LWebModule, Request, Response);

然后调用LWebAppServices.HandleRequest,这样就到了真正处理Service的地方了。

这里我们看看TDefaultWebAppServices.HandleRequest(Web.HTTPApp.pas):


function TDefaultWebAppServices.HandleRequest: Boolean;
begin
  Result := InvokeDispatcher;
end;


function TDefaultWebAppServices.InvokeDispatcher: Boolean;
begin
  if RequestHandler <> nil then
  begin
    Result := RequestHandler.HandleRequest(Request, Response);
  end
  else
    raise EWebBrokerException.CreateRes(@sNoDispatcherComponent);
end;


function TDefaultWebAppServices.GetRequestHandler: IWebRequestHandler;
begin
  if FRequestHandler = nil then
    FRequestHandler := FindRequestHandler;
  Result := FRequestHandler;
end;

function TDefaultWebAppServices.FindRequestHandler: IWebRequestHandler;
var
  Component: TComponent;
begin
  Result := nil;
  Component := FindWebDispatcher;
  if Component <> nil then
    if not Supports(Component, IWebRequestHandler, Result) then
      Assert(False, 'Expect support for IWebRequestHandler');  { do not localize }
end;

function TDefaultWebAppServices.FindWebDispatcher: TComponent;
var
  J: Integer;
begin
  Result := nil;
  if WebModule is TCustomWebDispatcher then
    Result := WebModule
  else
    for J := 0 to WebModule.ComponentCount - 1 do
      if WebModule.Components[J] is TCustomWebDispatcher then
      begin
        Result := WebModule.Components[J];
        break;
      end;
end;

在InvokeDispatcher()方法里,接口RequestHandler(类型为IWebRequestHandler)是通过GetRequestHandler()获得,注意GetRequestHandler()的实现,FRequestHandler是个单例变量。

通过FindWebDispatcher()方法,最终找到实现了IWebRequestHandler接口的组件,也就是TWebModule或者TDataModule里的组件TWebDispatcher。

TWebModule和TWebDispatcher都是TCustomWebDispatcher的子类。

通过RequestHandler.HandleRequest(Request, Response)转到TCustomWebDispatcher.HandleRequest():


function TCustomWebDispatcher.HandleRequest(
  Request: TWebRequest; Response: TWebResponse): Boolean;
begin
  FRequest := Request;
  FResponse := Response;
  Result := DispatchAction(Request, Response);
end;

到这里,我们看到了"Action"字眼,爬山涉水的,露出了曙光。。。

瞧瞧DispatchAction():


function TCustomWebDispatcher.DispatchAction(Request: TWebRequest;
  Response: TWebResponse): Boolean;
var
  I: Integer;
  Action, Default: TWebActionItem;
  Dispatch: IWebDispatch;
begin
  FRequest := Request;
  FResponse := Response;
  I := 0;
  Default := nil;
  if Response.Sent then
  begin
    Result := True;
    { Note that WebSnapSvr enabled apps have no way to mark response as sent }
    Exit;
  end;
  Result := DoBeforeDispatch(Request, Response) or Response.Sent;
  while not Result and (I < FActions.Count) do
  begin
    Action := FActions[I];
    Result := Action.DispatchAction(Request, Response, False);
    if Action.Default then Default := Action;
    Inc(I);
  end;
  // Dispatch to self registering components
  I := 0;
  while not Result and (I < FDispatchList.Count) do
  begin
    if Supports(IInterface(FDispatchList.Items[I]), IWebDispatch, Dispatch) then
    begin
      Result := DispatchHandler(Self, Dispatch,
        Request, Response, False);
    end;
    Inc(I);
  end;

  if not Result and Assigned(Default) then
    Result := Default.DispatchAction(Request, Response, True);
  if Result and not Response.Sent then
    Result := DoAfterDispatch(Request, Response);

end;

用户可以在TWebModule里添加动作,就是熟悉的Route->Action的那个"Action"。

首先判断本次会话是否已经完成,如代码:

  if Response.Sent then
  begin
    Result := True;
    { Note that WebSnapSvr enabled apps have no way to mark response as sent }
    Exit;
  end;

是否已经发送完Response;


  Result := DoBeforeDispatch(Request, Response) or Response.Sent;
注意这一行代码!!!这里可以让我们有机会插入请求处理过程及结果。嗯,我们可以在这里"截胡"。

DoBeforeDispatch()调用OnBeforeDispatch
procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest;
  Response: TWebResponse; var Handled: Boolean);
begin
//可以预处理或者完全处理,

//Handled := True会中段后续的处理

//调用Response.Send(),会中段后续的处理
end;

如果前面没有中断处理,则会继续处理后续的"Action",如果有的话。

到这里流程就基本明白了,如何从获取用户的请求,到我们在Server定制的功能。

你可能感兴趣的:(Delphi,WebBroker,WebServer)