DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: cuiqingbo
今日帖子: 2
在线用户: 5
导航: 论坛 -> Web应用开发 斑竹:bodies  
作者:
男 jacky19451685 (jacky168) ▲△△△△ -
普通会员
2024/3/21 9:37:44
标题:
请教WEB服务器端开发 浏览:1189
加入我的收藏
楼主: 一直有在用delphi做传统桌面开发, 最有个项目想做成所谓BS架构, 用户通过浏览器登陆,就像我们通过浏览器设置路由器参数一样的界面,实现简单的界面操作。比如通过输入192.168.0.1 在浏览器的界面点击一个按钮, 服务端的程序界面上可以出现‘HELLP WORLD'。 不知该如何入手, 请有经验的前辈,给指点下
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2024/3/21 10:06:44
1楼: 这个,首先是你要学 WEB 开发的原理。而学原理,用 Delphi 的 WebBroker 框架,是最容易理解 WEB 原理的。

页面按钮(代码就是 HTML 里面的一个 <input type="button">,HTML 代码由浏览器解释后执行,比如这个按钮 click 以后会做什么) --> 浏览器向 Web Server(比如 IIS 或者 Apache)发起一个 TCP 连接 --> 通过 TCP 连接发送一个 HTTP 命令(HTTP 命令都是字符串,也就是发送一串字符串给 Web 服务器)---> Web 服务器根据请求命令,给客户端浏览器返回一个 index.html 文件,或者,-->  调用一个外部程序,把请求参数传给外部程序,外部程序执行完后把结果(可能是 HTML 字符串,也可能是单纯的数据比如 JSON 字符串)返回给 Web Server --> Web Server 通过 TCP 发给浏览器。

WebBroker 框架就是这个“外部程序”,它解析 HTTP 请求的参数。

你创建 WebBroker 的时候如果创建的是独立运行的程序,则不需要外部的 Web Server 比如 IIS,而是程序自己内置了一个 IdHTTPServer 作为 Web Server,这样不需要 IIS 或者 Apache,浏览器可以直接访问这个程序,方便调试。
----------------------------------------------
-
作者:
男 gear1023 (gear1023) ★☆☆☆☆ -
盒子活跃会员
2024/3/21 10:11:04
1楼: 服务端主要提供的API服务,可以先厘清BS中的B和S之间的关系,其它就好说了:

B:浏览器,里面运行了HTML/CSS/JS,API一般情况是些在了JS里
S:服务端,提供B用的API(HTTP API),同时保存B用的HTML/CSS/JS
API 可以是D写的,也可以是.NET,JAVA,PYTHON等实现的。HTML/CSS/JS就是文件,最终有B下载到本地,在B里运行。

要实现开发,就要分别实现B和S,B完成与客户交互,同时和S打交道,S主要是提供API,一般是访问数据库/文件...
----------------------------------------------
-
作者:
男 deng584 (您好) ▲▲▲▲▲ -
普通会员
2024/3/21 10:15:11
2楼: 说那么深奥咋子!delphi开发web直接UniGUI,特别是根据作者的描述UniGui最适合他了
----------------------------------------------
-
作者:
男 msfm (清洁工) ★☆☆☆☆ -
盒子活跃会员
2024/3/21 10:34:06
3楼: 来合作合作 我直接给你vue 搞了 服务端也给你写了 带你做一遍
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2024/3/21 10:34:55
3楼: 如果不搞明白原理,直接使用 IntraWeb 或者 UniGUI,可以上来就做东西,但中间如果遇到问题,又不懂原理,就不容易解决。

