NEW SAMPLE: Render Binary Contents

This commit is contained in:
Daniele Teti 2024-07-08 12:57:06 +02:00
parent 6d22cf6c6e
commit 8ac772f871
9 changed files with 1356 additions and 1 deletions

View File

@ -0,0 +1,138 @@
unit ControllerU;
interface
uses
MVCFramework, MVCFramework.Commons, MVCFramework.Serializer.Commons, System.Generics.Collections,
System.Classes;
type
[MVCPath('/api')]
TMyController = class(TMVCController)
public
[MVCPath('/files/($FileName)')]
[MVCHTTPMethod([httpGET])]
function GetFileByName(const FileName: String): TStream;
[MVCPath('/files2/($FileName)')]
[MVCHTTPMethod([httpGET])]
procedure GetStreamByFileName(const FileName: String);
end;
implementation
uses
System.SysUtils,
System.IOUtils,
MVCFramework.Logger,
System.StrUtils;
{ TMyController }
{
This action show how to return binary contents using a functional action (the action is a function)
}
function TMyController.GetFileByName(const FileName: String): TStream;
var
lPath: String;
lExt: String;
begin
lPath := TPath.Combine(TPath.Combine(TDirectory.GetParent(AppPath), 'files_repository'), FileName);
lPath := TPath.GetFullPath(lPath);
if not lPath.StartsWith(AppPath) then // directory traversal check
begin
raise EMVCException.Create(HTTP_STATUS.BadRequest, 'Invalid path');
end;
LogD(Context.QualifiedClassName + ': ' + lPath);
if not TFile.Exists(lPath) then
begin
raise EMVCException.Create(HTTP_STATUS.NotFound, 'File not found');
end;
lExt := TPath.GetExtension(lPath).ToLower;
if lExt = '.jpg' then
begin
Context.Response.ContentType := TMVCMediaType.IMAGE_JPEG;
end
else if lExt = '.txt' then
begin
Context.Response.ContentType := TMVCMediaType.TEXT_PLAIN;
end
else if lExt = '.pdf' then
begin
Context.Response.ContentType := TMVCMediaType.APPLICATION_PDF;
end
else if lExt = '.png' then
begin
Context.Response.ContentType := TMVCMediaType.IMAGE_PNG;
end
else if lExt = '.html' then
begin
Context.Response.ContentType := TMVCMediaType.TEXT_HTML;
end
else
begin
Context.Response.ContentType := TMVCMediaType.APPLICATION_OCTET_STREAM;
end;
StatusCode := HTTP_STATUS.OK;
Result := TFileStream.Create(lPath, fmOpenRead, fmShareDenyNone)
end;
{
This action show how to return binary contents using a classic action (the action is a procedure)
}
procedure TMyController.GetStreamByFileName(const FileName: String);
var
lPath: String;
lExt: String;
begin
lPath := TPath.Combine(TPath.Combine(TDirectory.GetParent(AppPath), 'files_repository'), FileName);
lPath := TPath.GetFullPath(lPath);
if not lPath.StartsWith(AppPath) then // directory traversal check
begin
raise EMVCException.Create(HTTP_STATUS.BadRequest, 'Invalid path');
end;
LogD(Context.QualifiedClassName + ': ' + lPath);
if not TFile.Exists(lPath) then
begin
raise EMVCException.Create(HTTP_STATUS.NotFound, 'File not found');
end;
lExt := TPath.GetExtension(lPath).ToLower;
if lExt = '.jpg' then
begin
Context.Response.ContentType := TMVCMediaType.IMAGE_JPEG;
end
else if lExt = '.txt' then
begin
Context.Response.ContentType := TMVCMediaType.TEXT_PLAIN;
end
else if lExt = '.pdf' then
begin
Context.Response.ContentType := TMVCMediaType.APPLICATION_PDF;
end
else if lExt = '.png' then
begin
Context.Response.ContentType := TMVCMediaType.IMAGE_PNG;
end
else if lExt = '.html' then
begin
Context.Response.ContentType := TMVCMediaType.TEXT_HTML;
end
else
begin
Context.Response.ContentType := TMVCMediaType.APPLICATION_OCTET_STREAM;
end;
StatusCode := HTTP_STATUS.OK;
SendFile(lPath);
end;
end.

