[GWS] 服务简单做-RESTful的开发方式

在Genero FGL上也可以做出 RESTful 的 WEB Service。

先将回应WEB Request的方式拆解出来。如同之前在GAS设定章节中提到的,SERVICE与APPLICATION 的设定方式非常类似 (APPLICATION_LIST配置方式若不太清楚,可参考 [GAS] GBC上运作的Hello world!最下方的段落。)

https://ithelp.ithome.com.tw/upload/images/20210919/200511698hILAf5VOC.png

as.xcf配置 SERVICE_LIST

整段 as.xcf 最下方就是配置 WEB SERVICE的 SERVICE_LIST段落。基底(parent) 基本都是 ws.default

方法1.配置在as.xcf内

若以 service 名称 svms配置服务可参考下列范例:

      <APPLICATION Id="svms" Parent="ws.default" Abstract="TRUE">
        <EXECUTION>
          <PATH>$(res.zone)/svms/server</PATH>
          <MODULE>SvmsServer.42r</MODULE>
          <ACCESS_CONTROL>
            <ALLOW_FROM>$(res.access.control)</ALLOW_FROM>
          </ACCESS_CONTROL>
          <POOL>
            <START>0</START>
            <MIN_AVAILABLE>1</MIN_AVAILABLE>
            <MAX_AVAILABLE>1</MAX_AVAILABLE>
          </POOL>
        </EXECUTION>
      </APPLICATION>

与APPLICATION段落有差别的地方,主要在多了POOL设定。此设定可编配初始在 GAS启动时,是否一并启动此服务(START),最大同时在系统中可并行 (MAX_AVALIABLE)支数,最小留存在系统中(MIN_AVALIABLE)支数

最小留存只数的存在是为了不要让每次 REQUEST进来时,都要『等待』程序的启动(还要跑fglrun、连结database等等事务),应该要留下『待命用的』程序,随时随地等着 REQUEST做快速、即时的响应。

但是,要掂量掂量自己手上的 license。一个留存的service占用 1 runtime license。请仔细估算。别忘了还要保留给日常作业执行。

方法2.在as.xcf内,只配置GROUP ID

与 APPLICATION_LIST相同,在 SERVICE_LIST中也可以配置 GROUP

      <GROUP Id="_default">$(res.zone.config)/xcf/gws-ws</GROUP>
      <GROUP Id="demo">$(res.path.fgldir.demo.services)</GROUP>
      <GROUP Id="svms">/u1/topprd4/svms</GROUP>

系统中也有预设提供的路径,我们也可以加上自己有的路径(svms)。这样的配置好处仍旧是:不用每次调整个别的程序时,都要做 fastcgidispatch/httpdispatch的重启

在指定的路径下面(也就是上方标示的 /u1/topprd4/svms,若用RESOURCE_ID -$(res.xx)-替代,则需对应as.xcf最上方的讯息),则可以配置自己的应用程序名称,例如:我们配置一个名为『Vehicle.xcf』时,未来调用此服务的路径就会是『http://server_ip/wtopprd/svms/Vehicle 』开头 (此路径以下的拆解就会在程序内处理)。

配置 Server.4gl

下方的 server.4gl基本都可视为一个公版,主要在设定接收 WEB Service的设定。

IMPORT COM                 #取用httpd套件
IMPORT FGL ServiceVehicle  #实际进行服务处理的子程序

SCHEMA svms
MAIN
  DEFINE  req   com.HttpServiceRequest
  #设定发生错误前作可以等待的时间(预设:5sec)
  CALL com.WebServiceEngine.SetOption("readwritetimeout",60)
  #设定客?端、HTTPRequest和TCPRequest等待与server建立连线的最长时间(预设30,-1为无限)
  CALL com.WebServiceEngine.SetOption("connectiontimeout",25)
  #设定REST 操作中的 MIME类型的支持。有XML/JSON(预设BOTH)
  CALL com.webserviceEngine.SetOption("server_restdefaultformat","json")

  # Start server
  CALL com.WebServiceEngine.Start()  #开始等待

  WHILE TRUE
    TRY
      LET req = com.WebServiceEngine.GetHttpServiceRequest(-1)
      IF req IS NULL THEN
        IF int_flag THEN
          EXIT WHILE
        END IF
      ELSE
        CALL ProcessRequest(req)  #服务进线,往子程序传递处理
        LET int_flag = FALSE
      END IF
    CATCH
      EXIT WHILE
    END TRY
  END WHILE
END MAIN

这一段主程序主用途相当的固定。接下来看主程序内的 function 对照服务路径的写法:

