diff --git a/samples/fileupload/FileUploadControllerU.pas b/samples/fileupload/FileUploadControllerU.pas index a670f2aa..e70a387c 100644 --- a/samples/fileupload/FileUploadControllerU.pas +++ b/samples/fileupload/FileUploadControllerU.pas @@ -4,6 +4,7 @@ interface uses ReqMulti, // this unit is required to enable file uploading + System.Generics.Collections, MVCFramework, MVCFramework.Commons, MVCFramework.Logger; @@ -12,21 +13,21 @@ type [MVCPath] TFileUploadController = class(TMVCController) - private const + public const UPLOAD_FOLDER = 'uploadedfiles'; + private + function GetFiles: TList; + + protected + procedure OnBeforeAction(AContext: TWebContext; const AActionName: string; var AHandled: Boolean); override; public [MVCPath('/')] [MVCHTTPMethod([httpGET])] - procedure Index; + function Index: string; [MVCPath('/file/upload')] [MVCHTTPMethod([httpPOST])] - procedure SaveFile(CTX: TWebContext); - - [MVCPath('/file/list')] - [MVCProduces('text/html')] - [MVCHTTPMethod([httpGET])] - procedure FileList(CTX: TWebContext); + function SaveFile: IMVCResponse; end; implementation @@ -35,60 +36,94 @@ uses system.ioutils, system.Classes, system.SysUtils, - system.Types; + system.Types, + JsonDataObjects; { TFileUploadController } -procedure TFileUploadController.FileList(CTX: TWebContext); +//function TFileUploadController.FileList: String; +//var +// lFileNames: TList; +//begin +// lFileNames := GetFiles; +// try +// ViewData['files'] := lFileNames; +// Result := PageFragment(['filelist']); +// finally +// lFileNames.free; +// end; +//end; + +function TFileUploadController.GetFiles: TList; var - lUploadedFiles: TStringDynArray; + lUploadedFiles: TArray; lFName: string; begin - LoadView(['header']); - // ResponseStream.AppendLine(''); - ResponseStream.AppendLine('

**Upload Folder Content**

'); lUploadedFiles := TDirectory.GetFiles(UPLOAD_FOLDER); - ResponseStream.AppendLine('
    '); - for lFName in lUploadedFiles do - begin - ResponseStream.AppendLine('
  • ' + ExtractFileName(lFName) + '
  • '); + Result := TList.Create; + try + for lFName in lUploadedFiles do + begin + Result.Add(ExtractFileName(lFName)); + end; + except + Result.Free; + raise; end; - ResponseStream.AppendLine('
') - .AppendLine('

<< BACK TO HOME