View File

@ -0,0 +1,7 @@
object MyWebModule: TMyWebModule
OnCreate = WebModuleCreate
OnDestroy = WebModuleDestroy
Actions = <>
Height = 230
Width = 415
end

View File

@ -0,0 +1,82 @@
unit WebModuleU;
interface
uses
System.SysUtils,
System.Classes,
Web.HTTPApp,
MVCFramework;
type
TMyWebModule = class(TWebModule)
procedure WebModuleCreate(Sender: TObject);
procedure WebModuleDestroy(Sender: TObject);
private
fMVC: TMVCEngine;
end;
var
WebModuleClass: TComponentClass = TMyWebModule;
implementation
{$R *.dfm}
uses
System.IOUtils,
MVCFramework.Commons,
MVCFramework.Middleware.ActiveRecord,
MVCFramework.Middleware.StaticFiles,
MVCFramework.Middleware.Analytics,
MVCFramework.Middleware.Trace,
MVCFramework.Middleware.CORS,
MVCFramework.Middleware.ETag,
MVCFramework.Middleware.Compression, ControllerU;
procedure TMyWebModule.WebModuleCreate(Sender: TObject);
begin
FMVC := TMVCEngine.Create(Self,
procedure(Config: TMVCConfig)
begin
// session timeout (0 means session cookie)
Config[TMVCConfigKey.SessionTimeout] := dotEnv.Env('dmvc.session_timeout', '0');
//default content-type
Config[TMVCConfigKey.DefaultContentType] := dotEnv.Env('dmvc.default.content_type', TMVCConstants.DEFAULT_CONTENT_TYPE);
//default content charset
Config[TMVCConfigKey.DefaultContentCharset] := dotEnv.Env('dmvc.default.content_charset', TMVCConstants.DEFAULT_CONTENT_CHARSET);
//unhandled actions are permitted?
Config[TMVCConfigKey.AllowUnhandledAction] := dotEnv.Env('dmvc.allow_unhandled_actions', 'false');
//enables or not system controllers loading (available only from localhost requests)
Config[TMVCConfigKey.LoadSystemControllers] := dotEnv.Env('dmvc.load_system_controllers', 'true');
//default view file extension
Config[TMVCConfigKey.DefaultViewFileExtension] := dotEnv.Env('dmvc.default.view_file_extension', 'html');
//view path
Config[TMVCConfigKey.ViewPath] := dotEnv.Env('dmvc.view_path', 'templates');
//use cache for server side views (use "false" in debug and "true" in production for faster performances
Config[TMVCConfigKey.ViewCache] := dotEnv.Env('dmvc.view_cache', 'false');
//Max Record Count for automatic Entities CRUD
Config[TMVCConfigKey.MaxEntitiesRecordCount] := dotEnv.Env('dmvc.max_entities_record_count', IntToStr(TMVCConstants.MAX_RECORD_COUNT));
//Enable Server Signature in response
Config[TMVCConfigKey.ExposeServerSignature] := dotEnv.Env('dmvc.expose_server_signature', 'false');
//Enable X-Powered-By Header in response
Config[TMVCConfigKey.ExposeXPoweredBy] := dotEnv.Env('dmvc.expose_x_powered_by', 'true');
// Max request size in bytes
Config[TMVCConfigKey.MaxRequestSize] := dotEnv.Env('dmvc.max_request_size', IntToStr(TMVCConstants.DEFAULT_MAX_REQUEST_SIZE));
end);
// Controllers
FMVC.AddController(TMyController);
// Controllers - END
// Middleware
// Middleware - END
end;
procedure TMyWebModule.WebModuleDestroy(Sender: TObject);
begin
FMVC.Free;
end;
end.

