How does one get variables in paths to work with http_handler/3?

I’m having zero luck expanding the example at http://www.swi-prolog.org/pldoc/doc_for?object=http_handler/3#http:location/3

If I try something like:

:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/http_unix_daemon)).
:- use_module(library(http/http_files)).
:- use_module(library(http/http_error)).
:- use_module(library(http/html_write)).

:- initialization http_daemon.

:- multifile http:location/3.

http:location(files, '/user', []).

:- http_handler(files(user/User), user(Method, User),
                [ method(Method),
                  methods([get,post,put])
                ]).

user(get, User, _Request) :-
  reply_html_page(
    [title('Hello World!')],
    [p(User)]
  ).

I just get an error Arguments are not sufficiently instantiated.

What is the magic incantation to make this work?

Except for the :- initialization http_daemon., it works fine. This declaration leads to a double startup. The http_unix_daemon library uses initialization/2.

Test:

swipl s.pl --interactive --port=4001
% Started server at http://localhost:4001/
101 ?- 
curl http://localhost:4001/user/user/jan
<!DOCTYPE html>
<html>
<head>
<title>Hello World!</title>

<meta http-equiv="content-type" content="text/html; charset=UTF-8">

</head>
<body>

<p>
jan</p>
</body>
</html>

Thanks Jan

I’m using version 7.4.2, so maybe I need to upgrade. I’ll install the latest version and see if I can replicate what you did.

Yip. This has been added rather recently. I think you need 8.1.x (currently 8.1.5).

Thanks Jan

Problem solved by upgrading to 8.1.5.

Following the build instructions worked flawlessly on my home PC, but was a bit more challenging on my web hosting company’s server where CentOS 7 had a too old version of cmake, so I had to install a newer version in my home directory, and then make failed because of tex, so I had to edit the CMake file to turn documentation off… but all good now.

The build doesn’t use LaTeX. It does various packages that are not so obvious such as archive. That should eventually be avoided by restructuring the modules that constitute PlDoc. It is wise to install all prerequisites to avoid these issues. You find the list for Redhat/Fedora/CentOS here

1 Like

I am complete beginner in Prolog. Any help will be much appreciated.

  • I have created server.pl skeleton code using swi-pro openapi tool from an openapi yaml.
  • I have launched the server and am sending a POST request to
    the API end point /hello/{id}/world using swagger UI that sends the below curl request

curl -X POST “http://localhost:1234/hello/3fa85f64/world” -H “accept: application/json” -H “Content-Type: application/json” -d "{“callerId”:“1234”}

  • request reaches server but cant route it if the request has a parameter
    embedded in the end point as “3fa85f64” in the above example. This value will be changed for every request. Its the request parameter.

  • I tried this link https://www.swi-prolog.org/pldoc/doc_for?object=http_handler/3
    with no success.

Any concrete example for http handler in the server for the above kind of http request please ?
The above POST call should be routed to a method runHello in the server.pl and i tried
:- http_handler(root(hello/x/world), runHello, [X]).

but no success.

Thanks very much in advance

Seems the docs explicitly cite your request case:

If a segment is a variable it matches any segment and the binding may be passed to the closure. If the last segment is a variable it may match multiple segments. This allows registering REST paths, for example: …

(there could be a typo in the docs: methods([get,port,put]) should be methods([get,post,put]).)

So - I think - you should try to define your handler like

:- http_handler(root(api/v1/analytics/X/Y), runPrognosis(X,Y), []).
runPrognosis(X,Y,_Req) :-
 ...

I think you also need the 8.1.x series for this to work. Thanks for pointing out the typo. Fixed.

Thanks very much for the help. One more question though:
for my case, the ‘X’ in your example is a variable in every request and
Y is a string ‘projections’.
So as per your example, I should write like the below ? The server will substitute X for the new value every time ?
Again thanks so much in advance. Regards

:- http_handler(root(hello/X/world), runHello(X), []).
runHello(X,_Req) :-

Yes, that should work.

Unfortunately it did not work. I never got the log message that proves whether the request was routed or not to the function. My request URL looks as : “http://localhost:1234/hello/3fa85f64/world

(1) Installed 8.1.28 version from https://www.swi-prolog.org/download/devel/bin/swipl-8.1.28-1.x64.exe.envelope

(2) {code}

:- http_handler(root(run/X/Hello), runHello(X), []). runHello(Id,Response) :- http_log('==> runHello Id is:~w~n', [Id]), Response = status(200).

Not sure what the problem is.
This snippet runs correctly:

:- use_module(library(http/http_server)).

go :- http_server([port(6789)]).

:- http_handler(root(api/v1/analytics/X/analytics), runPrognosis(X), []).

runPrognosis(X,_Req) :-
    reply_html_page(
        title('API test'),
        html('called ~w'-[runPrognosis(X)])).

when I call it from the browser with http://localhost:6789/api/v1/analytics/123123/analytics i get the html page with called runPrognosis(123123) . So, http_handler works as advised.

Maybe you should dive deeper in debugging the server, or double check the log…

Thanks again !!! Yes, it seems I tried POST and it failed. So
I tried like yours GET request through browser and using CURL and Postman and all worked… But the POST request still fails…anything I need to
add to this function or http_handler for handling Post request ?
Again thanks so much for your help…I really appreciate it
Regards

You probably want to use http_parameters(+Request, ?Parameters) described at https://www.swi-prolog.org/pldoc/man?section=httpparam

I wrote some notes on my attempts to figure it out at https://github.com/roblaing/swipl-webapp-howto/tree/master/unit2

Changed @CapelliC code a little to this:

:- use_module(library(http/http_server)).

go :- http_server([port(6789)]).

:- http_handler(root(api/v1/analytics/X/analytics), runPrognosis(X), []).

runPrognosis(X,Req) :-
    http_read_json_dict(Req, Dict),
    reply_html_page(
        title('API test'),
        'called ~w with JSON=~p'-[runPrognosis(X), Dict]).

Now run

curl --header "Content-Type: application/json" --request POST --data '{"username":"xyz","password":"xyz"}' http://localhost:6789/api/v1/analytics/XXX/analytics

to get

<!DOCTYPE html>
<html>
<head>
<title>API test</title>

<meta http-equiv="content-type" content="text/html; charset=UTF-8">

</head>
<body>
called runPrognosis(XXX) with JSON=_3922{password:"xyz",username:"xyz"}
</body>
</html>
1 Like

Your request URL is /hello/3fa85f64/world, so the handler needs to follow the same format.

So change;
:- http_handler(root(run/X/Hello), runHello(X), []). runHello(Id,Response)

To this: (make sure you have a small Hello in the route… )

:- http_handler(root(hello/X/world), runHello(X), []).

You also need to make sure that you have a body to the head of your predicate i.e.

runHello(Id,Response):-
format('Content-type: text/plain~n~n'),
format('Send a default user~n', []).

Hope that helps…

Thanks ! awesome…much appreciated.