'); - LoadView(['footer']); - RenderResponseStream; end; -procedure TFileUploadController.Index; +function TFileUploadController.Index: String; +var + lFileNames: TList; begin - LoadView(['header', 'fileupload', 'footer']); - RenderResponseStream(); + lFileNames := GetFiles; + try + ViewData['files'] := lFileNames; + ViewData['files_count'] := lFileNames.Count; + Result := Page(['fileupload','filelist']); + finally + lFileNames.free; + end; end; -procedure TFileUploadController.SaveFile(CTX: TWebContext); +procedure TFileUploadController.OnBeforeAction(AContext: TWebContext; + const AActionName: string; var AHandled: Boolean); +begin + inherited; + SetPagesCommonHeaders(['header']); + SetPagesCommonFooters(['footer']); +end; + +function TFileUploadController.SaveFile: IMVCResponse; var lFName: string; - I: Integer; lFile: TFileStream; begin - TDirectory.CreateDirectory(UPLOAD_FOLDER); - for I := 0 to CTX.Request.RawWebRequest.Files.Count - 1 do + if Context.Request.RawWebRequest.Files.Count <> 1 then begin - lFName := String(CTX.Request.Files[I].FileName); - lFName := TPath.GetFileName(lFName.Trim(['"'])); - if not TPath.HasValidFileNameChars(lFName, false) then - raise EMVCException.Create - (lFName + ' is not a valid filename for the hosting OS'); - Log('Uploading ' + lFName); - lFile := TFile.Create(TPath.Combine(UPLOAD_FOLDER, lFName)); - try - lFile.CopyFrom(CTX.Request.Files[I].Stream, 0); - finally - lFile.free; - end; + Exit(RedirectResponse('/')); end; - Redirect('/file/list'); + lFName := String(Context.Request.Files[0].FileName); + lFName := TPath.GetFileName(lFName.Trim(['"'])); + if not TPath.HasValidFileNameChars(lFName, false) then + begin + raise EMVCException.Create + (HTTP_STATUS.BadRequest, lFName + ' is not a valid filename for the hosting OS'); + end; + if TFile.Exists(TPath.Combine(UPLOAD_FOLDER, lFName)) then + begin + raise EMVCException.Create + (HTTP_STATUS.BadRequest, lFName + ' already present, cannot override'); + end; + Log('Uploading ' + lFName); + lFile := TFile.Create(TPath.Combine(UPLOAD_FOLDER, lFName)); + try + lFile.CopyFrom(Context.Request.Files[0].Stream, 0); + finally + lFile.free; + end; + Result := RedirectResponse('/'); end; end. diff --git a/samples/fileupload/FilesUploadDemo.dpr b/samples/fileupload/FilesUploadDemo.dpr index bed5ea7f..cd39ea3e 100644 --- a/samples/fileupload/FilesUploadDemo.dpr +++ b/samples/fileupload/FilesUploadDemo.dpr @@ -10,12 +10,11 @@ uses Winapi.ShellAPI, {$ENDIF } IdHTTPWebBrokerBridge, + MVCFramework.Logger, Web.WebReq, Web.WebBroker, WebModuleUnit1 in 'WebModuleUnit1.pas' {WebModule1: TWebModule}, - FileUploadControllerU in 'FileUploadControllerU.pas', - MVCFramework.View.Renderers.TemplatePro in '..\serversideviewcustom\MVCFramework.View.Renderers.TemplatePro.pas', - TemplateProU in '..\serversideviewcustom\lib\TemplateProU.pas'; + FileUploadControllerU in 'FileUploadControllerU.pas', System.IOUtils; {$R *.res} @@ -24,12 +23,12 @@ procedure RunServer(APort: Integer); var LServer: TIdHTTPWebBrokerBridge; begin - Writeln(Format('Starting HTTP Server or port %d', [APort])); + LogI(Format('Starting HTTP Server or port %d', [APort])); LServer := TIdHTTPWebBrokerBridge.Create(nil); try LServer.DefaultPort := APort; LServer.Active := True; - Writeln('Press RETURN to stop the server'); + LogI('Press RETURN to stop the server'); {$IFDEF MSWINDOWS} ShellExecute(0, 'open', 'http://localhost:3000', nil, nil, SW_SHOW); @@ -42,13 +41,14 @@ begin end; begin + TDirectory.CreateDirectory(TFileUploadController.UPLOAD_FOLDER); try if WebRequestHandler <> nil then WebRequestHandler.WebModuleClass := WebModuleClass; RunServer(3000); except on E: Exception do - Writeln(E.ClassName, ': ', E.Message); + LogE(E.ClassName + ': ' + E.Message); end end. diff --git a/samples/fileupload/FilesUploadDemo.dproj b/samples/fileupload/FilesUploadDemo.dproj index cf4d2937..b100a0cc 100644 --- a/samples/fileupload/FilesUploadDemo.dproj +++ b/samples/fileupload/FilesUploadDemo.dproj @@ -50,7 +50,7 @@ $(BDS)\bin\delphi_PROJECTICNS.icns System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) .\$(Platform)\$(Config) - .\$(Platform)\$(Config) + .\bin false false false @@ -76,6 +76,7 @@ 1033 false + none false @@ -92,8 +93,6 @@ TWebModule - - Base @@ -156,8 +155,10 @@ - Microsoft Office 2000 Beispiele für gekapselte Komponenten für Automatisierungsserver - Microsoft Office XP Beispiele für gekapselte Komponenten für Automation Server + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package @@ -180,6 +181,12 @@ + + + FilesUploadDemo.exe + true + + 1 diff --git a/samples/fileupload/WebModuleUnit1.dfm b/samples/fileupload/WebModuleUnit1.dfm index b43b8558..e4c60405 100644 --- a/samples/fileupload/WebModuleUnit1.dfm +++ b/samples/fileupload/WebModuleUnit1.dfm @@ -1,5 +1,4 @@ object WebModule1: TWebModule1 - OldCreateOrder = False OnCreate = WebModuleCreate Actions = < item diff --git a/samples/fileupload/WebModuleUnit1.pas b/samples/fileupload/WebModuleUnit1.pas index 338755b5..577017c2 100644 --- a/samples/fileupload/WebModuleUnit1.pas +++ b/samples/fileupload/WebModuleUnit1.pas @@ -27,7 +27,7 @@ implementation uses FileUploadControllerU, MVCFramework.Commons, - MVCFramework.View.Renderers.TemplatePro, + MVCFramework.View.Renderers.Mustache, MVCFramework.Middleware.Trace, MVCFramework.Middleware.StaticFiles; @@ -40,17 +40,16 @@ begin procedure(Config: TMVCConfig) begin Config[TMVCConfigKey.ViewPath] := - ExtractFilePath(GetModuleName(HInstance)) + '..\..\templates'; + ExtractFilePath(GetModuleName(HInstance)) + '..\templates'; Config[TMVCConfigKey.DefaultContentType] := TMVCMediaType.TEXT_HTML; end); MVC.AddController(TFileUploadController); - MVC.AddMiddleware(TMVCTraceMiddleware.Create); MVC.AddMiddleware(TMVCStaticFilesMiddleware.Create( '/static', { StaticFilesPath } - ExtractFilePath(GetModuleName(HInstance)) + '..\..\document_root', { DocumentRoot } + ExtractFilePath(GetModuleName(HInstance)) + '..\www', { DocumentRoot } 'index.html' { IndexDocument - Before it was named fallbackresource } )); - MVC.SetViewEngine(TMVCTemplateProViewEngine); + MVC.SetViewEngine(TMVCMustacheViewEngine); end; diff --git a/samples/fileupload/document_root/milligram.min.css b/samples/fileupload/document_root/milligram.min.css deleted file mode 100644 index 85f877b9..00000000 --- a/samples/fileupload/document_root/milligram.min.css +++ /dev/null @@ -1,11 +0,0 @@ -/*! - * Milligram v1.3.0 - * https://milligram.github.io - * - * Copyright (c) 2017 CJ Patoilo - * Licensed under the MIT license - */ - -*,*:after,*:before{box-sizing:inherit}html{box-sizing:border-box;font-size:62.5%}body{color:#606c76;font-family:'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;font-size:1.6em;font-weight:300;letter-spacing:.01em;line-height:1.6}blockquote{border-left:0.3rem solid #d1d1d1;margin-left:0;margin-right:0;padding:1rem 1.5rem}blockquote *:last-child{margin-bottom:0}.button,button,input[type='button'],input[type='reset'],input[type='submit']{background-color:#9b4dca;border:0.1rem solid #9b4dca;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.8rem;letter-spacing:.1rem;line-height:3.8rem;padding:0 3.0rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type='button']:focus,input[type='button']:hover,input[type='reset']:focus,input[type='reset']:hover,input[type='submit']:focus,input[type='submit']:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type='button'][disabled],input[type='reset'][disabled],input[type='submit'][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type='button'][disabled]:focus,input[type='button'][disabled]:hover,input[type='reset'][disabled]:focus,input[type='reset'][disabled]:hover,input[type='submit'][disabled]:focus,input[type='submit'][disabled]:hover{background-color:#9b4dca;border-color:#9b4dca}.button.button-outline,button.button-outline,input[type='button'].button-outline,input[type='reset'].button-outline,input[type='submit'].button-outline{background-color:transparent;color:#9b4dca}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type='button'].button-outline:focus,input[type='button'].button-outline:hover,input[type='reset'].button-outline:focus,input[type='reset'].button-outline:hover,input[type='submit'].button-outline:focus,input[type='submit'].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type='button'].button-outline[disabled]:focus,input[type='button'].button-outline[disabled]:hover,input[type='reset'].button-outline[disabled]:focus,input[type='reset'].button-outline[disabled]:hover,input[type='submit'].button-outline[disabled]:focus,input[type='submit'].button-outline[disabled]:hover{border-color:inherit;color:#9b4dca}.button.button-clear,button.button-clear,input[type='button'].button-clear,input[type='reset'].button-clear,input[type='submit'].button-clear{background-color:transparent;border-color:transparent;color:#9b4dca}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type='button'].button-clear:focus,input[type='button'].button-clear:hover,input[type='reset'].button-clear:focus,input[type='reset'].button-clear:hover,input[type='submit'].button-clear:focus,input[type='submit'].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type='button'].button-clear[disabled]:focus,input[type='button'].button-clear[disabled]:hover,input[type='reset'].button-clear[disabled]:focus,input[type='reset'].button-clear[disabled]:hover,input[type='submit'].button-clear[disabled]:focus,input[type='submit'].button-clear[disabled]:hover{color:#9b4dca}code{background:#f4f5f6;border-radius:.4rem;font-size:86%;margin:0 .2rem;padding:.2rem .5rem;white-space:nowrap}pre{background:#f4f5f6;border-left:0.3rem solid #9b4dca;overflow-y:hidden}pre>code{border-radius:0;display:block;padding:1rem 1.5rem;white-space:pre}hr{border:0;border-top:0.1rem solid #f4f5f6;margin:3.0rem 0}input[type='email'],input[type='number'],input[type='password'],input[type='search'],input[type='tel'],input[type='text'],input[type='url'],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:0.1rem solid #d1d1d1;border-radius:.4rem;box-shadow:none;box-sizing:inherit;height:3.8rem;padding:.6rem 1.0rem;width:100%}input[type='email']:focus,input[type='number']:focus,input[type='password']:focus,input[type='search']:focus,input[type='tel']:focus,input[type='text']:focus,input[type='url']:focus,textarea:focus,select:focus{border-color:#9b4dca;outline:0}select{background:url('data:image/svg+xml;utf8,') center right no-repeat;padding-right:3.0rem}select:focus{background-image:url('data:image/svg+xml;utf8,')}textarea{min-height:6.5rem}label,legend{display:block;font-size:1.6rem;font-weight:700;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type='checkbox'],input[type='radio']{display:inline}.label-inline{display:inline-block;font-weight:normal;margin-left:.5rem}.container{margin:0 auto;max-width:112.0rem;padding:0 2.0rem;position:relative;width:100%}.row{display:flex;flex-direction:column;padding:0;width:100%}.row.row-no-padding{padding:0}.row.row-no-padding>.column{padding:0}.row.row-wrap{flex-wrap:wrap}.row.row-top{align-items:flex-start}.row.row-bottom{align-items:flex-end}.row.row-center{align-items:center}.row.row-stretch{align-items:stretch}.row.row-baseline{align-items:baseline}.row .column{display:block;flex:1 1 auto;margin-left:0;max-width:100%;width:100%}.row .column.column-offset-10{margin-left:10%}.row .column.column-offset-20{margin-left:20%}.row .column.column-offset-25{margin-left:25%}.row .column.column-offset-33,.row .column.column-offset-34{margin-left:33.3333%}.row .column.column-offset-50{margin-left:50%}.row .column.column-offset-66,.row .column.column-offset-67{margin-left:66.6666%}.row .column.column-offset-75{margin-left:75%}.row .column.column-offset-80{margin-left:80%}.row .column.column-offset-90{margin-left:90%}.row .column.column-10{flex:0 0 10%;max-width:10%}.row .column.column-20{flex:0 0 20%;max-width:20%}.row .column.column-25{flex:0 0 25%;max-width:25%}.row .column.column-33,.row .column.column-34{flex:0 0 33.3333%;max-width:33.3333%}.row .column.column-40{flex:0 0 40%;max-width:40%}.row .column.column-50{flex:0 0 50%;max-width:50%}.row .column.column-60{flex:0 0 60%;max-width:60%}.row .column.column-66,.row .column.column-67{flex:0 0 66.6666%;max-width:66.6666%}.row .column.column-75{flex:0 0 75%;max-width:75%}.row .column.column-80{flex:0 0 80%;max-width:80%}.row .column.column-90{flex:0 0 90%;max-width:90%}.row .column .column-top{align-self:flex-start}.row .column .column-bottom{align-self:flex-end}.row .column .column-center{-ms-grid-row-align:center;align-self:center}@media (min-width: 40rem){.row{flex-direction:row;margin-left:-1.0rem;width:calc(100% + 2.0rem)}.row .column{margin-bottom:inherit;padding:0 1.0rem}}a{color:#9b4dca;text-decoration:none}a:focus,a:hover{color:#606c76}dl,ol,ul{list-style:none;margin-top:0;padding-left:0}dl dl,dl ol,dl ul,ol dl,ol ol,ol ul,ul dl,ul ol,ul ul{font-size:90%;margin:1.5rem 0 1.5rem 3.0rem}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1.0rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:2.5rem}table{border-spacing:0;width:100%}td,th{border-bottom:0.1rem solid #e1e1e1;padding:1.2rem 1.5rem;text-align:left}td:first-child,th:first-child{padding-left:0}td:last-child,th:last-child{padding-right:0}b,strong{font-weight:bold}p{margin-top:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2.0rem;margin-top:0}h1{font-size:4.6rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:2.8rem;line-height:1.3}h4{font-size:2.2rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}img{max-width:100%}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right} - -/*# sourceMappingURL=milligram.min.css.map */ \ No newline at end of file diff --git a/samples/fileupload/templates/error.html b/samples/fileupload/templates/error.html new file mode 100644 index 00000000..6baba52b --- /dev/null +++ b/samples/fileupload/templates/error.html @@ -0,0 +1,2 @@ +

