/json-utils

A few utilities for handling JSON requests in Common Lisp.

Primary LanguageCommon LispMIT LicenseMIT

----------------------------------------------------------------------
Description

This package contains a couple functions that make my life easier when
writing JSON requests to RESTful APIs, such as CouchDB and
Twitter. They basically paper over Drakma and Yason to eliminate
boilerplate and normalize signaled conditions.

----------------------------------------------------------------------
API

[Function]
json-request method host port resource &rest keyword-args &keyword-args &key query-args content ssl &allow-other-keys
        => body-or-stream status-code headers uri stream must-close reason-phrase

This is a thin wrapper around drakma:http-request intended to simplify
JSON requests and paper over some weaknesses. It performs the
following functions:

  The uri is constructed from the supplied parameters.

  If query-args is supplied, it should be an alist. The list will be
  converted into an appropriate query string and appended to the
  uri. If any value in the alist is null, that pair will not be
  included.

  The request is initialized with the proper parameters for a JSON
  request, e.g., setting up the content-type, setting the format to
  UTF-8, and instructing Drakma to treat the response as text.

  If content is supplied, then the content-length parameter is
  calculated using Babel. This overcomes a shortcoming in Drakma,
  which uses LENGTH to determine content-length. LENGTH returns the
  number of characters in a string, not the number of octets, and thus
  is not suitable for determining the content-length of UTF-8 strings.

  Finally, it wraps the drakma:http-request with some condition
  handlers to catch common errors signaled by one of Drakma's
  dependencies and resignals them as conditions defined in this
  package.

method should be a keyword, like :GET or :POST.

If ssl is non-nil, then https will be used in the URI, rather than
http.

This function accepts additional keywords, which will be forwarded to
drakma:http-request. See drakma:http-request for a description of the
possible keyword arguments and the return values.

Example:

 (json-request :get "api.twitter.com" 80 "1/statuses/public_timeline.json" :query-args '(("trim_user" . "t")))

[Macro]
json-request* ((response-body-var (method host port resource
                                   &rest keyword-args
                                   &key query-args content ssl encode-content parse-response-body
                                   &allow-other-keys)) body
        => result from body

This macro provides a compact template for a making a JSON request and
dispatching on the status code of the reply. Within body,
response-body-var is bound to the body of the reply.

Most arguments are passed directly to json-request. See that function
for their documentation.

If content is supplied and encode-content is t, then content will be
encoded into JSON, using yason:encode, before creating the request. If
content cannot be encoded, CANNOT-ENCODE-TO-JSON-CONDITION will be
signaled.

If parse-response-body is t, then the response will be converted from
JSON to a hash-table, using yason:parse, before being bound to
response-body-var.

Handlers for several error statuses are provided, but they can be
overridden. The default handlers simply signal a corresponding
condition, such as NOT-FOUND-CONDITION when the status code is 404.

Example:

 (json-request* (response (:get "api.twitter.com" 80 "1/statuses/public_timeline.json" :query-args '(("trim_user" . "t"))))
   (200 (mapcar (lambda (r) (gethash "text" r)) response))
   (404 (print "Not found")))