#1 Re: mORMot 2 » EMvcApplication.GotoError » 2025-10-10 16:59:40

mormot '2.3.11868'

When running in the Lazarus IDE, and when running directly, it crashes. I don’t want the site to stop; if I execute this directly, the backend crashes, because your example was written that way, so I tested it.

I now have a requirement: if the user is not logged in, do not render the HTML code—return a notice page or return an informational message directly. Otherwise, I want to wrap the entire frontend source with {{#CookieData.Status}} ALL HTML {{/CookieData.Status}}.

I’m not sure whether my idea and direction are correct, or if there are other methods! I’m using an interface-based approach, and it seems I can’t use the gotoerror method, so I’m lost.

#2 mORMot 2 » EMvcApplication.GotoError » 2025-10-10 16:04:39

testgary
Replies: 3
procedure TBlogApplication.AuthorView(var ID: TID; var Author: TOrmAuthor; out Articles: variant);
begin
  RestModel.Orm.Retrieve(ID, Author);
  Author.HashedPassword := ''; 
  if Author.ID <> 0 then
    Articles := RestModel.Orm.RetrieveDocVariantArray(TOrmArticle, '',
      'Author=? order by RowId desc limit 50', [ID], ARTICLE_FIELDS)
  else
    EMvcApplication.GotoError(HTTP_NOTFOUND);
end; 

When opening this link http://localhost:8092/blog/AuthorView, the backend directly exits and an exception message pops up.

#3 Re: mORMot 2 » Authentication » 2025-09-27 14:11:49

  TAppServer = class(TRestServerDB)
    // There is no "CurrentSession" method in the IMvcApplication.
    // FMvcApp: IMvcApplication;

    // I changed it to this.
    FMvcApplication: TMvcApplication; 
    FAppService: TAppService;
  end;

// In MvcApp, it will prompt that the "CurrentSession" identifier cannot be found.
// result := TAppServer(Server).MvcApp.CurrentSession.CheckAndRetrieve(@S, TypeInfo(TCookieData)) > 0;

// I changed it to this.
result := TAppServer(Server).FMvcApplication.CurrentSession.CheckAndRetrieve(@S, TypeInfo(TCookieData)) > 0;

1、First of all, thank you very much for your help. With your method, the code runs well!
2、By the way, did you accidentally write a few lines of code incorrectly? I’ve put them in the comments. I’m not sure if I’m saying this correctly, because my skills are limited, so I’m not certain.

#4 Re: mORMot 2 » Authentication » 2025-09-26 05:58:16

ab wrote:

3) and 4) mixing TMvcApplication and TRestServer is not something natural and easy.
Just stick with interface based services.

How can TMvcApplication and TRestServer use the same authentication system? I'm a bit confused now. I don't know how the interface-based method can share authentication with TMvcApplication. It's impossible that I have to log in to the website and then log in to TRestServer again, right?

#5 mORMot 2 » Authentication » 2025-09-25 17:39:36

testgary
Replies: 6

Question 1:
When building a website with TMvcApplication, if the frontend uses JavaScript to obtain data from the backend, what approach is preferable? For example, backend user management (create/read/update/delete), article management (create/read/update/delete), etc.

Question 2:
Currently I am using the callback type TOnRestServerCallBack = procedure(Ctxt: TRestServerUriContext) of object; I’m not sure if this direction is correct, or if there are better options.

Question 3:
If my direction is fine, when the frontend calls the backend TOnRestServerCallBack method, I always need to use CurrentSession.CheckAndRetrieve to validate the data in CookieData to check whether the user is logged in and has the corresponding permissions (since some operations require certain permissions). Is there another approach, because I feel that validating in every method and writing the same code is not a good solution?

Question 4:
procedure TRestServer.Auth(Ctxt: TRestServerUriContext);
From what I’ve seen in TRestServerDB’s authentication methods, it seems different from the validation inside TMvcApplication. In other words, even if I set ServiceMethodRegister’s aByPassAuthentication to False, I can’t share the validation with TMvcApplication and must write validation separately, which is a bit troublesome. Is there another approach?

