#1 Re: mORMot 1 » Get available webapi from server » 2024-05-14 01:01:50

I'm also interested in this subject.
The referred link is broken and title_482 no longer exists. What was the topic?
What is equivalent link in v2 documentation?

#2 Re: mORMot 2 » Interface method variable input parameters » 2024-05-13 23:35:15

ab wrote:

Sounds like trying to make a circle enter into a rectangle hole....

I will refrain from commenting that one big_smile

ab wrote:

Perhaps you could just use not interface-based services, but a method-based service, or directly register a route at HTTP server level.

Sure, i could. But this way seems more convenient and the declaration expresses the intent better.
It seems like a basic feature to me that should be built-in, not reinvented each time (i searched, there have been others asking for this).

ab wrote:

The only fact that you need a kind of "varying" input smells to me like a poorly defined API.
IMHO an API should follow the "design by contract" pattern, i.e. it should do one thing, with no confusion possible.

In our product, customers can create their own extra fields for users table. This feature has been a huge success. It has helped keep the base package design clean and we don't have to worry about crazy customer ideas. Someone wants a column for employee waist size, no problem! smile
The API must also support this feature so customers can create integrations without directly querying our db.
So for user create and modify, must support inputs that match these unknown column names.
Is varying inputs poor for that? What would be better solution?

#3 mORMot 2 » Delphi debugger crash when inspecting DocVariant » 2024-05-11 22:18:26

Lauri
Replies: 2

Hi!

Moving another subject from e-mail to here.

ab wrote:

Delphi 7 debugger should work with a DocVariant, but it is likely to be a Delphi bug, not a mORMot issue. The Delphi 7 IDE has already troubles with our units, and is crashing for now reason. Lazarus is very stable, but the Delphi IDE is not stable. Even the latest versions of Delphi are crashing on a regular basis.

Hmm.. for mORMot v1, this was the case for me as well. The main unit was just too big.
However, mORMot v2 was much better in early versions and at least v2.2 has not caused *any* out-of-the-blue crashes. Perhaps this is because i have put all mormot.* units into one runtime package and removed any source files from compile path. I do this way for all components because it helps make builds much faster.

But there is a sure way to crash (and the reason for this posting) -- each time i (often accidentally) move cursor over a variant that contains a DocVariant, the IDE tries to get it's current value but instead hangs and crashes.

I was wondering, it this caused by something in my specific configuration or is this the case for everyone who is still using Delphi version 7?
Does someone here know of a workaround perhaps?
Can newer versions of Delphi inspect a DocVariant value?

#4 mORMot 2 » Interface method variable input parameters » 2024-05-11 20:26:26

Lauri
Replies: 2

Hi!

Continuing discussion from email as suggested by ab.

Arnaud wrote:

If you want undefined variable inputs, the best is to use a sub-parameter of type variant, so you can send some JSON object with this parameter, and retrieve it as a TDocVariantData at runtime.

I'm not exactly sure if i understood correctly, but the following works and seems to fit the description:
Given interface procedure(const Vars: variant);
For GET, had to put params like this API/Interface?Vars={Abc=123,Def=321}
For POST, such json body is needed for same result: {"Vars":{"Abc":123,"Def":321}}

Did i get that correct?

If yes, then i don't not like it! smile It annoys me to write artificial elements like that. Having variable inputs is not so unusual requirement i think.
So since i dont like, in order not to be a whiner, must try to improve smile
Since there are already special interface method names like GET_ and POST_, i thought why not use a special variable name to indicate that all input is to be passed along.

I made small changes in two mORMot units. And now i can declare such interface: procedure(const _AllInputAs: variant);
And call like so:
For GET, URI params simply Abc=123&Def=321
For POST, json body {"Abc":123,"Def":321}
The parameters are parsed into a docvariant and can be enumerated in the implementing method.

The conditions are that an interface must contain one input and its name must be "_AllInputAs" and its type must be variant. Unfortunately this could not be achieved by overriding classes. Here are the changes i made (based on version 2.2 stable).

Changed in mormot.core.interfaces.pas:

313,315d312
<     /// flag indicating first parameter of this method is const _AllInputAs: variant
<     // - used for passing undefined input parameters via GET/POST  
<     ArgsWantAllInputAs: boolean;
4002,4007d3998
<     // is this _AllInputAs interface method?
<     ArgsWantAllInputAs :=
<       (ArgsInputValuesCount = 1) and
<       (Args[ArgsInFirst].ValueDirection = imdConst) and
<       (Args[ArgsInFirst].ParamName^ = '_AllInputAs') and
<       (Args[ArgsInFirst].ValueType = imvVariant);
7476,7477c7467
<     if (not fMethod^.ArgsWantAllInputAs) and
<        (fMethod^.ArgsInputValuesCount <> 0) and
---
>     if (fMethod^.ArgsInputValuesCount <> 0) and
7530,7535d7519
<     if fMethod^.ArgsWantAllInputAs and (P <> nil) then
<     begin
<       ctxt.InitParser(P, nil, fFactory.JsonParserOptions, @fDocVariantOptions, nil, nil);
<       fMethod^.Args[fMethod^.ArgsInFirst].SetFromJson(ctxt, fMethod, fValues[fMethod^.ArgsInFirst], Error);
<     end
<     else

Changed in mormot.rest.server.pas:

4526,4536d4525
<   // _AllInputAs magic parameter for interface method
<   m := ServiceMethod;
<   if
<     (m.ArgsInputValuesCount = 1) and
<     (m.Args[m.ArgsInFirst].ValueDirection = imdConst) and
<     // a flag set at routes init time would be better than compare here
<     (m.Args[m.ArgsInFirst].ParamName^ = '_AllInputAs')
<   then begin
<     fCall^.InBody := GetInputAsTDocVariant([], nil);
<     exit;
<   end;

Terrible idea?

Edit:
Added test project:
https://gist.github.com/lzva/4756328024 … 0105f11601

#5 Re: mORMot 1 » Recursive search in DocVariant given path and value » 2022-11-24 17:06:19

I just thought it would be useful function to have and i asked because i could not find anything like it.
I already solved my specific problem with dumb nested iterations. It's fast but stupid to write. I had 5 levels of nesting.

So please take it as a feature suggestion.

Thank you Igor and Thomas!

#6 Re: mORMot 1 » Recursive search in DocVariant given path and value » 2022-11-22 19:13:13

No, GetValueByPath returns value of (only first?) item matching the path.

But I already know the value. Instead I need to find the object that has given value.

#7 mORMot 1 » Recursive search in DocVariant given path and value » 2022-11-22 16:52:26

Lauri
Replies: 5

Hi!

Given json such as this:

{
  "trees":
  [
    {
      "Name": "Tree 1"
      "Branches":
      [
        {
          "Name": "Branch 1"
          "Leaves":
          [
            {
              "Name": "Leaf 1"
            },
            {
              "Name": "Leaf 2"
            }
          ]
        },
        {
          "Name": "Branch 2"
          "Leaves":
          [
            {
              "Name": "Leaf 3"
            },
            {
              "Name": "Leaf 4"
            }
          ]
        }
      ]
    }
  ]
}

Is there a function to search for leaf based on its path and name?

Something defined similar to:

function findDVInPathByValue(const APath, AValue: string; out DV: PDocVariantData; var SearchStartIndex: Integer): Boolean;

And called like so:

SearchIndex := 0;
while 
  Forest.findDVInPathByValue('trees.branches.leaves.name', 'Leaf 3', Leaf, SearchIndex)
do begin
  // do stuff with each leaf named "Leaf 3"
end

I know how to iterate the trees and then branches and leaves.

  V := _json('...');
  Trees := _safe(V.Trees);
  for Idx := 0 to Trees.Count - 1 do
  begin
    Branch := _Safe(Trees.Values[Idx].Branches);
    for Idx2 := 0 to Branch.Count - 1 do
    begin
      Leaves := _Safe(Branch.Values[Idx2].Leaves);
      if Leaves.GetDocVariantByProp('Name', 'Leaf 3', False, Leaf) then  // only saves the last level
      begin
        ...

I was just wondering if there is something more appropriate/convenient already available.

#8 Re: mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2022-01-11 21:23:24

Because i live under a rock and don't have a Github account smile
An e-mail with a link could be arranged to someone willing to do it.

#9 Re: mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2022-01-11 19:23:44

Hi!

I've converted the library to use mORMot2. Seems to work and passes included tests. I was wondering maybe someone else is interested...

#11 Re: mORMot 1 » Some bug fixes and suggested modifications for mORMot2 » 2021-01-07 16:20:45

It seems that THttpApiServer.RemoveUrl does not remove a registered URL previously added by AddUrl. Http.RemoveUrlFromUrlGroup(fUrlGroupID, pointer(uri), 0) returns 0 (success). Yet "netsh http show urlacl" shows the registration remains.
I want to add a config option to my project to allow the user to pick either HTTP or HTTPS so removing the other one is necessary.
I do have a workaround to use "netsh http delete urlacl ...".