我说的是从 WebBroker 入手理解原理。
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2024/3/21 10:35:52
4楼: @msfm 目前 vue 的开发,页面部分,可以通过拖拉控件实现吗?还是说需要手写代码?
----------------------------------------------
-
作者:
男 wr960204 (武稀松) ★☆☆☆☆ -
盒子活跃会员
2024/3/21 10:41:48
5楼: 既然说是Delphi做服务端开发,那你就做一个服务端程序,需求又这么简单,客户端请求的时候往客户端吐出一个HTML的脚本就行了啊。
----------------------------------------------
武稀松http://www.raysoftware.cn
作者:
男 msfm (清洁工) ★☆☆☆☆ -
盒子活跃会员
2024/3/21 10:43:46
5楼: vue 非常简单 就和开放  vue不是拖拉 但是开发思路和delphi差不多 传统的网页开放太麻烦了 自从用了vue 在也不想写传统的了
----------------------------------------------
-
作者:
男 dlfsystem (dlfsystem) ★☆☆☆☆ -
盒子活跃会员
2024/3/21 10:49:55
6楼: 用INtraweb, 一两天就搞定,布置方便,应付十万以下并发很轻松。
----------------------------------------------
-
作者:
男 msfm (清洁工) ★☆☆☆☆ -
盒子活跃会员
2024/3/21 11:01:35
7楼:  学一门其他手艺 会带来不一样的视野  web3d threejs 开发找我啊
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2024/3/21 12:10:57
8楼: 用惯了 Delphi 做界面拖拉控件,凡是不能拖拉控件的界面做法都是耍流氓。

还好,IntraWeb 是拖拉控件,UniGUI 也是。

很多年前摸过 IntraWeb 和 UniGUI,不知道这两个玩意现在发展得如何了?很多年前的体会是,这玩意封装得太深了,单纯想自己用代码去把页面上的 button 和服务器端的代码对应起来自己去做一些它本身没有提供的功能的话,很难找到下手的地方。

传统的 WEB 开发是服务器直接输出 HTML 页面。现在流行的前后端分离,服务器只输出数据 JSON,页面上用 VUE 等等 JS 自己把页面画出来。也就是 @gear1023  在上面所提到的做法。

这个做法用 Delphi 做后端,也很简单。入门的做法是使用 WebBroker 框架自己写,更简单的做法是用 DataSnap 的 REST 模式,直接输出 JSON 给页面。

至于页面,用 VUE 也好,用以前的 jquery 也好,都能做。

我自己写了一点 Demo 测试:
https://blog.csdn.net/pcplayer/article/details/128153130
https://blog.csdn.net/pcplayer/article/details/128155867
----------------------------------------------
-
作者:
男 abcjingtong (jingtong) ★☆☆☆☆ -
普通会员
2024/3/21 12:35:53
9楼: 给你个基于Delphi-Cross-Socket做的HttpServer,特别的简单,作者封装的特别好,用记来很方便,下面的CreateRouter演示了收到前端请求路由后返回对应的html文件,DoHttpRequest演示了收到前端get和post请求后返回自定义的html字符串,这2个基本上应付了大部分的web开发了,如果页面简单,还是从简单的html,css,js来,这样能解web的基础,不建议上来就vue、React等框架(各种配置,打包,npm插件就能把你搞晕,桌面写多了需要花时间才能适应web的开发方式),有不明白的可以联系我。

THCHttpServer = class(TCrossHttpServer, IHCHttpServer)
  private
    FShutdown: Boolean;
    procedure CreateRouter;
    procedure CreateWatchThread;
  public
    constructor Create(const AIoThreads: Integer; const ASsl: Boolean); override;
    destructor Destroy; override;
    procedure StopServer;
  end;
  
{ THCHttpServer }

constructor THCHttpServer.Create(const AIoThreads: Integer; const ASsl: Boolean);
begin
  inherited Create(AIoThreads, ASsl);
  FShutdown := True;

  {$IFDEF __CROSS_SSL__}
  Self.SetCertificate(SSL_SERVER_CERT);
  Self.SetPrivateKey(SSL_SERVER_PKEY);
  {$ENDIF}
  //Self.Addr := IPv4_ALL;
  //Self.Addr := IPv6_ALL;
  Self.Addr := IPv4v6_ALL;
  Self.Port := 12860;
  Self.Compressible := True;

  CreateRouter;
  CreateWatchThread;
end;

procedure THCHttpServer.CreateRouter;
var
  i: Integer;