View File

Before

Width:  |  Height:  |  Size: 199 KiB

After

Width:  |  Height:  |  Size: 199 KiB

View File

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 180 KiB

View File

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 138 KiB

View File

@ -0,0 +1,83 @@
program render_binary_contents;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
Web.ReqMulti,
Web.WebReq,
Web.WebBroker,
IdContext,
IdHTTPWebBrokerBridge,
MVCFramework,
MVCFramework.Logger,
MVCFramework.DotEnv,
MVCFramework.Commons,
MVCFramework.Serializer.Commons,
MVCFramework.Signal,
ControllerU in 'ControllerU.pas',
WebModuleU in 'WebModuleU.pas' {MyWebModule: TWebModule};
{$R *.res}
procedure RunServer(APort: Integer);
var
LServer: TIdHTTPWebBrokerBridge;
begin
LServer := TIdHTTPWebBrokerBridge.Create(nil);
try
LServer.OnParseAuthentication := TMVCParseAuthentication.OnParseAuthentication;
LServer.DefaultPort := APort;
LServer.KeepAlive := dotEnv.Env('dmvc.indy.keep_alive', True);
LServer.MaxConnections := dotEnv.Env('dmvc.webbroker.max_connections', 0);
LServer.ListenQueue := dotEnv.Env('dmvc.indy.listen_queue', 500);
LServer.Active := True;
LogI('Listening on port ' + APort.ToString);
LogI('Application started. Press Ctrl+C to shut down.');
WaitForTerminationSignal;
EnterInShutdownState;
LServer.Active := False;
finally
LServer.Free;
end;
end;
begin
{ Enable ReportMemoryLeaksOnShutdown during debug }
// ReportMemoryLeaksOnShutdown := True;
IsMultiThread := True;
// DMVCFramework Specific Configurations
// When MVCSerializeNulls = True empty nullables and nil are serialized as json null.
// When MVCSerializeNulls = False empty nullables and nil are not serialized at all.
MVCSerializeNulls := True;
// MVCNameCaseDefault defines the name case of property names generated by the serializers.
// Possibile values are: ncAsIs, ncUpperCase, ncLowerCase (default), ncCamelCase, ncPascalCase, ncSnakeCase
MVCNameCaseDefault := TMVCNameCase.ncLowerCase;
// UseConsoleLogger defines if logs must be emitted to also the console (if available).
UseConsoleLogger := True;
LogI('** DMVCFramework Server ** build ' + DMVCFRAMEWORK_VERSION);
try
if WebRequestHandler <> nil then
WebRequestHandler.WebModuleClass := WebModuleClass;
WebRequestHandlerProc.MaxConnections := dotEnv.Env('dmvc.handler.max_connections', 1024);
{$IF CompilerVersion >= 34} //SYDNEY+
if dotEnv.Env('dmvc.profiler.enabled', false) then
begin
Profiler.ProfileLogger := Log;
Profiler.WarningThreshold := dotEnv.Env('dmvc.profiler.warning_threshold', 2000);
end;
{$ENDIF}
RunServer(dotEnv.Env('dmvc.server.port', 8080));
except
on E: Exception do
LogF(E.ClassName + ': ' + E.Message);
end;
end.

File diff suppressed because it is too large Load Diff

View File

@ -43,7 +43,9 @@ uses
System.Generics.Collections, System.Generics.Collections,
MVCFramework.DuckTyping, MVCFramework.DuckTyping,
JsonDataObjects, JsonDataObjects,
MVCFramework.DotEnv, MVCFramework.Container, sqids; MVCFramework.DotEnv,
MVCFramework.Container,
sqids;
{$I dmvcframeworkbuildconsts.inc} {$I dmvcframeworkbuildconsts.inc}