#12 Re: mORMot 1 » Some bug fixes and suggested modifications for mORMot2 » 2021-01-07 15:37:27

I propose a new define:

{$define ONLYUSEHTTPSYS}
// disable automatic failover to Http socket server when http.sys based server fails

and implementation in mormot.rest.http.server.pas, in exception handler, around line 665 add:

    on E: Exception do
    begin
      log.Log(sllError, '% for % % at%  -> fallback to socket-based server',
        [E, ToText(aUse)^, fHttpServer, ServersRoot], self);
      FreeAndNil(fHttpServer); // if http.sys initialization failed
      {$ifdef ONLYUSEHTTPSYS}
      raise;
      {$endif}
    end;

This way i can report the problem to user and abort startup. Silent fallback is not desired when SSL is required.

#13 Re: mORMot 1 » Some bug fixes and suggested modifications for mORMot2 » 2021-01-06 16:53:58

Thank you ab!
I've checked now and all works perfectly except for a corner case with issue 7.

Having such (incorrect?) implementation will cause an AV in mormot.core.interfaces line 7146 (AddJson) because fValues is nil.

function TSomeInterfaceMethodObject.Test1: TServiceCustomAnswer;
begin
  Result.Status := 200;
  Result.Content := 'seems to work';
end;

These work fine though:

function TSomeInterfaceMethodObject.Test2: TServiceCustomAnswer;
begin
  Result.Header := 'Content-type: application/json';
  Result.Status := 200;
  Result.Content := '{"x": "seems to work"}';

function TSomeInterfaceMethodObject.Test3: TServiceCustomAnswer;
begin
  Result.Header := BINARY_CONTENT_TYPE;
  Result.Status := 200;
  Result.Content := 'seems to work';
end;

#14 mORMot 1 » Some bug fixes and suggested modifications for mORMot2 » 2021-01-05 21:48:27

Lauri
Replies: 9

Hi!

1. In mormot.lib.winhttp.pas, procedure HttpApiInitialize:

  if Http.Module <> 0 then
    exit; // already loaded
  mormot.core.os.GlobalLock;
  try
    if Http.Module <> 0 then // remove this
    try

Remove the second check for "Http.Module = 0". Otherwise the API never gets initialized and there will be an AV when starting web server or using httpget.

2. In mormot.orm.rest.pas, function TRestOrm.AsynchBatchStop, add additional check for fRest.Run = nil. Without it one of the tests fails.

  if (self = nil) or
     (fRest.Run = nil) or // add this
     (fRest.Run.BackgroundTimer = nil) or
     (fRest.Run.BackgroundTimer.BackgroundBatch = nil) then
    result := false
  else
    result := fRest.Run.BackgroundTimer.AsynchBatchStop(Table);

3. In mormot.rest.server.pas, class TRestServerURIContext, please make this Error call virtual:

procedure Error(E: Exception; const Format: RawUTF8;
      const Args: array of const; Status: integer = HTTP_BADREQUEST); overload;

That way i can handle output for some exception classes.

4. In mormot.rest.http.server.pas:
- i suggest moving defines COMPRESSSYNLZ and COMPRESSDEFLATE to mormot.defines.inc. Seems to me more logical place to look for those.
- in function TRestHttpServer.Request, i suggest moving the block with check for OnlyJSONRequests below OPTIONS method handler. This way, OPTIONS request will work even when OnlyJSONRequests is set. Thus allowing browser to send requests with content-type "application/json". Like so:

   ...
    else
      result := HTTP_BADREQUEST
// move this: 
//  else if (Ctxt.Method = '') or
//          (OnlyJSONRequests and
//           not IdemPChar(pointer(Ctxt.InContentType), JSON_CONTENT_TYPE_UPPER)) then
//    // wrong Input parameters or not JSON request: 400 BAD REQUEST
//    result := HTTP_BADREQUEST
// from here... 
  else if Ctxt.Method = 'OPTIONS' then
  begin
    // handle CORS
    if fAccessControlAllowOrigin = '' then
      Ctxt.OutCustomHeaders := 'Access-Control-Allow-Origin:'
    else
    begin
      FindNameValue(Ctxt.InHeaders, 'ACCESS-CONTROL-REQUEST-HEADERS:', headers);
      Ctxt.OutCustomHeaders := 'Access-Control-Allow-Headers: ' + headers;
      ComputeAccessControlHeader(Ctxt);
    end;
    result := HTTP_NOCONTENT;
  end
