#1 Re: mORMot 1 » Blob from interface based method in Mormot2 » 2022-10-10 21:04:47

Thank you for your help @ab, it's exactly what I need.

IUserController =  interface(IInvokable)
  function GetPhoto(const ID: TID): TServiceCustomAnswer;
...

TUserController = class(TInjectableObjectRest, IUserController)
  public
    function GetPhoto(const ID: TID): TServiceCustomAnswer;
...

function TUserController.GetPhoto(const ID: TID): TServiceCustomAnswer;
var
  LPhoto: TSQLRawBlob;
begin
  if Server.RetrieveBlob(TORMUser, ID, 'Photo', LPhoto) then
  begin
    result.Header := HEADER_CONTENT_TYPE+JPEG_CONTENT_TYPE;
    result.Content := LPhoto;
    result.Status := 200;
  end
  else
  begin
    result.Header := HTML_CONTENT_TYPE_HEADER;
    result.Content := 'Not found';
    result.Status := 404;
  end;
end;

#2 mORMot 1 » Blob from interface based method in Mormot2 » 2022-10-09 22:37:21

talexone
Replies: 2

Got exception when returning blob:

procedure TUserController.GetPhoto(const Ctxt: TSQLRestServerURIContext; const ID: TID);
var
  blob: TSqlRawBlob;
begin
  Server.RetrieveBlob(TORMUser, ID, 'Photo', blob);
  Ctxt.ReturnBlob(blob, HTTP_PARTIALCONTENT, true, 'photo' + IntToStr(id));
end;

It seems Ctxt.Call is nil:

procedure TRestUriContext.ReturnBlob(const Blob: RawByteString;
  Status: integer; Handle304NotModified: boolean; const FileName: TFileName;
  CacheControlMaxAge: integer);
begin
  if not ExistsIniName(pointer(fCall^.OutHead), HEADER_CONTENT_TYPE_UPPER) then    <--- Exception here

Bug or feature?

Tried before with an out parameter, but response's Content-Type was 'application/json' instead of 'image/jpeg' and was refused by client:

procedure TUserController.GetPhoto(const ID: TID; out response: TSQLRawBlob);
begin
  Server.RetrieveBlob(TORMUser, ID, 'Photo', response);
end;

How to define a Content-Type for interface based method?

#3 Re: mORMot 1 » Column names in response of restful query changes with startIndex » 2022-07-18 13:48:23

Thank you for your help @ab, StaticVirtualTableDirect did the trick. I'll check if there is any performance issue with bigger table.
REST layer is very handy for basic CRUD operations with entities, and I use interface-based services for more complex requests.

#4 Re: mORMot 1 » Column names in response of restful query changes with startIndex » 2022-07-18 07:54:09

Which version of mORMOt do you use? : 1.8

Which database do you use? Firebird

How is the table defined?

 TSQLUser = class(TSQLRecord)
    private
      fUsername: RawUTF8;
      fHashedPassword: RawUTF8;
      fRole: TSQLUserRole;
      FLoginDate: TDateTime;
    published
      property Username: RawUTF8 index 30 read fUsername write fUsername stored AS_UNIQUE;
      property HashedPassword: RawUTF8 index 64 read fHashedPassword write fHashedPassword;
      property Role: TSQLUserRole read fRole write fRole;
      property LoginDate: TDateTime read FLoginDate write FLoginDate;
  end;

It is wieird there is "user1" then "user2" as column name: user1 and user2 are the values of Username column of the rows 1 and 2.

You were right I found the checking for OFFSET in AdaptSQLForEngineList:

 if Stmt.Offset<>0 then begin
      InternalLog('AdaptSQLForEngineList: unsupported OFFSET for [%]',
        [SQL],sllWarning);
      exit;

I use it for pagination (but for example I put only one record by request), the data returned on the first page always works, and then on the second page nothing, but response body seems to keep good values in it.
As my client is a JavaScript and it's case sensitive, the response body of the second page was not accepted by client.
Is it possible to keep column naming structure between direct engine and virtual table?

#5 mORMot 1 » Column names in response of restful query changes with startIndex » 2022-07-17 16:19:46

talexone
Replies: 4

Hi,

Can't figure out why the response from RESTful server has different column names (rowid instead of ID and camel cased) when define startIndex other than 0:

Request/Response with startIndex=0:

http://localhost:8080/root/User?select=*&startIndex=0&results=1 

[{"ID":1,"USERNAME":"user1","HASHEDPASSWORD":null,"ROLE":2,"LOGINDATE":null}]

