Prolog HTTP Dispatch handle encapsulation

181 views Asked by At

I'm creating a program in Prolog using a HTTP server to make requests...

I'm want to encapsulate the code in a way I can reuse it and have the http stuff in one module, my "controller" to handle requests in another module, etc.

I started having problems with the http dispatch handler registration:

:- http_handler('/test', foobar, []).  

Is it possible to have something like this:

register_handler(path, callback) :-
    http_handler(path, callback, []).  

I tried using that but I got a error must likely due to the "callback" parameter. Also, the callback predicate is defined in a different module so I used:

:-consult(api_controller).

[EDIT]

server.pl

:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/http_parameters)).
:- use_module(library(http/http_json)).

:- use_module(api_controller).

:- http_handler('/test', foo, []).  

server(Port):-http_server(http_dispatch, [port(Port)]).

api_controller.pl

foo(_request) :-
    format('Content-type: text/plain~n~n'),
    format('Hello world!~n').

Error:

http_dispatch:call_action/2: Undefined procedure: foo/1
1

There are 1 answers

4
mat On

http_handler/3 is a directive, and you can place such directives in other files and then use include/1 to load them.

In addition, you can have total control over the HTTP dispatch by installing a generic handler as follows:

:- http_handler(/, handle_request, [prefix]).

Note the prefix option.

Then, you supply a suitable handle_request/1, for example like this:

handle_request(Request) :-
        debug(my_dispatch, "~q\n", [Request]),
        memberchk(path(Path0), Request),
        atom_concat(., Path0, Path1),
        http_safe_file(Path1, []),
        absolute_file_name(Path1, Path),
        (   reply_file(Path0, File) -> http_reply_file(File, [unsafe(true)], Request)
        ;   redirect(Path0, Other)  -> http_redirect(moved, Other, Request)
        ;   see_other(Path0, Other) -> http_redirect(see_other, Other, Request)
        ;   hidden_file(Path0)      -> http_404([], Request)
        ;   exists_file(Path)       -> http_reply_file(Path, [unsafe(true)], Request)
        ;   ...
        ).

In this example, the following predicates are meant to be supplied by you to tailor the server to your exact use cases:

  • reply_file(Path, File): Send contents File in response to request Path.
  • redirect(Path0, Path): Redirect to Path in response to Path0.
  • see_other/2: Meaning left as an exercise.
  • hidden_file/1: Meaning left as an exercise.

These rules can be defined elsewhere, and you can include these files with the directive:

:- include(other_source).

A related directive you should check out is multifile/1.

I leave figuring out the precise libraries you need for the above to work as an exercise. A starting point:

:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/http_server_files)).
:- use_module(library(http/http_files)).
:- use_module(library(http/http_header)).