procedure TBlogApplication.Delete(Ctxt: TRestServerUriContext);
var
    CookieData: TCookieData;
begin
    if CurrentSession.CheckAndRetrieve(@CookieData, TypeInfo(TCookieData)) > 0 then  
    ...
end;  

// Here I enabled TRestServerDB’s validation
(aRestModel as TRestServerDB).ServiceMethodRegister('Delete', Delete, False, [mGET]);            

#6 Re: mORMot 2 » TAuthGroup » 2025-09-24 10:33:36

ab wrote:

It is documented as such.
This is part of the optimization of header names lookup.


This is truly perplexing and very prone to causing mistakes.

Could we make a change so that friends who come later won't waste time checking for mistakes at this place?

#7 mORMot 2 » TAuthGroup » 2025-09-24 09:17:31

testgary
Replies: 4

// I don't know whether it's a mistake on my part or something else. Here, "HOST" can only be in uppercase; lowercase doesn't yield any data.
Host := Ctxt.Call^.Header('HOST:')

Also, are there any examples of TAuthGroup + TAuthUser + MongoDB

#8 mORMot 2 » CreateJoined » 2025-09-20 09:55:45

testgary
Replies: 0
procedure TMvcApplication.Test(Ctxt: TRestServerUriContext);
...
begin
  // No data can be obtained here.
  ORM := TORMInfo.CreateJoined(RestModel.Orm, 1);
end;

#9 mORMot 2 » Exception handling » 2025-09-11 09:40:14

testgary
Replies: 1
      try
        RestModel.Orm.Add(ORM, 'test');
      except
        // It seems that EngineAdd swallows the exceptions raised by RaiseUtf8, and I can't catch them. I'm not sure if I'm right.
        on E: ESynException do
        ...
      end;  

function TRestStorageMongoDB.EngineAdd(TableModelIndex: integer;
  const SentData: RawUtf8): TID; 

class procedure ESynException.RaiseUtf8(const Format: RawUtf8;
  const Args: array of const);
begin
  raise CreateUtf8(Format, Args);
end; 

The EngineAdd method throws an exception while returning 0. Thus, I was able to catch it.

#10 Re: mORMot 2 » stored AS_UNIQUE » 2025-09-11 07:10:09

class procedure TORMTest.InitializeTable(const Server: IRestOrmServer; const FieldName: RawUtf8; Options: TOrmInitializeTableOptions);
begin
  // inherited InitializeTable(Server, FieldName, Options);
  Server.CreateSqlIndex(TORMTest, ['User_ID', 'create_time', 'update_time', 'subject'], False);
  Server.CreateSqlIndex(TORMTest, 'Idempotency_Key', True);
end;

I just checked and found that commenting out "inherited" would work. As for whether to include "stored AS_UNIQUE", it seems to make no difference. I haven't written the log yet.

#11 mORMot 2 » stored AS_UNIQUE » 2025-09-10 08:03:45

testgary
Replies: 2

mongodb+windows+lazarus

  TORMTest= class(TORM)
  private
    FIdempotency_Key: RawUtf8;
  public
    class procedure InitializeTable(const Server: IRestOrmServer; const FieldName: RawUtf8; Options: TOrmInitializeTableOptions); override;
  published
    property Idempotency_Key: RawUtf8 read FIdempotency_Key write FIdempotency_Key stored AS_UNIQUE; 
  end;  

class procedure TORMTest.InitializeTable(const Server: IRestOrmServer; const FieldName: RawUtf8; Options: TOrmInitializeTableOptions);
var
  B: boolean;
begin
  inherited InitializeTable(Server, FieldName, Options);
  // B := Server.CreateSqlIndex(TORMTest, ['User_ID', 'create_time', 'update_time', 'subject'], False);
  // B := Server.CreateSqlIndex(TORMTest, 'Idempotency_Key', True);
  B := Server.CreateSqlMultiIndex(TORMTest, ['Idempotency_Key'], True);
end; 

Why isn't Idempotency_Key a unique index? Did I make a mistake somewhere?

#12 mORMot 2 » mormot.core.mustache » 2025-08-24 04:36:42

testgary
Replies: 0