begin
  Self
  .Get('/hello',
    procedure(const ARequest: ICrossHttpRequest; const AResponse: ICrossHttpResponse)
    begin
      AResponse.Send('Hello World');
    end)
//  .Get('/null',
//    procedure(const ARequest: ICrossHttpRequest; const AResponse: ICrossHttpResponse)
//    begin
//      AResponse.Send('');
//    end)
//  .Get('/progress/:id(\d+)',
//    procedure(const ARequest: ICrossHttpRequest; const AResponse: ICrossHttpResponse)
//    var
//      LProgID: Int64;
//      LProg: IProgress;
//    begin
//      LProgID := ARequest.Params['id'].ToInt64;
//      LProg := TProgress.Get(LProgID);
//
//      if (LProg <> nil) then
//        AResponse.Json(LProg.ToString)
//      else
//        AResponse.Send('非法id');
//    end)
//  .Delete('/progress/:id(\d+)',
//    procedure(const ARequest: ICrossHttpRequest; const AResponse: ICrossHttpResponse)
//    var
//      LProgID: Int64;
//    begin
//      LProgID := ARequest.Params['id'].ToInt64;
//
//      if TProgress.Remove(LProgID) then
//        AResponse.Send('删除任务进度成功')
//      else
//        AResponse.Send('非法id');
//    end)
//  .Get('task',
//    procedure(const ARequest: ICrossHttpRequest; const AResponse: ICrossHttpResponse)
//    begin
//      TTask.Run(
//        procedure
//        var
//          I: Integer;
//          LWatch: TStopwatch;
//          LSeconds: Integer;
//          LProg: IProgress;
//        begin
//          LProg := TProgress.New;
//          LProg.Max := 10;
//          LProg.Position := 0;
//
//          AResponse.Json(LProg.ToString);
//
//          LWatch := TStopwatch.StartNew;
//          for I := 1 to 10 do
//          begin
//          LSeconds := 5;//RandomRange(1, 10 + 1);
//          Sleep(LSeconds * 500);
//
//          LProg.Position := I;
//          end;
//          LWatch.Stop;
//        end);
//    end)
//  .Dir('/static', 'C:\')
//  .Dir('/g', 'G:\')
//  .Get('/file',
//    procedure(const ARequest: ICrossHttpRequest; const AResponse: ICrossHttpResponse)
//    var
//      LStream: TStream;
//    begin
//      LStream := TFile.OpenRead('d:\2.txt');
//      AResponse.ContentType := TMediaType.TEXT_HTML;
//      AResponse.SendNoCompress(LStream,
//        StrToInt64Def(ARequest.Query['pos'], 0),
//        StrToInt64Def(ARequest.Query['count'], 0),
//        procedure(const AConnection: ICrossConnection; const ASuccess: Boolean)
//        begin
//          FreeAndNil(LStream);
//        end);
//    end)
//  .Get('/stream',
//    procedure(const ARequest: ICrossHttpRequest; const AResponse: ICrossHttpResponse)
//    var
//      LStream: TMemoryStream;
//    begin
//      LStream := TMemoryStream.Create;
//      LStream.LoadFromFile('d:\2.txt');
//      AResponse.ContentType := TMediaType.TEXT_HTML;
//      AResponse.SendZCompress(LStream,
//        StrToInt64Def(ARequest.Query['pos'], 0),
//        StrToInt64Def(ARequest.Query['count'], 0),
//        TCompressType.ctGZip,
//        procedure(const AConnection: ICrossConnection; const ASuccess: Boolean)
//        begin
//          FreeAndNil(LStream);
//        end);
//    end)
//  .Get('/bytes',
//    procedure(const ARequest: ICrossHttpRequest; const AResponse: ICrossHttpResponse)
//    var
//      LBytes: TBytes;
//    begin
//      LBytes := TFile.ReadAllBytes('d:\2.txt');
//      AResponse.ContentType := TMediaType.TEXT_HTML;
//      AResponse.SendNoCompress(LBytes,
//        StrToInt64Def(ARequest.Query['pos'], 0),
//        StrToInt64Def(ARequest.Query['count'], 0),
//        procedure(const AConnection: ICrossConnection; const ASuccess: Boolean)
//        begin
//          LBytes := nil;
//        end);
//    end)
    ;

