在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
最近为 YxdIocp 开源库增加了这个轻量级的 MVC 支持。其实说是 MVC ,但由于现在还没有配套的视图模版引擎,所以在V上面很虚弱。不过本着做数据服务为主的思想,现在已经基本够用了。下面来简单介绍一下它吧。 要使用 MVC 功能非常简单, 引用 iocp.Http.MVC 单元,或者在窗体上放入一个 TIocpHttpMvcServer 组件即可。然后就是编写业务代码 (可参考开源库中 demo\IcopHttpSvrMVC 中的代码)。iocp.Http.MVC 引入了多个属性标注,它是 MVC 的灵魂,要用好 MVC, 必须了解这些属性标注的功能和意义。MVC引擎会使用扫描器根据这些标注自动扫描注入,响应客户请求。 在此之前,我们需要注意一点,在 Delphi 中, 属性标注定义的名称尾部如果是 "Attribute", 在使用时可以直接省略后面的 "Attribute" ,比如定义了“ServiceAttribute”标注,使用时可以只输入 "[Service]" 即可。下面讲到的属性标注,都是省略掉了 "Attribute" 。
[Service]业务层标识性标注,为指定类添加此标注后,此类才会被MVC引擎识别,从而具备业务请求处理能力。 uses iocp.Http.MVC, iocp.Http, SysUtils, Classes; type [Service] THelloMvc = class(TObject) public end; 在上面的示例代码中,由于加入了 [Service] 标识,会被 MVC 扫描器发现该类,并自动创建一个单例用于响应业务请求。
[Controller]控制层标注。增加此标注后,指定类才具备处理业务请求的能力。此标注和 [Service] 功能一样,只是方便在一些情况下区分业务类别。 uses iocp.Http.MVC, iocp.Http, SysUtils, Classes; type [Controller] THelloMvc = class(TObject) public end;
[Autowired]自动装配标注。为一个类中的变量增加此标注后,MVC 引擎会在实例化此类时,为这个成员变量自动注入适当的值。(目前由于暂无IOC实现,只能自动注入TIocpWebSocketServer、Ser ver) type [Controller] THelloMvc = class(TObject) protected [Autowired] FServer: TIocpHttpServer; public end; 程序运行后,会自动将 FServer 初始化为 HttpMVC.Server 。 [RequestMapping]请求地址映射标注。此标注可用于类级别,也可用于类中的函数或方法上。 此标注有如下几个属性:
注意: 在类级别使用此标注时, Params 属性无效。类级别主要用于设置URL的基址,以及一些默认值。比如在类级别指定 Method 为 Post,那么类中的函数或方法映射时,如果未指定 Method, 那么默认就是 Post。另外,在函数或方法被触发时,以下类型的参数会被自动注入:
另外,如果参数名是 “RequestData”, 且为字符串类型时,自动注入 Request.DataString 。
示例: unit Unit2; interface uses iocp.Http.MVC, iocp.Http, SysUtils, Classes; type [Controller] [RequestMapping('/hello')] THelloMvc = class(TObject) public // 映射地址:/hello/view1 // 响应所有类型的请求(Get, Post, Put 等等) [RequestMapping('/view1')] function View1(Request: TIocpHttpRequest): string; // 映射地址:/hello/view2 // 响应 Get 请求 [RequestMapping('/view2', http_GET)] function View2(Request: TIocpHttpRequest): string; // 映射地址:/hello/view3/aaa?uid=123 // 响应 Get 请求, 并且必须是包含参数uid,值为123时才响应 // 如果参数中不包括uid,或者uid的值不为123,会返回405错误 [RequestMapping('/view3/aaa', http_GET, 'uid=123')] function View3(Request: TIocpHttpRequest): string; // 映射地址:/hello/view4?uid=123 // 必须是 Get 请求, Content-Type必须包含application/json, // Accept必须是"*.*"或者包含“application/json” , // 必须包含参数uid=123 // 如果不符合条件会返回405错误 [RequestMapping('/view4', http_GET, 'application/json', 'application/json', 'uid=123')] function View4(Request: TIocpHttpRequest): string; end; implementation { THelloMvc } function THelloMvc.View1(Request: TIocpHttpRequest): string; begin Result := 'httpPostTest.html'; end; function THelloMvc.View2(Request: TIocpHttpRequest): string; begin Result := 'httpPostTest.html'; end; function THelloMvc.View3(Request: TIocpHttpRequest): string; begin Result := 'httpPostTest.html'; end; function THelloMvc.View4(Request: TIocpHttpRequest): string; begin Result := 'httpPostTest.html'; end; initialization THelloMvc.RegToMVC; end. 如果服务器Host是: http://127.0.0.1:8080, 那么,
URL: http://127.0.0.1:8080/view1 HTTP: 不限(GET, POST, PUT...) 触发: THelloMvc.View1
URL: http://127.0.0.1:8080/view2 HTTP: GET 触发: THelloMvc.View2
URL: http://127.0.0.1:8080/view3/aaa?uid=123 HTTP: GET 触发: THelloMvc.View3
URL: http://127.0.0.1:8080/view3/aaa?uid=789 HTTP: GET 触发: 无,返回405错误
URL: http://127.0.0.1:8080/view3 HTTP: GET 触发: 无,返回404错误
URL: http://127.0.0.1:8080/view4?uid=123 HTTP: GET 触发: 无,返回405错误 原因: 没有在Http Header 的"Content-Type"属性中添加 "application/JSON",需要使用类似下面的请求头才可以正常访问: GET /hello/view4?uid=123 HTTP/1.1
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:12.0) Gecko/20100101 Firefox/12.0
Content-Type: application/JSON; charset=GB2312
Accept-Encoding: gzip, deflate
Content-Length: 0
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Connection: Keep-Alive
使用上面的请求头,会触发 THelloMvc.View4 。 [Download]标识请求返回页面采用下载方式。加入此标注后,会在Http响应头中加入文件下载标识,浏览会以文件下载的方式读取数据,一般会提示用户保存文件。 type [Controller] THelloMvc = class(TObject) public [Download] function FileDownload(Request: TIocpHttpRequest): string; end;
[WebSocket]WebSocket 请求处理标注。当类中的一个函数或方法要用来处理 WebSocket 请求时,需要添加此标注。此标注不能与 [RequestMapping] 混用。 此标注有如下几个属性:
// WebSocket 请求处理,直接返回字符串内容 [WebSocket] function HelloWebSocket(): string; // WebSocket 请求处理, 只有接收到文本信息且内容是 'hello' 时才响应 [WebSocket('hello')] procedure HelloWebSocket2(Response: TIocpWebSocketResponse);
[PathVariable]这是一个参数级的属性标注,用来获得请求url中的动态参数,并绑定到处理函数中指定参数上。如果参数类型与实际传入的内容不符,会产生 500 错误。 此标注有如下几个属性:
URL中的动态参数,需要以 "{}" 包围起来。 [RequestMapping('/view/{uid}/{uname}', http_GET)] procedure ViewTest1( [PathVariable('uid')] UID: Integer; [PathVariable('uname')] const UName: string; Response: TIocpHttpWriter); 示例中,{uid}、{uname} 都是url级的动态参数。通过 PathVariable 标注将它们分别绑定到了处理函数 ViewTest1 的参数 UID 和 UName 上。在触发 ViewTest1 时,UID 的值就是 {uid} , UName 就是 {uname} 。
[RequestParam]这是一个参数级的属性标注。用于将请求参数区数据映射到功能处理方法的参数上。如果实际URL中参数不存在时,会将参数置为空(如数字型会是0,字符串会是空串)。如果参数类型与实际传入的内容不符,会产生 500 错误。 此标注有如下几个属性:
// 返回一个网页名称, 以下载方式 // 处理 URL: /demo/view4?uid=123456 [RequestMapping('/view4', http_GET)] function ViewTest4([RequestParam('uid')] UID: Integer): string; 示例中,当 url 是 http://host/demo/view4?uid=123456 时, 触发 ViewTest4 时,参数 UID 的值会是 123456。
[RequestBody]这是一个函数(方法)级的属性标注。它的作用如下: 1. 读取Request请求的body部分数据,使用系统默认配置的Converter(转换器)进行解析,然后把相应的数据绑定到要返回的对象上; 2. 再把Converter返回的对象数据绑定到controller中方法的参数上。 示例: type TUserData = record UID: Integer; Age: Integer; Name: string; Nick: string; end; ...... // 提交用户数据 // 处理 URL: /demo/person/profile/reguser [RequestMapping('/person/profile/reguser')] function RegUser([RequestBody] Data: TUserData): string; 在触发 RegUser 函数时, 转换器会将请求的数据注入 Data 中。在编写转换器时,GET请求一般需要单独处理。 转换器示例: 设置 HttpMvc.OnDeSerializeData 事件,在事件中进行处理: // 反序列化处理 function TForm1.DoDeSerializeData(Sender: TObject; const Value: string; const Dest: TValue; IsGet: Boolean): Boolean; var Json: JSONObject; S: AnsiString; begin if not IsGet then begin Json := JSONObject.ParseObject(Value, False); try if Assigned(Json) then TYxdSerialize.ReadValue(Json, Dest); finally FreeAndNil(Json); end; end else begin // Get 请求单独处理 Json := JSONObject.Create; try S := AnsiString(Value); HttpMvc.Server.DecodeParam(PAnsiChar(S), Length(S), DoReadedItem, Json); TYxdSerialize.ReadValue(Json, Dest); finally FreeAndNil(Json); end; end; Result := True; end; 在上面的反序列化操作中, Dest 实际上就是标注了 RequestBody 的响应函数中的正要注入的参数,在示例中就是 Data: TUserData。 如果有这样的一个Post请求: POST /demo/person/profile/reguser HTTP/1.1
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:12.0) Gecko/20100101 Firefox/12.0
Content-Type: text/html; charset=GB2312
Accept-Encoding: gzip, deflate
Content-Length: 59
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Connection: Keep-Alive
{"UID":666,"Age":30,"Name":"yangyxd","Nick":"\u55B5\u55B5"}
在触发 RegUser 时, 参数 Data 的值会是: Name Value
[ResponseBody]这是一个函数(方法)级的属性标注。它的作用如下: 将Controller的方法返回的对象,通过适当的Converter的Adapter转换对象, 将内容转换为指定格式后,写入到Response对象的body数据区。 // 根据UID查询用户信息。 // 处理 URL: /demo/person/profile/123456 [RequestMapping('/person/profile/{id}', http_GET)] [ResponseBody] function Porfile([PathVariable('id')] UID: Integer): TPerson; 在触发 Porfile 函数时, 返回值 TPerson 会被转换器转换序列化为指定格式,然后写入 Response 对象, 返回给客户端。 设置 HttpMvc.OnSerializeData 事件,在事件中进行处理: // 序列化处理 function TForm1.DoSerializeData(Sender: TObject; const Value: TValue): string; var Json: JSONObject; begin Json := JSONObject.Create; try TYxdSerialize.WriteValue(Json, '', Value); finally Result := Json.ToString(); Json.Free; end; end; 如果在 Porfile 中的返回内容是: function TMvcDemo.Porfile(UID: Integer): TPerson; begin Result.UID := UID; Result.Name := 'Admin'; Result.Status := 100; end; 通过使用上面的转换器, 在触发 Porfile 函数后,浏览器收到的数据会是: {"UID":123456,"Name":"Admin","Status":100}
二、映射函数返回值普通WEB请求:无返回值时, 会返回客户端 200 状态。 返回字符串时, 会返回一个 Prefix + 返回值 + Suffix 的文件。如果文件不存在,则返回 404 错误。 返回整数时, 认为是错误代码。比如返回 404, 则浏览器会收到 404 错误。 返回对象(Class) 或记录(Record)时,如果标注了ResponseBody,会使用转换器转换后输出给客户端。如果没有标注 ResponseBody, 则直接返回 200 状态。如果返回的是对象,会自动释放对象。
WebSocket 请求:无返回值时, 服务器不作任何响应。 返回字符串时,会直接将字符串发送给客户端。 返回数据时,会将数据转为字符串后发送给客户端。 返回对象(Class) 或记录(Record)时,如果标注了ResponseBody,会使用转换器转换后输出给客户端。如果没有标注 ResponseBody, 服务器不作任何响应。 三、 MVC 服务器配置在窗口上放置 TIocpHttpMvcServer 组件时, 通过组件属性面板进行设置。 不使用 TIocpHttpMvcServer 组件时, 引用 iocp.Http.Mvc , 会自动开启 MVC 服务。此时会自动加载配置文件 "http_mvc_setting.xml"。 配置文件应当放于服务程序相同目录中,文件名为 "http_mvc_setting.xml"。 配置文件示例: <?xml version="1.0" encoding="UTF-8"?> <xml> <ListenPort>8081</ListenPort> <Active>true</Active> <Charset>UTF-8</Charset> <UseWebSocket>false</UseWebSocket> <ContentLanguage/> <WebPath>.\Web\</WebPath> <GzipFileTypes>.htm;.html;.css;.js;.txt;.xml;.csv;.ics;.sgml;.c;.h;.pas;.cpp;.java;</GzipFileTypes> <AutoDecodePostParams>true</AutoDecodePostParams> <UploadMaxDataSize>2097152</UploadMaxDataSize> <AccessControlAllow> <Enabled>false</Enabled> <Origin>*</Origin> <Methods>POST, GET, OPTIONS</Methods> <Headers>X-Requested-With, Content-Type</Headers> </AccessControlAllow> </xml> 配置文件说明: ListenPort: 服务器监听端口 Active: 是否在程序运行后自动开始服务 Charset: 默认字符集 UseWebSocket: 是否使用WebSocket服务 ContentLanguage: 默认内容语言 WebPath: WEB服务文件根目录 Prefix: 返回视图的前缀名称,默认为空 Suffix: 返回视图的后缀名称,默认为空 UriCaseSensitive: URI是否大小写敏感 BindAddr: 服务器端口绑定地址,默认为“0.0.0.0” GzipFileTypes: 自动使用gzip压缩数据的文件类型 AutoDecodePostParams: 自动解析Post方式传入的参数 UploadMaxDataSize: 上传文件的最大大小(字节)。 AccessControlAllow: WEB请求跨域控制选项。 Enabled: 是否启用跨域控制 Origin: 允许哪些url可以跨域请求到本域 Methods: 允许的请求方法 Headers: 允许哪些请求头可以跨域
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论