// to here:
  else if (Ctxt.Method = '') or
          (OnlyJSONRequests and
           not IdemPChar(pointer(Ctxt.InContentType), JSON_CONTENT_TYPE_UPPER)) then
    // wrong Input parameters or not JSON request: 400 BAD REQUEST
    result := HTTP_BADREQUEST
  else
  ....

5. Seems to me that implementations for TRestServerRoutingREST and TRestServerRoutingJSON_RPC are switched. The header comments are correct but implementation for REST is clearly checking for JSON_RPC mode of method calling and vice-versa.

6. Interface methods return output parameters without commas like so:
{"n1":2"n2":3"text":"test text"}
instead of:
{
    "n1": 2,
    "n2": 3,
    "text": "test text"
}
I have set
ResultAsJSONObject := True;
ResultAsJSONObjectWithoutResult := True;

7. Defining an interface with function returning TServiceCustomAnswer will cause AV when calling TInterfaceFactory.RegisterInterfaces.
in mormot.core.base, function TSynTempBuffer.InitZero, there is a call "Init(ZeroLen - 16)" but ZeroLen is 12 in this case. That results in a negative value and buf getting set to nil. Next FillCharFast will fail.

This is based on source archive taken on 12.24. So maybe you already changed some of those things..

#15 Re: mORMot 1 » mORMot2 examples » 2020-12-14 23:44:53

Lauri wrote:

What happened to TTextWriter.RegisterCustomJSONSerializer in v2?
Only refrences left are in source comments.

Looks like new solution is TRttiJson.RegisterCustomSerializer. Seems to work just fine. Thank you!

#16 Re: mORMot 1 » mORMot2 examples » 2020-12-14 13:10:43

Don't worry about that. It will be easy enough to switch back to 1.18. But for discovering things and browsing around the code, v2 is a lot better. Plus my IDE doesn't crash every second click.
I don't mind reporting the bugs if it helps get mORMot 2 released sooner.

#17 Re: mORMot 1 » mORMot2 examples » 2020-12-14 04:47:10

What happened to TTextWriter.RegisterCustomJSONSerializer in v2?
Only refrences left are in source comments.

#18 Re: mORMot 1 » mORMot2 examples » 2020-12-12 20:20:44

I found another minor bug in v2.
Interface methods return output parameters without commas like so:
{"n1":2"n2":3"text":"test text"}
instead of:
{
    "n1": 2,
    "n2": 3,
    "text": "test text"
}
I have set
ResultAsJSONObject := True;
ResultAsJSONObjectWithoutResult := True;

And another one:
Defining an interface with function returning TServiceCustomAnswer will cause AV when calling TInterfaceFactory.RegisterInterfaces.
in mormot.core.base, function TSynTempBuffer.InitZero, there is a call "Init(ZeroLen - 16)" but ZeroLen is 12 in this case. That results in a negative value and buf getting set to nil. Next FillCharFast will fail.