//  for I := 0 to AppCfg.DirMaps.Count - 1 do
//  begin
//    FHttpServer.Dir(
//      AppCfg.DirMaps.Names[I],
//      AppCfg.DirMaps.ValueFromIndex[I]);
//  end;
end;

procedure THCHttpServer.CreateWatchThread;
begin
  TThread.CreateAnonymousThread(
    procedure
    var
      LLastConCount, LCurConCount: Integer;
    begin
      LLastConCount := 0;
      while not FShutdown do
      begin
        LCurConCount := Self.ConnectionsCount;
        if (LCurConCount <> LLastConCount) then
        begin
          LLastConCount := LCurConCount;
          //Writeln('conn count:', LCurConCount);
        end;
        Sleep(100);
      end;
    end).Start;
end;

destructor THCHttpServer.Destroy;
begin

  inherited;
end;

procedure THCHttpServer.StopServer;
begin
  Self.Stop;
  FShutdown := True;
  Sleep(150);
end;

==========
FHttpServer := THCHttpServer.Create(0, False);
FHttpServer.Port := 23109;  // 端口号
FHttpServer.OnConnected := DoHttpConnected;
FHttpServer.OnDisconnected := DoHttpDisconnected;
FHttpServer.OnRequest := DoHttpRequest;
FHttpServer.Start(procedure(const AListen: ICrossListen; const ASuccess: Boolean)
begin
end);

procedure TfrmMain.DoHttpConnected(const Sender: TObject; const AConnection: ICrossConnection);
begin
  //Log('连接:' + AConnection.PeerAddr);
//  if (FHttpServer.ConnectionsCount > 100) then
//    AConnection.Close;
end;

procedure TfrmMain.DoHttpDisconnected(const Sender: TObject; const AConnection: ICrossConnection);
begin
  //Log('断开:' + AConnection.PeerAddr);
end;

procedure TfrmMain.DoHttpRequest(const Sender: TObject; const ARequest: ICrossHttpRequest;
  const AResponse: ICrossHttpResponse; var AHandled: Boolean);
var
  vResponseStr: string;
begin
  System.TMonitor.Enter(FReqObject);
  try
    //mmoLog.Lines.Add(ARequest.RawRequestText);
    AResponse.Header['Access-Control-Allow-Origin'] := '*';
    // 指定特定域名可以访问
    //AResponse.Header['Access-Control-Allow-Origin'] := 'http:localhost:8080/';

    vResponseStr :=
      '<html>' +
        '<head>' +
          '<meta charset="UTF-8">' +
          '<title>HCServer</title>' +
        '</head>' +
        '<body>' +
          '<h3>HCServer 运行正常(DPI:%d 程序版本:%s,文件版本:%s,SSL:%s)</h3>' +
          '</br><a>已打印 %d 次</a>' +
          '</br><a>您的地址:%s</a>' +
        '</body>' +
      '</html>';

    try
      if ARequest.Method = 'GET' then
      begin
        AResponse.Send(vResponseStr);
      end
      else
      if ARequest.Method = 'POST' then
      begin
        AResponse.Send(vResponseStr);
      end;
    except
      on E: Exception do
      begin
        AResponse.Send('{"err":"' + E.Message + '"}');  // 返回错误信息到前端
      end;
    end;
  finally
    System.TMonitor.Exit(FReqObject);
  end;