Request/Response with startIndex=1:

http://localhost:8080/root/User?select=*&startIndex=1&results=1 

[{"rowid":2,"Username":"user2","HashedPassword":null,"Role":0,"LoginDate":null}]

Is it possible to get the same format for both requests?

#6 Re: mORMot 1 » Dependency injection in interface based service » 2022-01-25 22:48:26

TInjectableObjectRest works if declared with ServiceDefine, but this way a useless root/interfacename/{method} entry points were created. I tried TInterfaceResolverInjected.RegisterGlobal so dependable classes can find and create the object, but Rest is not injected anymore in created object (Create called instead of CreateWithResolverAndRest).


TUserService.UserRepository created and TUserService.UserRepository.Server injected, but root/userrepository/{method} service's entry points declared.

TUserRepository = class(TInjectableObjectRest, IUserRepository) 
...
TUserService = class(TInjectableObject, IUserService) 
    private
      fUserRepository: IUserRepository;
    published
      property UserRepository: IUserRepository read fUserRepository write fUserRepository;  
...
    aServer.ServiceDefine(TUserRepository, [IUserRepository], sicShared);
    aServer.ServiceDefine(TUserService, [IUserService], sicShared);
 

TUserService.UserRepository created, but TUserService.UserRepository.Server is nil

  TInterfaceResolverInjected.RegisterGlobal(TypeInfo(IUserRepository),TUserRepository);
  aServer.ServiceDefine(TUserService, [IUserService], sicShared);

How to register a TInjectableObjectRest class with a proper auto Rest injection and avoiding a useless entry points creation?

#7 Re: mORMot 1 » Dependency injection in interface based service » 2022-01-25 00:07:52

uian2000 wrote:

This constructor should be override, not reintroduced.

One can override only the same ancestor method. If the method was declared with a different parameters it can be only overload or reintroduce.

Chaa wrote:

See TInjectableObjectRest.

Switching to TInjectableObjectRest seems to work

 TUserService = class(TInterfacedObjectRest, IUserService)
 ...

constructor TUserService.Create;
begin
  inherited Create;
  Assert(Server.Services.Resolve(IUserRepository, fUserRepository),'IUserRepository object not found'); 
end; 

But what I'm looking for is automated DI

#8 mORMot 1 » Dependency injection in interface based service » 2022-01-23 23:19:39

talexone
Replies: 5

How to properly declare interfaces to permit a dependency injection in interface based services?

UserService.pas

  IUserService = interface(IInvokable)
    ['{A8C05236-F397-4422-9201-E676AB82F380}']
      function Read(ID: TID; out User: TSQLUser): boolean;   
  end;

  TUserService = class(TInterfacedObject, IUserService)
    private
      fUserRepository: IUserRepository;
    public
      constructor Create(aUserRepository: IUserRepository); reintroduce;
      function Read(ID: TID; out User: TSQLUser): boolean;                              
  end;

initialization

TInterfaceFactory.RegisterInterfaces([
   TypeInfo(IUserService)
   ]);  

UserRepository.pas

  IUserRepository = interface(IInvokable)
    ['{D4AF2ABB-5D53-43CA-8403-EC4A65BB76C4}']
    function Read(ID: TID; out User: TSQLUser): boolean;
  end;

  TUserRepository = class(TInterfacedObject, IUserRepository)
  private
    fRest: TSQLRest;
  public
    constructor Create(aRest: TSQLRest); reintroduce;
    function Read(ID: TID; out User: TSQLUser): boolean;
  end;

initialization

TInterfaceFactory.RegisterInterfaces([
   TypeInfo(IUserRepository)
   ]); 

Main.pas

...
aServer := TSQLRestServerDB.Create(lModel, SQLITE_MEMORY_DATABASE_NAME);
aServer.ServiceDefine(TUserService, [IUserService], sicShared);
...

