Improved SPA support

This commit is contained in:
Daniele Teti 2020-05-14 17:41:20 +02:00
parent eb112316f7
commit 530084271b
5 changed files with 50 additions and 23 deletions

View File

@ -342,6 +342,7 @@ Render(lPerson, False,
- New! `TMVCActiveRecord.Count` method (e.g. `TMVCActiveRecord.Count(TCustomer)` returns the number of records for the entity mapped by the class `TCustomer`)
- Change! `TMVCACtiveRecord.GetByPK<T>` raises an exception by default if the record is not found - optionally can returns `nil` using new parameter `RaiseExceptionIfNotFound`
- New! `contains` clause has been added in the RQL compiler for Firebird and Interbase
- New! Added support out operator in RQL parser. The RQL out operator is equivalent to the SQL NOT IN operator.
- New! `TMVCAnalyticsMiddleware` to do automatic analytics on the API (generates a CSV file). Based on an idea by Nirav Kaku (https://www.facebook.com/nirav.kaku). Check the sample in `\samples\middleware_analytics\`
- New! `TMVCActiveRecord.DeleteAll` deletes all the records from a table
- New! `TMVCActiveRecord.DeleteRQL` deletes records using an `RQL` expression as `where` clause.

View File

@ -3,5 +3,6 @@
<body>
<h1>This is the index.html file</h1>
<h2>Folder: www</h2>
<script src="js/app.js"></script>
</body>
</html>

View File

@ -0,0 +1,4 @@
window.addEventListener('load', (event) => {
console.log('The page has fully loaded');
alert('The page has fully loaded');
});

View File

@ -124,7 +124,7 @@ constructor TMVCStaticFilesMiddleware.Create(
const AStaticFilesCharset: string = TMVCStaticFilesDefaults.STATIC_FILES_CONTENT_CHARSET);
begin
inherited Create;
fStaticFilesPath := AStaticFilesPath;
fStaticFilesPath := AStaticFilesPath;
fDocumentRoot := TPath.Combine(AppPath, ADocumentRoot);
fIndexDocument := AIndexDocument;
fStaticFilesCharset := AStaticFilesCharset;
@ -175,6 +175,23 @@ begin
Exit;
end;
{
If user ask for
www.server.it/folder
the browser is redirected to
www.server.it/folder/
}
if SameText(lPathInfo, fStaticFilesPath) then
begin
if (not lPathInfo.EndsWith('/')) and (not fIndexDocument.IsEmpty) then
begin
AContext.Response.StatusCode := HTTP_STATUS.MovedPermanently;
AContext.Response.CustomHeaders.Values['Location'] := lPathInfo + '/';
AHandled := True;
Exit;
end;
end;
if not((fStaticFilesPath = '/') or (fStaticFilesPath = '')) then
begin
lPathInfo := lPathInfo.Remove(0, fStaticFilesPath.Length);

View File

@ -223,6 +223,8 @@ type
[Test]
procedure TestDirectoryTraversal2;
[Test]
procedure TestDirectoryRedirect;
[Test]
procedure TestSPASupport;
// test server side views
[Test]
@ -1392,6 +1394,22 @@ begin
end;
end;
procedure TServerTest.TestDirectoryRedirect;
var
lRes: IRESTResponse;
begin
lRes := RESTClient
.Accept(TMVCMediaType.TEXT_HTML)
.doGET('/static/', []);
Assert.areEqual(200, lRes.ResponseCode, '/static/');
lRes := RESTClient
.Accept(TMVCMediaType.TEXT_HTML)
.doGET('/static', []);
Assert.areEqual(301, lRes.ResponseCode, '/static');
Assert.areEqual('/static/', lRes.HeaderValue('Location'), 'Wrong redirect');
end;
procedure TServerTest.TestDirectoryTraversal1;
var
lRes: IRESTResponse;
@ -1435,16 +1453,6 @@ begin
.doGET('/static.html', []);
Assert.areEqual(200, lRes.ResponseCode, '/static.html');
lRes := RESTClient
.Accept(TMVCMediaType.TEXT_HTML)
.doGET('/static/', []);
Assert.areEqual(200, lRes.ResponseCode, '/static/');
lRes := RESTClient
.Accept(TMVCMediaType.TEXT_HTML)
.doGET('/static', []);
Assert.areEqual(200, lRes.ResponseCode, '/static');
lRes := RESTClient
.Accept(TMVCMediaType.TEXT_HTML)
.doGET('/static/..\..\donotdeleteme.txt', []);
@ -1610,26 +1618,21 @@ var
begin
lRes := RESTClient
.Accept(TMVCMediaType.TEXT_HTML)
.doGET('/static/index.html', []);
.doGET('/spa/index.html', []);
Assert.areEqual(200, lRes.ResponseCode);
Assert.Contains(lRes.BodyAsString, 'This is a TEXT file');
lRes := RESTClient
.Accept(TMVCMediaType.TEXT_HTML)
.doGET('/static/', []);
.doGET('/spa/', []);
Assert.areEqual(200, lRes.ResponseCode, '/static/');
Assert.Contains(lRes.BodyAsString, 'This is a TEXT file');
lRes := RESTClient
.Accept(TMVCMediaType.TEXT_HTML)
.doGET('/static/..\..\donotdeleteme.txt', []);
Assert.areEqual(404, lRes.ResponseCode, '/static/..\..\donotdeleteme.txt');
lRes := RESTClient
.Accept(TMVCMediaType.TEXT_HTML)
.doGET('/static/../../donotdeleteme.txt', []);
Assert.areEqual(404, lRes.ResponseCode, '/static/../../donotdeleteme.txt');
Assert.Contains(lRes.Error.ExceptionMessage, 'Not Found', true);
.doGET('/spa/pippo/pluto/paperino', []);
Assert.areEqual(200, lRes.ResponseCode, '/spa/pippo/pluto/paperino');
Assert.Contains(lRes.BodyAsString, 'This is a TEXT file');
lUrl := 'Windows\win.ini';
for I := 1 to 30 do
@ -1637,8 +1640,9 @@ begin
lUrl := '..\' + lUrl;
lRes := RESTClient
.Accept(TMVCMediaType.TEXT_HTML)
.doGET('/' + lUrl, []);
Assert.areEqual(404, lRes.ResponseCode, 'Fail with: ' + '/' + lUrl);
.doGET('/spa/' + lUrl, []);
Assert.areEqual(200, lRes.ResponseCode);
Assert.Contains(lRes.BodyAsString, 'This is a TEXT file');
end;
end;