‹ 首页

delphimvcframework

@delphicleancode · 收录于 1 周前

Patterns for development with DelphiMVCFramework (DMVC) — controllers, Active Record, JWT, Swagger

适合你,如果使用 Delphi 开发 MVC 后端,需要规范代码结构

/ 下载安装
delphimvcframework.skill双击,或拖进 Claude 桌面版 / Cowork,即完成安装↓ .skill↓ .zip
用别的 agent?下载 .zip 解压,把文件夹放进它的技能目录
Claude Code~/.claude/skills/(项目级 .claude/skills/)
Codex CLI~/.codex/skills/
Cursor自动读取上面两处目录
其他工具见其文档的「skills」目录;两个下载是同一份文件,只是名字不同
/ 通过 npx 安装 校验哈希
npx oh-my-skill add delphicleancode/delphi-spec-kit/delphimvcframework
/ 通过 bash 安装
curl -fsSL https://oh-my-skill.com/install.sh | bash -s -- delphicleancode/delphi-spec-kit/delphimvcframework
/ 已经装过?验证本机副本,不用重装
npx oh-my-skill verify delphicleancode/delphi-spec-kit/delphimvcframework
安装目标可用 --agent / --scope 或 --to 明确指定;省略时只会在唯一已存在的 agent 目录上自动选择,零命中或多命中会停止并提示。content_hash 缺失或不一致均拒装。
43GitHub stars
~1.4K上下文体积 · 单文件
镜像托管

怎么用

技能原文 SKILL.md作者撰写 · MIT · 9f7fc31

DelphiMVCFramework (DMVC) — Skill

Use this skill when creating web applications and REST APIs with DelphiMVCFramework.

When to Use
  • When creating REST APIs with MVC architecture
  • When using Active Record for data access
  • When implementing JWT authentication
  • When generating Swagger/OpenAPI documentation
About DMVC

DelphiMVCFramework is the most complete MVC framework for Delphi, created by Daniele Teti. Supports RESTful APIs, Server-Sent Events, WebSockets, Active Record, JSON/XML serialization and more.

Project Structure
src/
├── MeuApp.dpr                          ← Projeto principal
├── MeuApp.WebModule.pas                ← WebModule com engine DMVC
├── Controllers/
│   ├── MeuApp.Controller.Customer.pas
│   ├── MeuApp.Controller.Product.pas
│   └── MeuApp.Controller.Base.pas
├── Models/
│   ├── MeuApp.Model.Customer.pas       ← Active Record
│   ├── MeuApp.Model.Product.pas
│   └── MeuApp.Model.Base.pas
├── Services/
│   └── MeuApp.Service.Customer.pas
├── Middleware/
│   ├── MeuApp.Middleware.Auth.pas
│   └── MeuApp.Middleware.CORS.pas
└── Config/
    └── MeuApp.Config.pas
WebModule (Bootstrap)
unit MeuApp.WebModule;

interface

uses
  System.SysUtils,
  System.Classes,
  Web.HTTPApp,
  MVCFramework,
  MVCFramework.Commons;

type
  TAppWebModule = class(TWebModule)
    procedure WebModuleCreate(Sender: TObject);
    procedure WebModuleDestroy(Sender: TObject);
  private
    FMVCEngine: TMVCEngine;
  end;

implementation

uses
  MVCFramework.Middleware.CORS,
  MVCFramework.Middleware.JWT,
  MVCFramework.Middleware.StaticFiles,
  MeuApp.Controller.Customer,
  MeuApp.Controller.Product;

procedure TAppWebModule.WebModuleCreate(Sender: TObject);
begin
  FMVCEngine := TMVCEngine.Create(Self,
    procedure(AConfig: TMVCConfig)
    begin
      AConfig[TMVCConfigKey.DocumentRoot] := 'public';
      AConfig[TMVCConfigKey.DefaultContentType] := TMVCMediaType.APPLICATION_JSON;
      AConfig[TMVCConfigKey.DefaultViewFileExtension] := 'html';
    end
  );

  //Controllers
  FMVCEngine.AddController(TCustomerController);
  FMVCEngine.AddController(TProductController);

  //Middleware
  FMVCEngine.AddMiddleware(TMVCCORSMiddleware.Create);
end;

procedure TAppWebModule.WebModuleDestroy(Sender: TObject);
begin
  FMVCEngine.Free;
end;
Controller Pattern
unit MeuApp.Controller.Customer;

interface

uses
  MVCFramework,
  MVCFramework.Commons,
  MVCFramework.Serializer.Commons;

type
  [MVCPath('/api/customers')]
  TCustomerController = class(TMVCController)
  public
    [MVCPath]
    [MVCHTTPMethod([httpGET])]
    [MVCProduces(TMVCMediaType.APPLICATION_JSON)]
    procedure GetAll;

    [MVCPath('/($id)')]
    [MVCHTTPMethod([httpGET])]
    [MVCProduces(TMVCMediaType.APPLICATION_JSON)]
    procedure GetById(const AId: Integer);

    [MVCPath]
    [MVCHTTPMethod([httpPOST])]
    [MVCConsumes(TMVCMediaType.APPLICATION_JSON)]
    [MVCProduces(TMVCMediaType.APPLICATION_JSON)]
    procedure CreateCustomer;

    [MVCPath('/($id)')]
    [MVCHTTPMethod([httpPUT])]
    [MVCConsumes(TMVCMediaType.APPLICATION_JSON)]
    procedure UpdateCustomer(const AId: Integer);

    [MVCPath('/($id)')]
    [MVCHTTPMethod([httpDELETE])]
    procedure DeleteCustomer(const AId: Integer);
  end;

