JSON error responses for 50x HTTP status codes

Skip to first unread message


Aug 2, 2017, 5:18:42 PM8/2/17
to SWI-Prolog

While working on building an API layer for a swi-prolog app I ran into an issue. The client requires that every error status returns valid JSON in the body with the proper HTTP status code set. The exceptions I was generating in swi-prolog were returning as HTML pages and I'm not sure how to adjust that behaviour.

I tried to use the "http:status_page" hook in order to override the HTML page returned when an exception occurs on the server with a JSON body response including a Content-Type header value of "application/json" but the hook didn't seem to be called.

I solved my issue by forking the "package-http" git repo and then changing some logic here to use the status_page hook and use a new DCG to return the JSON I was expecting. Does someone know a better approach? I feel like I've totally hacked this together and I'm certain there's a much better way.

My goal: a better approach to adjust what is returned to clients when exceptions occur.

This is what I changed in the "http/http_header.pl" file:

status_reply(server_error(ErrorTerm), Out, HdrExtra, Context, Method, Code) :-
(server_error(ErrorTerm), 500, Context, JSON),  % It is assuming JSON is a string
(reply_header(json_status(server_error, JSON), HdrExtra1, Code), Header),
(Out, '~s', [Header]),
(Out, JSON).  % Just used this predicate because it worked but need to investigate a better alternative

%   ...

reply_header(json_status(Status, Tokens), HdrExtra, Code) -->
    vstatus(Status, Code),
    header_fields(HdrExtra, CLen),
    content_length(html(Tokens), CLen),  % Is there another method to get content_length?
    content_type(application/json, utf8),

In the module I use to start the HTTP server I did an "asserta" to add a new library path pointing to the altered version of the http package, is there a better way to do this? I'm still new at prolog and this process in Python I'd call "Monkey Patching" and am uncertain the proper way to do it in swi-prolog.

% Forcing the usage of the local library (HTTP) to override the base libraries
:- library_directory(Y), nonvar(Y).  % Not sure why but I needed to check for something in the `library_directory` dyanamic DB before adjusting it.
:- absolute_file_name('.', X), asserta(library_directory(X)).
:- use_module(library(http/http_header)).

Any input, ideas or rough critique is welcomed. If I need to RTFM, please feel free to tell me that too.

Thank you
Reply all
Reply to author
0 new messages