Is it necessary to add a special check for the value 0? Otherwise, using {{^section}}...{{/section}} could be problematic.

#13 Re: mORMot 2 » i18n » 2025-08-11 08:36:05

ab wrote:

In the mustache template, any {{"English text}} tag will be passed as OnStringTranslate('English text') to the corresponding callback for rendering.

Would you like fully translated file resources, e.g. per-language HTML view files?


I'm familiar with this method. However, I don't think it's a good solution. I believe the HTML view files classified by language would be better. But I have no experience in this area and can't offer you any constructive suggestions.

#14 Re: mORMot 2 » i18n » 2025-08-11 05:34:36

My current requirement is that the website needs to be accessible to users of multiple different languages. If the website is in a single language, it would be a bit troublesome.

#15 mORMot 2 » i18n » 2025-08-10 13:12:50

testgary
Replies: 4

Are there any mature internationalization solutions available at present? Solutions for multiple languages

#16 Re: mORMot 2 » ServiceMethodRegister CurrentSession » 2025-08-07 04:52:50


I did nothing. I used the "mvc-blog" example.

windows 11
Lazarus 4.0RC3 (rev lazarus_4_0RC3) FPC 3.2.2 x86_64-win64-win32/win64

#0 TWrapperContext.ContextFromRtti(nil, wUnknown, TRttiCustom($000000000169C3C0), '', '') at mormot.soa.codegen.pas:685
#1 TWrapperContext.ContextArgsFromMethod(nil, TInterfaceMethod (''; ''; ''; 0; []; []; 0; 0; 0; 0; nil; 0; 0; 0; 0; 0; 0; 0; 0; 0; []; 100398128; nil; (''); nil; (4, 0, 0, 0, 0, 0, 0, 0, 187, 179))) at mormot.soa.codegen.pas:886
#2 TWrapperContext.ContextFromMethod(TWrapperContext($0000000005FBF278), Failed to read data from register (while calculating location)) at mormot.soa.codegen.pas:938
#3 TWrapperContext.ContextFromMethods(TWrapperContext($0000000005FBF278), Failed to read data from register) at mormot.soa.codegen.pas:994
#4 ContextFromMethods(Failed to read data from register) at mormot.soa.codegen.pas:1128
#5 TMvcApplication.GetMvcInfo(TMvcApplication($0000000005FBF278), Failed to read data from register (while calculating location)) at mormot.rest.mvc.pas:2663
#6 TMvcRunWithViews.ComputeMvcInfoCache(TMvcRunWithViews($0000000005FBF278)) at mormot.rest.mvc.pas:2084
#7 TMvcRunOnRestServer.InternalRunOnRestServer(TMvcRunOnRestServer($00000000016018D0), TRestServerUriContext($0000000007499710), 'mvc-info') at mormot.rest.mvc.pas:2220
#8 TMvcRunOnRestServer.RunOnRestServerRoot(TMvcRunOnRestServer($0000000005FBF278), Failed to read data from register) at mormot.rest.mvc.pas:2298
#9 TRestServerUriContext.ExecuteSoaByMethod(TRestServerUriContext($0000000005FBF278)) at mormot.rest.server.pas:3264

#17 Re: mORMot 2 » ServiceMethodRegister CurrentSession » 2025-08-06 15:22:22

This URL is incorrect
http://localhost:8092/blog/mvc-info

#18 mORMot 2 » ServiceMethodRegister CurrentSession » 2025-08-04 07:40:33

testgary
Replies: 4
procedure TMvcApplication.Test(Ctxt: TRestServerUriContext);
var
  CookieData: TCookieData;
begin
  // There was an error in this place. Before, I could do it this way and it worked!
  // After synchronizing the latest code, here are the issues.
  if CurrentSession.CheckAndRetrieve(@CookieData, TypeInfo(TCookieData)) > 0 then;
end;  

procedure TMvcApplication.Start(aRestModel: TRest);
begin
  (aRestModel as TRestServerDB).ServiceMethodRegister('Test', Test, True, [mGET]);
end; 

#19 Re: mORMot 2 » ACME + TMVCApplication » 2025-08-02 20:03:01

Thank you very much for your efforts! I'm really happy to see how powerful mormot has become.

#20 Re: mORMot 2 » ACME + TMVCApplication » 2025-07-27 08:06:22

type
  Tzerossl = class
  private
    FAcmeClient: TAcmeClient;
    FICryptCert: ICryptCert;
    FChallengeRec: ^TAcmeChallenge;
  protected
    procedure DoChallenges(Sender: TObject; const Domain, Key, Token: RawUtf8);
  public
    constructor Create;
    destructor Destroy; override;

    procedure start;
    procedure CheckCertificate(Sender: TObject);
    procedure CheckCertificatesBackground;
  published
  end;


implementation

const
  ACME_ZEROSSL_URL = 'https://acme.zerossl.com/v2/DV90';
  ACME_CHALLENGE_PATH = '/.well-known/pki-validation/';

procedure Tzerossl.DoChallenges(Sender: TObject; const Domain, Key, Token: RawUtf8);
begin

  if Key <> '' then
  begin
    FChallengeRec^.Token := Token;
    FChallengeRec^.Key := Key;
  end;
end;

constructor Tzerossl.Create;
begin

  inherited Create;
  New(FChallengeRec);
  FChallengeRec^ := Default(TAcmeChallenge);


  FICryptCert := Cert('x509-es256');
  FAcmeClient := TAcmeClient.Create(nil, FICryptCert, ACME_ZEROSSL_URL, 'tempEmail@temp.com', 'www.temp.com');
  // FAcmeClient.OnChallenges := @DoChallenges;
end;

destructor Tzerossl.Destroy;
begin
  if Assigned(FChallengeRec) then
  begin
    Dispose(FChallengeRec);
    FChallengeRec := nil;
  end;

  FAcmeClient.Free;
  inherited Destroy;
end;

procedure Tzerossl.start;
var
  Terminated: boolean = False;
  SSLDir: TFileName;
const
  WaitForSec = 30; 
  PrivateKeyPassword = 'tempPwd';
begin
  SSLDir := IncludeTrailingPathDelimiter(GetCurrentDir) + 'ssl' + PathDelim;
  if not DirectoryExists(SSLDir) then
    ForceDirectories(SSLDir);

  if FAcmeClient.RegisterAndWait(@DoChallenges, SSLDir + 'mycert.pen', SSLDir + 'privkey.pem', PrivateKeyPassword, WaitForSec, @Terminated) = TAcmeStatus.asValid then
  begin
    // ok
  end;

end;

procedure Tzerossl.CheckCertificate(Sender: TObject);
var
  ExpiryDate: TDateTime;
  SSLDIR: TFileName;
  CertFile: TFileName;
const
  RenewBeforeDays = 30;
begin
  SSLDIR := IncludeTrailingPathDelimiter(GetCurrentDir) + 'ssl' + PathDelim;
  CertFile := SSLDIR + 'mycert.pen';

  if not FileExists(CertFile) then Exit;
  if not FICryptCert.LoadFromFile(CertFile) then Exit;

  ExpiryDate := FICryptCert.GetNotAfter;
  if ExpiryDate < (NowUtc + RenewBeforeDays) then
    start;
end;

procedure Tzerossl.CheckCertificatesBackground;
begin
  TLoggedWorkThread.Create(TSynLog, 'CheckCertificate', self, @CheckCertificate, nil, False);
end;

Right now I’m using a ZeroSSL certificate instead of a Let’s Encrypt one. The snippets above show some code, but there’s no documentation and I don’t know how to write it. I’m using TMVCApplication and I need help

#21 Re: mORMot 2 » Access violation writing to address » 2025-06-23 09:14:31

#0 ntdll:RtlInitializeResource+1251 at :0
#1 ntdll:RtlRaiseStatus+1167 at :0
#2 ntdll:RtlEnterCriticalSection+242 at :0
#3 InitThreadNumber(PSynLogThreadInfo($000000000A7EFFD8)) at mormot.core.log.pas:4628
#4 TSynLog.LockAndDisableExceptions(Failed to read data from register) at mormot.core.log.pas:4716
#5 TSynLog.LogInternalFmt(TSynLog($00000000014DCB40), sllTrace, $000000010057AAF8^: 'UnSubscribe(socket=% seq=%) cnt=% %', PVarRec($000000000A307A98), 4, TObject($00000000014E6930)) at mormot.core.log.pas:5782
#6 TSynLog.DoLog(Internal dereference error, Failed to read data from register, Internal dereference error, Failed to read data from register (while calculating location), Failed to read data from register, Failed to read data from register) at mormot.core.log.pas:5261
#7 TWinIocp.Unsubscribe(Failed to read data from register, Internal dereference error) at mormot.net.sock.windows.inc:1947
#8 TPollAsyncSockets.Stop(TPollAsyncSockets($00000000015075B0), TPollAsyncConnection($000000000AB193B0), 'Fatal Error') at mormot.net.async.pas:1751
#9 TPollAsyncSockets.CloseConnection(TPollAsyncSockets($00000000015075B0), nil, 'Fatal Error') at mormot.net.async.pas:2010

windows11 lazarus 4.0

#22 Re: mORMot 2 » Access violation writing to address » 2025-06-23 07:23:06

unit mormot.core.log;
procedure InitThreadNumber(nfo: PSynLogThreadInfo);
GlobalThreadLock.Lock;  // It seems that the problem lies with this method.

windows11 lazarus 4.0RC

#23 Re: mORMot 2 » Access violation writing to address » 2025-06-22 19:37:27

flydev wrote:

I can't reproduce it - it work as expected at least on fpc 3.2.2.

edit: ... and mormot 2.3.10775

For testing, I downloaded the latest Lazarus 4.0 RC + mORMot ('2.3.11235'), but I still reproduced the error. I'm not sure if it's my issue or if the code itself is designed that way — it seems necessary to explicitly add the freeing code on exit.

I added the freeing code at the end before exiting, and then the problem did not occur.

Steps to reproduce the error:

Create a new form application, call the units from the mvc-blog example and other necessary units
Run the form application and start it
Open https://localhost/blog/default
Close the form
An error dialog pops up, reproducing the error.


uses
   MVCModel, MVCViewModel;

var
  Form1: TForm1;

  aModel: TOrmModel;
  aServer: TRestServerDB;
  aApplication: TBlogApplication;
  aHTTPServer, aHTTPSServer: TRestHttpServer;
  LogFamily: TSynLogFamily;

implementation

{$R *.lfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  aModel := CreateModel;

  aServer := TRestServerDB.Create(aModel, ChangeFileExt(Executable.ProgramFileName, '.db'));

  aServer.DB.Synchronous := smNormal;
  aServer.DB.LockingMode := lmExclusive;
  aServer.Server.CreateMissingTables;
  aApplication := TBlogApplication.Create;

  aApplication.Start(aServer);
  aHTTPServer := TRestHttpServer.Create('8092', aServer, '+', HTTP_DEFAULT_MODE, nil, 16, secNone, '', '', HTTPSERVER_DEBUG_OPTIONS);

  aHTTPSServer := TRestHttpServer.Create([aServer], '443', 32, secTLS, HTTPSERVER_DEBUG_OPTIONS,
    'C:\Users\ssl\mycert.pfx',
    'C:\Users\ssl\privkey.pem',
    '|&SSSv;"f',
    'C:\Users\ssl\cert.pem');
  aHTTPSServer.RootRedirectToURI('blog/default');
  aServer.RootRedirectGet := 'blog/default';

  aHTTPServer.RootRedirectToURI('blog/default'); // redirect / to blog/default
  aServer.RootRedirectGet := 'blog/default';     // redirect blog to blog/default
end;

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  aHTTPServer.Free;
  aHTTPSServer.Free;
  aApplication.Free;
  aServer.Free;
end;    

#24 mORMot 2 » Access violation writing to address » 2025-06-21 19:34:22

testgary
Replies: 8
implementation

{$R *.lfm}

var
  aModel: TOrmModel;
  aServer: TRestServerDB;
  aApplication: TBlogApplication;
  aHTTPServer: TRestHttpServer;
  aHTTPServer443: TRestHttpServer;
  LogFamily: TSynLogFamily;

procedure TForm1.Button1Click(Sender: TObject);
begin
  aModel := CreateModel;
  aServer := TRestServerDB.Create(
    aModel, ChangeFileExt(Executable.ProgramFileName, '.db'));
  aServer.DB.Synchronous := smNormal;
  aServer.DB.LockingMode := lmExclusive;
  aServer.Server.CreateMissingTables;
  aApplication := TBlogApplication.Create;
  aApplication.Start(aServer);

  aHTTPServer443 := TRestHttpServer.Create([aServer], '443', 32, secTLS, HTTPSERVER_DEBUG_OPTIONS,
    'C:\Users\aaa\Desktop\ssl\mycert.pfx',
    'C:\Users\aaa\Desktop\ssl\privkey.pem',
    '|sBcaVv;"f',
    'C:\Users\aaa\Desktop\ssl\cert.pem');
  aHTTPServer443.RootRedirectToURI('blog/default');
  aServer.RootRedirectGet := 'blog/default';


  aHTTPServer := TRestHttpServer.Create('8092', aServer, '+',
    HTTP_DEFAULT_MODE, nil, 16, secNone, '', '', HTTPSERVER_DEBUG_OPTIONS);
  aHTTPServer.RootRedirectToURI('blog/default'); // redirect / to blog/default
  //aHTTPServer.RootRedirectToURI('blog/default', True, True);
  aServer.RootRedirectGet := 'blog/default';     // redirect blog to blog/default
end;   

Convert the mvc-blog example into a program with a form, without making any other changes. Start the program, then open the web page, and then exit the program. At this point, the following error message will appear: "Access violation writing to address $0000000000000024."

I am using Lazarus 3.0.

Of course, you need to access it via https.     https://localhost/blog/default

#25 mORMot 2 » TJsonClient + TSynThreadPool » 2025-06-16 05:03:06

testgary
Replies: 1

I need to execute the TJsonClient.Request method inside a thread pool task, but I have a problem: how can I pass an array of const parameter?

In such a situation, how would you operate?

I looked at the official code and found that it directly used the addresses of records and classes.However, both records and classes cannot pass an array of const.

procedure TCustomThreadPool.Task(aCaller: TSynThreadPoolWorkThread; aContext: pointer);
begin
  TJsonClient.Request(const Method, ActionFmt: RawUtf8;
      const ActionArgs, QueryNameValueParams, HeaderNameValueParams: array of const;
      var Res; ResInfo: PRttiInfo;
      const CustomError: TOnJsonClientError = nil);
end; 

#26 Re: mORMot 2 » TJsonClient » 2025-06-15 05:42:13

ab wrote:

There is not enough RTTI on FPC 3.2.2 to define the record fields.

You need to describe your record with a text definition in your code.
This is what is done for example with our OpenAPI generator.


It's done! Thank you.

#27 Re: mORMot 2 » TJsonClient » 2025-06-14 17:12:31

ab wrote:

On which compiler?

lazarus 3.0
FPC 3.2.2
WIN64

#28 mORMot 2 » TJsonClient » 2025-06-14 15:10:23

testgary
Replies: 4

I'm not sure if what I wrote is correct. DataRec can't get the data.

type
  TDataRec = packed record
    userId: integer;
    id:     integer;
    title:  RawUtf8;
    completed: boolean;
  end;
  PDataRec = ^TDataRec;
  TDataRecArr = array of TDataRec;

procedure TestJsonClient;

implementation

procedure TestJsonClient;
var
  JsonClient: TJsonClient;
  DataRecArr: TDataRecArr;
  DataRec:    TDataRec;
begin
  FillChar(DataRecArr, SizeOf(DataRecArr), 0);
  JsonClient := TJsonClient.Create('https://jsonplaceholder.typicode.com');
  JsonClient.Request('GET', 'todos/%', [1], [], [], DataRec, TypeInfo(TDataRec), nil);
end;

The following is the returned data.

https://jsonplaceholder.typicode.com/todos/1

{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

#29 Re: mORMot 2 » Differences between backend Mustache rendering in Mormot and frontend » 2025-06-13 11:26:42

This is really a headache-inducing issue. The Mustache official specification should resolve this problem, or we could implement this functionality directly in the Pascal code. Some online Mustache implementations already treat empty strings as true, and I think this doesn't pose any security risk because {{#section}}...{{/section}} and {{^section}}...{{/section}} are paired constructs. At least in general understanding, they are supposed to work that way; otherwise, it would truly cause confusion.

For just this small feature, I really have to write some code to handle it—it’s definitely not convenient.

#30 Re: mORMot 2 » Differences between backend Mustache rendering in Mormot and frontend » 2025-06-13 02:29:07

I used the mvc-blog example and modified the following code in the articlerow.partial file, but the functionality did not meet my expectations:

<h2 class="blog-post-title"><a href=articleView?id={{id}}>{{Title}}</a></h2>
<h2 class="blog-post-title"><a href=articleView?id={{id}}>{{Title}}{{^Title}}000{{/Title}}</a></h2>

I just checked the mvc-blog source code and found that {{^ }} (the inverted section) is always used with Boolean, Array, or null types. It seems it’s not used with strings. Could it be that it doesn’t work with strings?

I also looked up the inverted.json file but it seems there’s no operation for empty strings.

However, if the following code is rendered on the frontend with JavaScript calling Mustache.min.js, it works fine. I guess it works because my code is similar and also rendered on the frontend:

<h2 class="blog-post-title"><a href=articleView?id={{id}}>{{Title}}{{^Title}}000{{/Title}}</a></h2>

I saw the mormot.core.mustache unit’s RenderContext code and it looks like empty strings are not supported:

procedure TSynMustache.RenderContext(Context: TSynMustacheContext;
  TagStart, TagEnd: PtrInt); 

      mtInvertedSection:
        // display section for no key, false value, or empty list
        if Context.AppendSection(t^.ValueSpace, t^.Value) <> msNothing then
        begin
          TagStart := t^.SectionOppositeIndex;
          continue; // ignore whole section
        end; 

I haven’t looked deeper into the code yet, and I might have said something wrong.

#31 Re: mORMot 2 » Differences between backend Mustache rendering in Mormot and frontend » 2025-06-12 09:43:08

I don’t know where I went wrong; the code is still not executing as I expected.

The name field in the MongoDB database is of string type and can be empty "".

{{name}} is correct — it shows empty if the value is empty, or if I write a character in the database, it displays correctly.

I am using TMVCApplication, and these templates are all placed in the frontend HTML.

{
  "Result": {
    "Data": [
      {
        "Name": ""
        "Sex": "Male"
      },
      {
        "Name": "1"
        "Sex": "Male"
      }
      {
        "Name": "2"
        "Sex": "Male"
      }
    ]
  }
}

None of the following work:

{{#if name, "=", ""}}No name{{/if}}
{{#if name =, ""}}No name{{/if}}
{{#if name,"=",""}}No name{{/if}} 
{{#if name=""}}No name{{/if}}   

{{^name}}No name{{/name}}

{{#Equals name,""}}No name{{/Equals name,""}}
{{#Equals name,""}}No name{{/Equals}}

The link you gave me, I also looked at it, but I still don't understand.
Is it that strings cannot be compared?

I wrote a RegisterExpressionHelpers myself and got it working, but I still want to understand where I went wrong.

#32 Re: mORMot 2 » Differences between backend Mustache rendering in Mormot and frontend » 2025-06-12 06:45:59

Today I checked, and if Name is empty, the code below doesn't work at all.

{{#Result.Data}}
  {{^Result.Data.Name}}Unknown{{/Result.Data.Name}}
{{/Result.Data}}

{{#Result.Data}}
  {{^Name}}Unknown{{/Name}}
{{/Result.Data}}

#33 mORMot 2 » Differences between backend Mustache rendering in Mormot and frontend » 2025-06-11 17:50:58

testgary
Replies: 9

Differences between backend Mustache rendering in Mormot and frontend Mustache.min.js rendering:

Backend data:

{
  "Result": {
    "Data": [
      {
        "Name": "John"
      },
      {
        "Sex": "Male"
      }
    ]
  }
}

Backend Mustache rendering:

{{#Result.Data}}
  {{Name}}{{^Result.Data}}Unknown{{/Result.Data}}
{{/Result.Data}}

Frontend Mustache.min.js rendering:

{{#Result.Data}}
  {{Name}}{{^Data}}Unknown{{/Data}}
{{/Result.Data}}

One is a relative path, and the other is an absolute path
While writing the code, I found a small issue. I want to share it here so everyone can avoid spending hours troubleshooting the same problem.

Mustache.min.js is the official code I downloaded; my testing is not rigorous enough

#34 mORMot 2 » Hsts » 2025-06-07 14:28:56

testgary
Replies: 1
  TWebServerHsts = (
    wshOff,
    wshOn,
    wshIncludeSubDomains,
    wshIncludeSubDomainsPreload);   

In TMVCApplication, how to use `TWebServerHsts`?

#35 Re: mORMot 2 » ACME + TMVCApplication » 2025-06-07 11:35:48

Chaa wrote:

I created pull request to fix minor ACME client bug:
https://github.com/synopse/mORMot2/pull/367

First of all, thank you very much for sharing

I have reviewed your code and noticed that you associate tmvcapplication and acme through reading and writing local certificate files, rather than linking them directly in the code. Could this cause any issues, such as file access conflicts?

Also, it seems you did not use the RegisterAndWaitFolder CompleteDomainRegistration method. Have you tested your code to ensure it runs completely without any problems?

#36 Re: mORMot 2 » ACME + TMVCApplication » 2025-06-06 09:55:43

Sorry, I forgot to check before sending the content.

#37 mORMot 2 » ACME + TMVCApplication » 2025-06-06 07:08:13

testgary
Replies: 16
procedure ACMEStart;
var
  F: TFileName;
  ES: TAcmeLetsEncryptServer;
  I: integer;
  Status: TAcmeStatus;
  Cert, PrivateKey: RawUtf8;
begin
  RegisterOpenSsl;
  F := IncludeTrailingPathDelimiter(GetCurrentDir) + 'sslFolder';

  ES := TAcmeLetsEncryptServer.Create(TSynLog, F,
    ACME_LETSENCRYPT_DEBUG_URL,
    'x509-es256', 'jf93v83');
  try
    ES.LoadFromKeyStoreFolder;

    for I := Low(ES.Client) to High(ES.Client) do
      with ES.Client[I] do
      begin
        RegisterAndWaitFolder(F, F, F, 'jf93v83', 3);

        repeat
          Status := CheckChallengesStatus;
          Sleep(1000);
        until Status <> asPending;

        if Status = asValid then
        begin
          Status := ES.Client[I].CompleteDomainRegistration(Cert, PrivateKey, 'jf93v83');
        end;
      end;

    ES.CheckCertificatesBackground;
    ES.Redirect('xxx.com', 'https://xxx.com');
    ES.Redirect('www.xxx.com', 'https://www.xxx.com');
  finally
    ES.Free;
  end;

end;

Below is the JSON file, to be used by the LoadFromKeyStoreFolder method.

{
  "contact": "mailto:admin@xxx.com",
  "subjects": [
    "xxx.com"
    "www.com"
  ]
}   

1.Since I don't have a domain name or a public IP, I cannot perform testing. I’m not sure if what I wrote is correct. Is there anything else I should pay attention to?

2.How does ACME integrate with TMvcApplication? Should I directly call RegisterAndWaitFolder to save the certificate files, or how should it be handled?

  HttpServer := TRestHttpServer.Create([RestServerDB], '443', 32, secTLS, HTTPSERVER_DEFAULT_OPTIONS,
    'C:\Users\FBI\Desktop\BOOK\1\ssl\mycert.pfx',
    'C:\Users\FBI\Desktop\BOOK\1\ssl\privkey.pem',
    '|&VwVx;2S',                                      
    'C:\Users\FBI\Desktop\BOOK\1\ssl\cert.pem'
    ); 
  FRestHttpServer.DomainHostRedirect('xxx.com', 'root');
  FRestHttpServer.RootRedirectToURI('xxx.com', 'blog/default', true, true);

Board footer

Powered by FluxBB