end;
----------------------------------------------
18114532@qq.com
作者:
男 mengyufeng (Raphael) ▲▲▲▲△ -
普通会员
2024/3/21 13:04:16
10楼: 其实GitHub上有叫兽做的标准框架,OneDelphi 还有一个基于lazaus的OnePascal,可以以他们为基础假设服务
----------------------------------------------
-
作者:
男 msfm (清洁工) ★☆☆☆☆ -
盒子活跃会员
2024/3/21 14:13:58
11楼: 以前我写安卓的时候也是不适应他的xml 一直想找拖拉 后来也出了拖拉了 但是已经不适应 感觉不拖拉才方便 都是一个熟悉与接收过程 要有一段时间适应 现在我处了 delphi拖拉 感觉还好 其它 qt c#啥的拖拉感觉很别扭
----------------------------------------------
-
作者:
男 zhangpuqing (pupu) ★☆☆☆☆ -
普通会员
2024/3/21 14:54:32
12楼: 1楼的老哥是牛人。
----------------------------------------------
-
作者:
男 bdl1 (bdl1) ▲▲▲▲▲ -
普通会员
2024/3/21 16:37:30
13楼:   1楼的老哥是牛人。
----------------------------------------------
-我的博客
作者:
男 sail2000 (小帆工作室) ★☆☆☆☆ -
盒子活跃会员
2024/3/21 16:52:58
14楼: delphi做服务端是很简单的,协议可以忽略,组件已经帮你做了,只需要注重核心功能即可,也就是客户按照你指定的东西提问,服务器返回你要回复的东西。
想找最简单的?我觉得github上的 Horse 组件就最简单了(基于Indy),而且 Horse 还能集成各种插件例如 Json, Log,oct- Sream, 等等

https://github.com/HashLoad/horse

uses Horse;

begin
  THorse.Get('/ping',//浏览器端请求参数
    procedure(Req: THorseRequest; Res: THorseResponse)
    begin
      Res.Send('pong');//服务器端回复
    end);

  THorse.Listen(9000);
end.
----------------------------------------------
delphi 是兴趣,和工作无关,即使它倒闭。又不靠它 delphi 吃饭,怕甚?
作者:
男 bahamut8348 (leonna) ★☆☆☆☆ -
普通会员
2024/3/22 13:38:55
15楼: 其实web端也不复杂,只要把原本的cs思路改成bs思路即可,目前比较流行的是前后端分离模式,也就是b端只用来显示数据。
至于协议,其实就是http,这东西用到再去研究也是可以的,底层协议嘛,1.0、1.1、2.0都是tcp协议,3.0则为udp协议。这个都是预设好的,了解一下即可。
不过我不推荐用delphi去做web server。delphi做这个说省的其实就是省点封装,其他更重要的比如http server,各种框架,delphi方面都很差。完全可以有更好的选择,比如php、go等。


至于说楼主说的登陆页面,其实就是页面提交,一般来说登录都会有一个单独的页面,然后一个表单<form>用来同步发送Post请求,服务端接收请求以后处理,然后返回结果,成功的话返回一个登录id,否则返回错误。客户端则根据不同的返回结果跳转到相应页面即可。
至于安全,服务端验证这个返回的登录id就可以判断当前请求是否合法,不合法就不返回数据就可以了。
----------------------------------------------
--
作者:
男 eben (蓝星帝国) ▲△△△△ -
普通会员
2024/3/23 11:42:32
16楼: 学到了,感谢楼上各位大佬
----------------------------------------------
33岁高龄学习编程---2022
作者:
男 lsh341999 (虫子) ★☆☆☆☆ -
普通会员
2024/3/25 13:39:27
17楼: UGFrame 简单介绍

UGFrame 模块精灵*简单介绍
----------------------------------------------
就怕想不到,没有做不到的
作者:
男 mousesoft (MouseSoft) ★☆☆☆☆ -
盒子活跃会员
2024/3/25 13:52:28
18楼: delphimvcframework
----------------------------------------------
-
作者:
男 jacky19451685 (jacky168) ▲△△△△ -
普通会员
2024/4/5 10:45:00
19楼: delphi 的坛子 还是挺活跃的嘛, 感谢,感觉是道道通罗马呀。3楼怎么联系, 带着做一遍,感觉比较靠谱!
----------------------------------------------
-
作者:
男 FengLinYuShu (FengLinYuShu) ★☆☆☆☆ -
盒子活跃会员
2024/4/5 15:15:03
20楼: DeWeb了解一下?
----------------------------------------------
-delphi大富翁论坛http://www.delphibbs.com
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行68.35938毫秒 RSS