Ab, do you want bugreports like this or should i not bother reporting?
(if yes then see post #13 also)

And a suggestion -- why not make TRestServerURIContext.Error(E: Exception; const Format: RawUTF8; const Args: array of const; Status: integer); ....virtual. I want to override it so i can handle some "normal" exceptions from interface implementing functions and return a bit less information than by default is returned for exception.

#19 Re: mORMot 1 » mORMot2 examples » 2020-12-12 18:20:02

Hmm.. i'll try disabling cnPack and see if i can open v1.18 mormot.pas.

Regarding focusing on newer versions. I can't say i like that thinking too much. I have a huge, currently stable and relatively bug free, project written in D7. Porting it to latest version of Delphi (for which i do own a current subscription) is a lot of work. We can't all be so productive as Arnaud smile And it will destabilize the project for years. I would rather add useful new features my customers are interested in (such as an API for a web based client). D7, with some tweaks, can do everything that is necessary and produce a nice modern looking application. The only issue is Unicode UI. But that is something i can live without. Just because something is old does not mean its become crap. Just my POV.

#20 Re: mORMot 1 » mORMot2 examples » 2020-12-12 17:54:41

It would seem that implementations of TRestServerRoutingREST and TRestServerRoutingJSON_RPC have gotten mixed up in mORMot2 smile

#21 Re: mORMot 1 » mORMot2 examples » 2020-12-10 21:29:25

I see i was a bit unclear in my question. Sorry about that. I'll try again based on the doc --
Using TSQLRestRoutingREST routing, having URI scheme "/Model/Interface.Method[/ClientDrivenID]", is there any way to publish one or more methods without the interface name? Like so: "/Model/Method[/ClientDrivenID]"?

#22 Re: mORMot 1 » mORMot2 examples » 2020-12-10 20:24:05

After a week and a half of reading and trying things i'm starting to get a handle on this library. I like how the source is full of detailed comments. And more abstract documentation is online (SAD and blog).

I found another bug though. It's in the blog smile
Various older links from this forum to the blog are broken and some links inside the blog that refrence prior articles also. See the link collection at the bottom of this post for example:
https://blog.synopse.info/?post/2012/03 … d-services
The fix is real easy -- need to add ? before post/ in the URI

I also have a question i have not found an answer yet:
given such URL - host.domain/API
where API is the root. how can i define and publish an interface for function "version(out major, minor: integer)" so that it would be accessible at host.domain/API/version or host.domain/API.version? In other words, how can i publish some interfaces under the root?

Also, while mORMot 2 is working much better with D7, there is one annoying issue - i can not use the built-in search because file names with multiple dots are confusing it (nothing is ever found in directory search). I've been using notepad+ to search but this is annoying. Is there a workaround? I guess asking for file name parts to be separated using an underscore would be unreasonable? smile

#23 Re: mORMot 1 » mORMot2 examples » 2020-12-06 21:22:58

By the way, i found a minor issue with mORMot 2 tests. The tests that were using HttpGet function failed with AV because WinHttpAPIInitialize was not called.  I added a call to unit initialization of mormot.lib.winhttp.pas and now all test pass.

#24 Re: mORMot 1 » mORMot2 examples » 2020-12-06 21:14:10

ab wrote:

The trouble is that Delphi has troubles with even smaller mORMot2 units... and not only Delphi 7, but any modern Delphi without the Andy IdeFixPack.
And be aware: mORMot 2 is NOT stable, and should NOT be use on production. There are a lot of bugs still to be fixed, and some mission functions - as you noticed.
I hope/pray/sight it would be usable for Christmas. smile

I understand the risk of using alpha-grade libraries. Fortunately the API wont be exposed to the world and i can always update it when there are important fixes in MORMot. It is likely i wont finish the project this year anyway.

ab wrote:

Another trick for stability: compile all mORMot units into .dcu, and don't compile from the source during development. Or create a package. It would make compilation of your code faster, and the IDE will be more responsible.

This is exactly what i'm doing (i describe that in my lengthy first post). I'm also using the IdeFixPack. I did not notice IDE issues with mORMot 2, only the current stable version is too much for my poor old D7.

Unfortunately i still don't know where to focus my attention. My eyes are sore from all the reading already smile I would really appreciate a hint on which building blocks to use for a HTTP server with REST api *without* all the ORM/DB stuff.

#25 mORMot 1 » mORMot2 examples » 2020-12-06 03:33:40

Lauri
Replies: 18

Hi!

I have a task to create a JSON REST API for my n-tier application. In my search for a suitable library, mORMot kept turning up as good candidate for the problem.

I tried installing mORMot 1.18 library (i created a runtime package to have all the units compiled, put the dcus in a folder and added it to library path. this way a build wont try to recompile the library - i do it for all the libraries i use, it saves a lot of time). However it seems Delphi 7 is having a hard time with huge library files syncommons.pas and mormot.pas. Every other time i invoke code completion the IDE hangs. Unfortunately this makes it impossible for me to use the library in any serious way.

I discovered mORMot2 is nearly ready and the libraries have been modularized and organized into more independent units. So i installed it in similar way and it seems Delphi 7 indeed has no issue with it.

Unfortunately there are no examples yet for version 2 and the examples from mORMot 1.18 i'm interested in (06), are not compiling with version 2 (i think i fixed the uses units correctly but some functions like UrlDecodeNeedParameters just no longer exist in v2, it would be extremely difficult for me to find out how it's functionality has been replaced).

I've been reading the documentation and trying some things for four days now. The learning curve is steep but i like what i see so far. I've decided to invest the time but maybe i should invest the time in mORMot2 since it is the future anyway?.

For now, I'm not interested in ORM functionality. All i need is a http(s) server with rest methods + json parameters parsing. Something similar to what dXmlRpc library offers but for JSON. API authentication solution would be a bonus.

I would appreciate suggestions for which classes/examples is should focus on and if it is a good idea to start with v2 (in that case, which v2 classes i should focus on and what are the counterparts in v1 so i can read the docs).

Thanks and regards,
Lauri

Board footer

Powered by FluxBB