The reintroduced Create constructors never called and the Read function raising exception 'fRepository is nil' . Only works a manual injection: aServer.ServiceDefine(TUserService.Create(TUserRepository.Create(aServer), [IUserService]), but I think it's not the right solution.

#9 Re: mORMot 1 » Websockets from MVCApplication » 2022-01-22 23:12:48

Thank you, the second option is what I need. I'll try that way.

#10 mORMot 1 » Websockets from MVCApplication » 2022-01-21 23:59:58

talexone
Replies: 2

Is it possible to enbable websockets from MVCApplication?

Something like this:

aServer := TSQLRestServerDB.Create(aModel,ChangeFileExt(ExeVersion.ProgramFileName,'.db'));
aApplication := TBlogApplication.Create;
      try
        aApplication.Start(aServer);
        aServer.ServiceMethodRegisterPublishedMethods('', aApplication);
        aHTTPServer := TSQLHttpServer.Create('8092',aServer,'+',useBidirSocket);
        aHTTPServer.WebSocketsEnable(aServer,'').Settings.SetFullLog;

...

IChatService = interface(IServiceWithCallbackReleased)
    ['{D3108D0F-AC2A-4520-B3C3-A0F3695ECEBC}']
    procedure Join(const callback: IChatCallback);
    procedure Post(const msg: string);
  end; 
    
...

initialization
  TInterfaceFactory.RegisterInterfaces([
    TypeInfo(IChatService),TypeInfo(IChatCallback)]);

and then subscribe from javascript

  socket = new WebSocket("ws://localhost:8092/blog/chat/join","synopsejson");

#11 mORMot 1 » Redirecting to default » 2020-05-07 15:56:24

talexone
Replies: 0

Hi,

In 30-MVCServer  the RootRedirectToURI redirects from '/' path to '/blog/default', but what about '/blog/'? I got Error 400.

20200507 15501404  +    mORMotSQLite3.TSQLRestServerDB(025818b0).URI GET blog/ in=0 B
20200507 15501404 debug 	mORMotSQLite3.TSQLRestServerDB(025818b0) TSQLRestRoutingREST.Error: {  "errorCode":400,  "errorText":"Bad Request"  }
20200507 15501404 srvr  	mORMotSQLite3.TSQLRestServerDB(025818b0)   GET blog/ Read=400 out=49 B in 59us

Another issue with a multiple controllers:

aBlog.Start(aServer);
aBlog2.Start(aServer);

In case of calling "/blog/blog2/default" got the default page from second controller, but in case of calling "/blog/blog2/" the request redirected to the faulty path "blog/blog2/blog/blog2//default"

20200507 15171034  +    mORMotSQLite3.TSQLRestServerDB(026618b0).URI GET blog/blog2/ in=0 B
20200507 15171034 srvr  	mORMotSQLite3.TSQLRestServerDB(026618b0)   GET blog/blog2 Method=307 out=0 B in 34us
20200507 15171034  -    00.000.044
20200507 15171558  +    mORMotSQLite3.TSQLRestServerDB(026618b0).URI GET blog/blog2/blog/blog2//default in=0 B

It's a bug or some configuration issue?

Regards,
AT

#12 Re: mORMot 1 » Virtual methods for loading templates in MVC Web App » 2020-05-04 13:09:40

Thanks ab.

I need to share a static content between controllers. When a first controller's Views contains templates like header.partial or footer.partial and I used it from another controller through {{>../header.partial}} there is problem with a static content due to relative paths (blog/sub-uri/.static instead of blog/.static/)

#13 Re: mORMot 1 » Virtual methods for loading templates in MVC Web App » 2020-05-03 14:08:17

Since GetStaticFile was removed, is there another method to change static file loading?

#14 Re: Free Pascal Compiler » 30-MVC Server crashes on redirection with FPC » 2020-04-28 13:02:01

I have tested on two VMs Win10 and Ubuntu18.04, same error on Win10 with fpcupdeluxe fresh install(trunk 40491 and 59757), but no problem with Ubuntu.

Seems to be a windows problem.

Tested on windows with target: Win32/i386, no errors.

#15 Re: Free Pascal Compiler » 30-MVC Server crashes on redirection with FPC » 2020-04-27 16:23:02

ab wrote:

What is the exact target you use?
Win32 or Win64?

Target: Win64
Mormot: 1.18 (github master)

#16 Free Pascal Compiler » 30-MVC Server crashes on redirection with FPC » 2020-04-27 15:01:07

talexone
Replies: 7

Hi,
Sample starting well, I can access all views, but can't get 404 page, instead got an error  "raised exception class 'Exception: ?'" and the apps stops.

Steps to reproduce:
- Start sample and open "http://localhost:8092/blog/default"
- go to the article "http://localhost:8092/blog/articleView?id=10000"
- replace article id to get 404 error ex. "http://localhost:8092/blog/articleView?id=10001"

Error comes after "raise EMVCApplication.CreateGotoError(HTTP_NOTFOUND)", same with "raise EMVCApplication.CreateGotoView..."

No issues with Delphi.

OS: Win10x64
FPC: 3.2.0rc1
Laz: 2.1.0


Regards,
AT

#17 Re: mORMot 1 » Manually define MVC URI » 2016-08-21 22:41:38

It seems that the MVC URI defined in TSQLModel and not in TMVCRunOnRestServer as documentation said:

result := TSQLModel.Create([TSQLBookInfo,TSQLAuthor,
    TSQLTag,TSQLArticle,TSQLComment,TSQLArticleSearch],'blog');

And it seems I can't use a host's root as MVC root, cause TSQLModel will replace an empty URI string with 'root'. (I'm confused with using 'root', cause usually root means '/' and not '/root')

Did I missed something, but why MVC root URI was defined in TSQLModel?

Thanks,
Alexandre

#18 mORMot 1 » Manually define MVC URI » 2016-08-21 10:40:13

talexone
Replies: 1

Hi,

In framework documentation said:

In order to have the BLOG content hosted in root/blog URI, you should specify the expected sub-URI when initializing your TMVCApplication:

  procedure TBlogApplication.Start(aServer: TSQLRestServer);
  begin
  ...
  fMainRunner := TMVCRunOnRestServer.Create(self,nil,'blog').
  ...

,but in sample "30 - MVC Server" there is no sub-URI definition of the hosted MVC and it works:

  ...
  fMainRunner := TMVCRunOnRestServer.Create(Self).
  ...

How it works and is it possible to use root (/) instead of root/blog (/blog)?

Thanks
Alexandre

#19 Re: mORMot 1 » Custom content from a MVC Application without a template file » 2016-08-11 14:46:09

@ab: Thank you for your answer.

But if it's not to much to ask you to point it in mormot documentation or in samples.

Finally I have found a solution as you said with a method-based service:

type
  TMyServer = class(TSQLRestServerDB)
    published
      procedure Version(Ctxt: TSQLRestServerURIContext);
  end;
  
var aServer: TDeskScreen_Server;
...
procedure TMyServer.Version(Ctxt: TSQLRestServerURIContext);
begin
  Ctxt.Returns(FormatUTF8('Blog Version v1.0',[]),HTTP_SUCCESS,TEXT_CONTENT_TYPE_HEADER);
end;

...
aServer := TMyServer.Create(aModel,ChangeFileExt(ExeVersion.ProgramFileName,'.db'));

Thank you again.

#20 mORMot 1 » Custom content from a MVC Application without a template file » 2016-08-11 13:51:54

talexone
Replies: 4

Hi,

Can't figure out how to send a custom content (json or text string) from a MVC Application (ex. 30 - MVC Server)

I have declared my custom function in MVCViewModel.pas:

IBlogApplication = interface(IMVCApplication)
    ['{73B27C06-9DB9-45A2-BEDD-2013CFB609D0}']
    function Version: TServiceCustomAnswer;
    ...


function TBlogApplication.Version:TServiceCustomAnswer;
begin
  Result.Header := TEXT_CONTENT_TYPE_HEADER;
  Result.Content := FormatUTF8('Blog Version v1.0',[]);
  Result.Status := HTML_SUCCESS;
end;

But when I call "blog/Version" it ask me to fill Version.html template.


Any help is welcome.
Thanks

Alexandre

#21 Re: mORMot 1 » Connect Firebird » 2013-08-25 11:30:25

Hello,
Any news about native connection to Firebird server? Or it's sill in progress. I want implement it into existing project, but can't find any info about.
Started by creating a connection, then connect to server (seems to work), creating simple record's object and model, but then ... (there is no TSQLRestServerDB class in SynFirebird)
Any way to use it without virtual tables?


type
TUser = class (TSQLRecord)
  private
    fID: integer;
    fUsername: RawUTF8;
    fPassword: RawUTF8;
  public
    property ID: integer read fID write fID stored AS_UNIQUE;
    property Username: RawUTF8 index 20 read fUsername write fUsername;
    property Password: RawUTF8 index 20 read fPassword write fPassword;
  end;

var
  cn: TSQLDBFirebirdConnection;
  cp: TSQLDBFirebirdConnectionClientProperties;
  mu: TSQLModel;
  ur: TUser;

...
      mu := TSQLModel.Create([TUser]);

      cp := TSQLDBFirebirdConnectionClientProperties.Create(aServername+':'+aFilename,aFilename,aUsername,aPassword);
      cn := TSQLDBFirebirdConnection.Create(cp);
      cn.Connect;

      ur := TUser.Create;

...

Board footer

Powered by FluxBB