implementation

uses
  System.SysUtils,
  MVCFramework.ActiveRecord,
  MeuApp.Model.Customer;

procedure TCustomerController.GetAll;
var
  LCustomers: TMVCActiveRecordList;
begin
  LCustomers := TMVCActiveRecord.SelectRQL<TCustomer>('', 100);
  Render<TCustomer>(LCustomers);
end;

procedure TCustomerController.GetById(const AId: Integer);
var
  LCustomer: TCustomer;
begin
  LCustomer := TMVCActiveRecord.GetByPK<TCustomer>(AId);
  if not Assigned(LCustomer) then
  begin
    Render(HTTP_STATUS.NotFound, 'Customer not found');
    Exit;
  end;
  Render(LCustomer);
end;

procedure TCustomerController.CreateCustomer;
var
  LCustomer: TCustomer;
begin
  LCustomer := Context.Request.BodyAs<TCustomer>;
  try
    LCustomer.Insert;
    Render201Created('/api/customers/' + LCustomer.Id.ToString);
  except
    on E: Exception do
    begin
      LCustomer.Free;
      raise;
    end;
  end;
end;

procedure TCustomerController.UpdateCustomer(const AId: Integer);
var
  LCustomer: TCustomer;
begin
  LCustomer := Context.Request.BodyAs<TCustomer>;
  try
    LCustomer.Id := AId;
    LCustomer.Update;
    Render(HTTP_STATUS.OK, 'Updated');
  except
    on E: Exception do
    begin
      LCustomer.Free;
      raise;
    end;
  end;
end;

procedure TCustomerController.DeleteCustomer(const AId: Integer);
var
  LCustomer: TCustomer;
begin
  LCustomer := TMVCActiveRecord.GetByPK<TCustomer>(AId);
  if not Assigned(LCustomer) then
  begin
    Render(HTTP_STATUS.NotFound, 'Customer not found');
    Exit;
  end;

  try
    LCustomer.Delete;
    Render(HTTP_STATUS.NoContent, '');
  finally
    LCustomer.Free;
  end;
end;
Active Record (Model)
unit MeuApp.Model.Customer;

interface

uses
  MVCFramework.ActiveRecord,
  MVCFramework.Serializer.Commons;

type
  [MVCTable('customers')]
  [MVCNameCase(ncLowerCase)]
  TCustomer = class(TMVCActiveRecord)
  private
    [MVCTableField('id', [foPrimaryKey, foAutoGenerated])]
    FId: Integer;

    [MVCTableField('name')]
    FName: string;

    [MVCTableField('cpf')]
    FCpf: string;

    [MVCTableField('email')]
    FEmail: string;

    [MVCTableField('status')]
    FStatus: Integer;

    [MVCTableField('created_at')]
    FCreatedAt: TDateTime;
  public
    property Id: Integer read FId write FId;
    property Name: string read FName write FName;
    property Cpf: string read FCpf write FCpf;
    property Email: string read FEmail write FEmail;
    property Status: Integer read FStatus write FStatus;
    property CreatedAt: TDateTime read FCreatedAt write FCreatedAt;
  end;

implementation

end.
JWT Middleware
uses
  MVCFramework.Middleware.JWT;

// No WebModule:
FMVCEngine.AddMiddleware(
  TMVCJWTAuthenticationMiddleware.Create(
    TAuthHandler.Create,   //implementa IMVCAuthenticationHandler
    'my-secret-key',
    '/api/login',          //public login route
    [TJWTCheckableClaim.ExpirationTime]
  )
);
RQL (Resource Query Language)
//Filter via query string (DMVC automatically interprets RQL)
//GET /api/customers?$filter=contains(name,'João')&$orderby=name&$top=10

LCustomers := TMVCActiveRecord.SelectRQL<TCustomer>(
  Context.Request.QueryStringParam('$filter'),
  Context.Request.QueryStringParam('$top').ToInteger
);
DMVC Conventions

| Appearance | Convention | |---------|-----------| | Controller | Inherits from TMVCController with [MVCPath] attribute | | Models | Inherits from TMVCActiveRecord with [MVCTable] attribute | | Routes | Attributes: [MVCPath], [MVCHTTPMethod] | | Serialization | Automatic via Render() (JSON by default) | | Parameters | Path: ($id), Query: Context.Request.QueryStringParam | | Body | Context.Request.BodyAs<T> to deserialize JSON | | Status | Render(HTTP_STATUS.OK), Render201Created | | Middleware | Inherits from TMVCCustomMiddleware or uses built-in | | Swagger | Attributes [MVCSwagSummary], [MVCSwagParam] |

Checklist for DMVC Projects
  • [ ] WebModule configured with TMVCEngine?
  • [ ] Controllers registered with AddController?
  • [ ] CORS middleware added?
  • [ ] Active Record models with [MVCTable] and [MVCTableField]?
  • [ ] Do routes follow the RESTful standard?
  • [ ] Render() used for all answers?
  • [ ] JWT middleware for protected routes?
  • [ ] BodyAs<T> objects released in case of exception?
按 MIT 许可原样转载,未经改动 · 在 GitHub 查看 →

评论

登录即可评论;带「已验证安装」的,是发布者名下有本店的安装或持有记录。