You are not logged in.
Pages: 1
Yes, thank you.
Hi,
After updating my repo to the latest master commit from synopse repo got an error on compile in mormot.net.client.pas "E2003 Undeclared identifier TJsonClient" in line 4250.
Can't find TJsonClient class in interface section, only in implementation.
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;
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?
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.
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?
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?
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?
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.
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
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.
Thank you, the second option is what I need. I'll try that way.
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");
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
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/)
Since GetStaticFile was removed, is there another method to change static file loading?
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.
What is the exact target you use?
Win32 or Win64?
Target: Win64
Mormot: 1.18 (github master)
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
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
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
@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.
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
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;
...
Pages: 1