FUNCTION ProcessRequest(req)
   DEFINE  req   com.HTTPServiceRequest
   DEFINE  path  STRING
   DEFINE  ind   INT
   LET path = req.getUrlPath()    #依照接续下来的路径区分要呼叫的子 function

   #Request路径 /List
   LET ind = path.getIndexOf("/List",1)
   IF ind>0 THEN
      LET path = path.subString(ind,path.getLength())
      CALL ServiceVehicle.ProcessListRequest(req, path)  #此处意思就是 /List就呼叫这个
      # 42m模组名.function名
      RETURN
   END IF
   #上述这一项,若 http://server_ip/wtopprd/svms/下除了List还有其他路径,
   #均需要模仿此方式串接处理子程序
   #其他不合路径一律列为未定义 Undefined request
   CALL req.sendTextResponse(400,NULL,"The Function Still UNSupport!")
END FUNCTION

主程序段,主要在做路径对应处理的function,搭配提供的服务逐一配置即可。

子程序 ServiceVehicle.4gl 配置

子程序个别 function内,就是对应资料的处理。关於个别对应资料如何检核、校正调整,并写入database,就请参考相关章节进行编写。

IMPORT com
IMPORT util
IMPORT FGL Helper     #FGL一些额外的套件
IMPORT FGL WSHelper   #FGL一些处理WS的额外套件
SCHEMA svms
PUBLIC FUNCTION ProcessListRequest(req, path)
  DEFINE  req     com.HTTPServiceRequest
  DEFINE  path    STRING

  CASE req.getMethod()   #确认要接的RESTful型态
    WHEN "GET"   CALL GetListRequest(req,path)   #GET方法的处理
    WHEN "POST"  CALL PostListRequest(req,path)  #POST方法的处理
    OTHERWISE
       CALL req.sendTextResponse(400,NULL, "暂时不支持本服务")
  END CASE
END FUNCTION

承接主程序的路径後,接下来在此子程序做的就是 ** action种类的判读**。从上述的范例可以看到,在这段作业中只接受 REST的GET与POST,而若是DELETE或其他动作传入,则会回传 400。

接下来以 GET的方法为例,说明处理方式:

PRIVATE FUNCTION GetListRequest(req,path)
  DEFINE req     com.HTTPServiceRequest
  DEFINE path    STRING
  DEFINE ops     Helper.OperationsType
  DEFINE txt     STRING
  DEFINE token   STRING
  DEFINE now     DATETIME YEAR TO SECOND
  DEFINE query   WSHelper.WSQueryType

  CALL Helper.SplitOperations(path,"/List") RETURNING ops  #确认List後面跟上的路径是否有次层
  CALL req.getUrlQuery(query)

  CASE ops.getLength()
    WHEN 1   #Request发在本层的处理
       #在这里写接收到资料的处理方式
       LET response_string="xxxxx"  #如果回传的是资料,记得转换为json格式的字串
       #处理完成後要回传的字串
       CALL req.setResponseHeader("Content-Type","application/json")  #回传格式设为json
       CALL req.setResponseHeader("Cache","no-cache")       #不支持cache
       CALL req.sendTextResponse(200,NULL, response_string) #处理完成後,回传200及对应讯息

    WHEN 2    #如果有两层的 Request路径,例如 /List/OnlyThisWeek  只找本周来的资料
      # 处理 /Commmant/Asktime
      IF ops[1]=="OnlyThisWeek" AND query.getLength()==1 THEN
        #放处理机制,回传资料放入 response_this_week
        CALL req.sendTextResponse(200, NULL, response_this_week)
      END IF

    OTHERWISE  CALL req.sendTextResponse(400,NULL,"暂时不提供此路径的服务")
  END CASE
END FUNCTION

透过上述程序的组装,我们就可以完成基本的 RESTful 程序搭建。


<<:  PHP 一些特性

>>:  股市小白混乱篇-使用 ticks API(1)

骨董级Fortigate 60B防火墙dual WAN应用心得分享

从事网路维运工作已超过15个年头,曾在公司内部担任资料中心网路管理员,并偕同Intranet网域网路...

[Day 11] 从零开始的 DenseNet 生活

0. 进度条 模型 进度 VGG Net (完成) ResNet (完成) DensNet (此篇)...

第 11 集:浅谈 Sass

此篇作为 Bootstrap 5 客制化 sass 的序章,会大致讲解什麽是 Sass 以及优势。...

企业资料通讯Week4 (2) | HTTP

HTTP 与Web 请求 HTTP,超文本传输协定(HyperText Transfer Proto...

Day 1:为什麽工程师要建立自己的技术部落格?

大家好我是 Gui,一名刚於私立科大资管系毕业的社会新鲜人,这是我第一次参与 IT 铁人赛,既紧张又...