❌ {{error}}

+ \ No newline at end of file diff --git a/samples/fileupload/templates/filelist.html b/samples/fileupload/templates/filelist.html new file mode 100644 index 00000000..c9d1a97c --- /dev/null +++ b/samples/fileupload/templates/filelist.html @@ -0,0 +1,7 @@ +

There are {{files_count}} uploaded files

+
    +{{#files}} +
  • {{.}}
  • +{{/files}} +
+ diff --git a/samples/fileupload/templates/fileupload.html b/samples/fileupload/templates/fileupload.html index 25801f45..53a2b39e 100644 --- a/samples/fileupload/templates/fileupload.html +++ b/samples/fileupload/templates/fileupload.html @@ -1,4 +1,5 @@
- +
+
diff --git a/samples/fileupload/templates/header.html b/samples/fileupload/templates/header.html index c073c59d..91cd128e 100644 --- a/samples/fileupload/templates/header.html +++ b/samples/fileupload/templates/header.html @@ -1,6 +1,7 @@ + - +
diff --git a/samples/fileupload/www/milligram.min.css b/samples/fileupload/www/milligram.min.css new file mode 100644 index 00000000..5e8955c3 --- /dev/null +++ b/samples/fileupload/www/milligram.min.css @@ -0,0 +1,3 @@ +*,*:after,*:before{box-sizing:inherit}html{box-sizing:border-box;font-size:62.5%}body{color:#606c76;font-family:'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;font-size:1.6em;font-weight:300;letter-spacing:.01em;line-height:1.6}blockquote{border-left:0.3rem solid #d1d1d1;margin-left:0;margin-right:0;padding:1rem 1.5rem}blockquote *:last-child{margin-bottom:0}.button,button,input[type='button'],input[type='reset'],input[type='submit']{background-color:#9b4dca;border:0.1rem solid #9b4dca;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.8rem;letter-spacing:.1rem;line-height:3.8rem;padding:0 3.0rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type='button']:focus,input[type='button']:hover,input[type='reset']:focus,input[type='reset']:hover,input[type='submit']:focus,input[type='submit']:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type='button'][disabled],input[type='reset'][disabled],input[type='submit'][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type='button'][disabled]:focus,input[type='button'][disabled]:hover,input[type='reset'][disabled]:focus,input[type='reset'][disabled]:hover,input[type='submit'][disabled]:focus,input[type='submit'][disabled]:hover{background-color:#9b4dca;border-color:#9b4dca}.button.button-outline,button.button-outline,input[type='button'].button-outline,input[type='reset'].button-outline,input[type='submit'].button-outline{background-color:transparent;color:#9b4dca}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type='button'].button-outline:focus,input[type='button'].button-outline:hover,input[type='reset'].button-outline:focus,input[type='reset'].button-outline:hover,input[type='submit'].button-outline:focus,input[type='submit'].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type='button'].button-outline[disabled]:focus,input[type='button'].button-outline[disabled]:hover,input[type='reset'].button-outline[disabled]:focus,input[type='reset'].button-outline[disabled]:hover,input[type='submit'].button-outline[disabled]:focus,input[type='submit'].button-outline[disabled]:hover{border-color:inherit;color:#9b4dca}.button.button-clear,button.button-clear,input[type='button'].button-clear,input[type='reset'].button-clear,input[type='submit'].button-clear{background-color:transparent;border-color:transparent;color:#9b4dca}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type='button'].button-clear:focus,input[type='button'].button-clear:hover,input[type='reset'].button-clear:focus,input[type='reset'].button-clear:hover,input[type='submit'].button-clear:focus,input[type='submit'].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type='button'].button-clear[disabled]:focus,input[type='button'].button-clear[disabled]:hover,input[type='reset'].button-clear[disabled]:focus,input[type='reset'].button-clear[disabled]:hover,input[type='submit'].button-clear[disabled]:focus,input[type='submit'].button-clear[disabled]:hover{color:#9b4dca}code{background:#f4f5f6;border-radius:.4rem;font-size:86%;margin:0 .2rem;padding:.2rem .5rem;white-space:nowrap}pre{background:#f4f5f6;border-left:0.3rem solid #9b4dca;overflow-y:hidden}pre>code{border-radius:0;display:block;padding:1rem 1.5rem;white-space:pre}hr{border:0;border-top:0.1rem solid #f4f5f6;margin:3.0rem 0}input[type='color'],input[type='date'],input[type='datetime'],input[type='datetime-local'],input[type='email'],input[type='month'],input[type='number'],input[type='password'],input[type='search'],input[type='tel'],input[type='text'],input[type='url'],input[type='week'],input:not([type]),textarea,select{-webkit-appearance:none;background-color:transparent;border:0.1rem solid #d1d1d1;border-radius:.4rem;box-shadow:none;box-sizing:inherit;height:3.8rem;padding:.6rem 1.0rem .7rem;width:100%}input[type='color']:focus,input[type='date']:focus,input[type='datetime']:focus,input[type='datetime-local']:focus,input[type='email']:focus,input[type='month']:focus,input[type='number']:focus,input[type='password']:focus,input[type='search']:focus,input[type='tel']:focus,input[type='text']:focus,input[type='url']:focus,input[type='week']:focus,input:not([type]):focus,textarea:focus,select:focus{border-color:#9b4dca;outline:0}select{background:url('data:image/svg+xml;utf8,') center right no-repeat;padding-right:3.0rem}select:focus{background-image:url('data:image/svg+xml;utf8,')}select[multiple]{background:none;height:auto}textarea{min-height:6.5rem}label,legend{display:block;font-size:1.6rem;font-weight:700;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type='checkbox'],input[type='radio']{display:inline}.label-inline{display:inline-block;font-weight:normal;margin-left:.5rem}.container{margin:0 auto;max-width:112.0rem;padding:0 2.0rem;position:relative;width:100%}.row{display:flex;flex-direction:column;padding:0;width:100%}.row.row-no-padding{padding:0}.row.row-no-padding>.column{padding:0}.row.row-wrap{flex-wrap:wrap}.row.row-top{align-items:flex-start}.row.row-bottom{align-items:flex-end}.row.row-center{align-items:center}.row.row-stretch{align-items:stretch}.row.row-baseline{align-items:baseline}.row .column{display:block;flex:1 1 auto;margin-left:0;max-width:100%;width:100%}.row .column.column-offset-10{margin-left:10%}.row .column.column-offset-20{margin-left:20%}.row .column.column-offset-25{margin-left:25%}.row .column.column-offset-33,.row .column.column-offset-34{margin-left:33.3333%}.row .column.column-offset-40{margin-left:40%}.row .column.column-offset-50{margin-left:50%}.row .column.column-offset-60{margin-left:60%}.row .column.column-offset-66,.row .column.column-offset-67{margin-left:66.6666%}.row .column.column-offset-75{margin-left:75%}.row .column.column-offset-80{margin-left:80%}.row .column.column-offset-90{margin-left:90%}.row .column.column-10{flex:0 0 10%;max-width:10%}.row .column.column-20{flex:0 0 20%;max-width:20%}.row .column.column-25{flex:0 0 25%;max-width:25%}.row .column.column-33,.row .column.column-34{flex:0 0 33.3333%;max-width:33.3333%}.row .column.column-40{flex:0 0 40%;max-width:40%}.row .column.column-50{flex:0 0 50%;max-width:50%}.row .column.column-60{flex:0 0 60%;max-width:60%}.row .column.column-66,.row .column.column-67{flex:0 0 66.6666%;max-width:66.6666%}.row .column.column-75{flex:0 0 75%;max-width:75%}.row .column.column-80{flex:0 0 80%;max-width:80%}.row .column.column-90{flex:0 0 90%;max-width:90%}.row .column .column-top{align-self:flex-start}.row .column .column-bottom{align-self:flex-end}.row .column .column-center{align-self:center}@media (min-width: 40rem){.row{flex-direction:row;margin-left:-1.0rem;width:calc(100% + 2.0rem)}.row .column{margin-bottom:inherit;padding:0 1.0rem}}a{color:#9b4dca;text-decoration:none}a:focus,a:hover{color:#606c76}dl,ol,ul{list-style:none;margin-top:0;padding-left:0}dl dl,dl ol,dl ul,ol dl,ol ol,ol ul,ul dl,ul ol,ul ul{font-size:90%;margin:1.5rem 0 1.5rem 3.0rem}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1.0rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:2.5rem}table{border-spacing:0;display:block;overflow-x:auto;text-align:left;width:100%}td,th{border-bottom:0.1rem solid #e1e1e1;padding:1.2rem 1.5rem}td:first-child,th:first-child{padding-left:0}td:last-child,th:last-child{padding-right:0}@media (min-width: 40rem){table{display:table;overflow-x:initial}}b,strong{font-weight:bold}p{margin-top:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2.0rem;margin-top:0}h1{font-size:4.6rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:2.8rem;line-height:1.3}h4{font-size:2.2rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}img{max-width:100%}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right} + +/*# sourceMappingURL=milligram.min.css.map */ \ No newline at end of file diff --git a/sources/MVCFramework.Commons.pas b/sources/MVCFramework.Commons.pas index 7a4bcde8..77b2ea4b 100644 --- a/sources/MVCFramework.Commons.pas +++ b/sources/MVCFramework.Commons.pas @@ -247,7 +247,17 @@ type /// NotModified = 304; UseProxy = 305; + /// + /// HTTP 307 Temporary Redirect redirect status response code indicates that the resource requested has been temporarily moved to the URL given by the Location headers. + /// The method and the body of the original request are reused to perform the redirected request. In the cases where you want the method used to be changed to GET, use 303 See Other instead. This is useful when you want to give an answer to a PUT method that is not the uploaded resources, but a confirmation message (like "You successfully uploaded XYZ"). + /// The only difference between 307 and 302 is that 307 guarantees that the method and the body will not be changed when the redirected request is made. With 302, some old clients were incorrectly changing the method to GET: the behavior with non-GET methods and 302 is then unpredictable on the Web, whereas the behavior with 307 is predictable. For GET requests, their behavior is identical. + /// TemporaryRedirect = 307; + /// + /// The HyperText Transfer Protocol (HTTP) 308 Permanent Redirect redirect status response code indicates that the resource requested has been definitively moved to the URL given by the Location headers. A browser redirects to this page and search engines update their links to the resource (in 'SEO-speak', it is said that the 'link-juice' is sent to the new URL). + /// The request method and the body will not be altered, whereas 301 may incorrectly sometimes be changed to a GET method. + /// + PermanentRedirect = 308; // Client Error 4xx /// /// The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications. diff --git a/sources/MVCFramework.pas b/sources/MVCFramework.pas index 912e3cff..f87a7aa3 100644 --- a/sources/MVCFramework.pas +++ b/sources/MVCFramework.pas @@ -742,6 +742,7 @@ type UnsupportedMediaTypeResult ConflictResult InternalServerErrorResult + RedirectResult } function OKResponse(const Body: TObject): IMVCResponse; overload; @@ -754,7 +755,8 @@ type function BadRequestResponse(const Error: TObject): IMVCResponse; overload; function CreatedResponse(const Location: string = ''; const Body: TObject = nil): IMVCResponse; function AcceptedResponse(const Location: string = ''; const Body: TObject = nil): IMVCResponse; - function ConflictResult: IMVCResponse; + function ConflictResponse: IMVCResponse; + function RedirectResponse(Location: String; Permanent: Boolean = False; PreserveMethod: Boolean = False): IMVCResponse; function InternalServerErrorResponse: IMVCResponse; /// @@ -3968,7 +3970,7 @@ begin Result := StatusCodeResponseWithOptionalBody(HTTP_STATUS.BadRequest, Error); end; -function TMVCRenderer.ConflictResult: IMVCResponse; +function TMVCRenderer.ConflictResponse: IMVCResponse; begin Result := StatusCodeResponseWithOptionalBody(HTTP_STATUS.Conflict, nil); end; @@ -4078,6 +4080,35 @@ end; end; +function TMVCRenderer.RedirectResponse(Location: String; Permanent: Boolean = False; PreserveMethod: Boolean = False): IMVCResponse; +var + lBuilder: IMVCResponseBuilder; +begin + lBuilder := MVCResponseBuilder.Header('location', Location); + if Permanent then + begin + if PreserveMethod then + begin + Result := lBuilder.StatusCode(HTTP_STATUS.TemporaryRedirect).Build; + end + else + begin + Result := lBuilder.StatusCode(HTTP_STATUS.MovedPermanently).Build; + end; + end + else + begin + if PreserveMethod then + begin + Result := lBuilder.StatusCode(HTTP_STATUS.PermanentRedirect).Build; + end + else + begin + Result := lBuilder.StatusCode(HTTP_STATUS.Found).Build; + end; + end; +end; + function TMVCRenderer.InternalServerErrorResponse: IMVCResponse; begin Result := StatusCodeResponseWithOptionalBody(HTTP_STATUS.InternalServerError, nil);