Simple HTTP and REST client for Crystal, inspired by the Ruby's RestClient gem.
Add this to your application's shard.yml
:
dependencies:
crest:
github: mamantoha/crest
require "crest"
Basic usage:
Crest.get(
"http://example.com/users",
params: {:lang => "en"}
)
Crest.post(
"http://example.com/users",
payload: {:age => 27, :name => {:first => "Kurt", :last => "Cobain"}}
)
Crest::Request
accept next parameters:
Mandatory parameters:
:method
- HTTP method (:get
.:post
,:put
,:patch
,:delete
,:options
):url
- URL (e.g.: "http://httpbin.org/ip")
Optional parameters:
:headers
- a hash containing the request headers:cookies
- a hash containing the request cookies:payload
- a hash containing query params:params
- a hash that represent query-string separated from the preceding part by a question mark (?
) a sequence of attribute–value pairs separated by a delimiter (&
):user
and:password
- for Basic Authentication:p_addr
,:p_port
,:p_user
, and:p_pass
- specify a per-request proxy by passing these parameters:max_redirects
- maximum number of redirections (default to 10):logging
- enable logging (default tofalse
):logger
- set logger (default toCrest::CommonLogger
):handle_errors
- error handling (default totrue
):http_client
- instance ofHTTP::Client
More detailed examples:
request = Crest::Request.new(:post,
"http://example.com/resource",
headers: {"Content-Type" => "application/json"},
payload: {:width => 640, "height" => "480"}
)
request.execute
Crest::Request.execute(:get,
"https://example.com/resource",
params: {:width => 640, "height" => "480"},
headers: {"Content-Type" => "application/json"})
)
Crest::Request.get(
"http://example.com/resource",
p_addr: "127.0.0.1",
p_port: 3128,
p_user: "admin",
p_pass: "1234"
)
A block can be passed to the Crest::Request
instance.
This block will then be called with the Crest::Request
.
request = Crest::Request.new(:get, "http://httpbin.org/headers") do |request|
request.headers.add("foo", "bar")
end
response = request.execute
response = Crest::Request.get("http://httpbin.org/headers") do |request|
request.headers.add("foo", "bar")
end
You can access HTTP::Client
via the http_client
instance method.
This is usually used to set additional options (e.g. read timeout, authorization header etc.)
client = HTTP::Client.new("http://example.com")
client.read_timeout = 1.second
begin
Crest::Request.new(:get,
"http://example.com/delay",
http_client: client
)
rescue IO::Timeout
puts "Timeout!"
end
Yeah, that's right! This does multipart sends for you!
file = File.open("#{__DIR__}/example.png")
Crest.post("http://example.com/upload", payload: {:image => file})
file = File.open("#{__DIR__}/example.png")
resource = Crest::Resource.new("https://example.com")
response = resource["/upload"].post(payload: {:image => file})
crest
does not speak JSON natively, so serialize your payload to a string before passing it to crest
.
Crest.post(
"http://example.com/",
headers: {"Content-Type" => "application/json"},
payload: {:foo => "bar"}.to_json
)
Request headers can be set by passing a hash containing keys and values representing header names and values:
response = Crest.get(
"http://httpbin.org/headers",
headers: {"Authorization" => "Bearer cT0febFoD5lxAlNAXHo6g"}
)
response.headers
# => {"Authorization" => ["Bearer cT0febFoD5lxAlNAXHo6g"]}
Request
and Response
objects know about HTTP cookies, and will automatically extract and set headers for them as needed:
response = Crest.get(
"http://httpbin.org/cookies/set",
params: {"k1" => "v1", "k2" => "v2"}
)
response.cookies
# => {"k1" => "v1", "k2" => "v2"}
response = Crest.get(
"http://httpbin.org/cookies",
cookies: {"k1" => "v1"}
)
response.cookies
# => {"k1" => "v1"}
For basic access authentication for an HTTP user agent you should to provide a user
name and password
when making a request.
Crest.get(
"http://httpbin.org/basic-auth/user/passwd",
user: "user",
password: "passwd"
)
If you need to use a proxy, you can configure individual requests with the proxy host and port arguments to any request method:
Crest.get(
"http://httpbin.org/ip",
p_addr: "localhost",
p_port: 3128
)
To use HTTP Basic Auth with your proxy, use next syntax:
Crest.get(
"http://httpbin.org/ip",
p_addr: "localhost",
p_port: 3128,
p_user: "user",
p_pass: "qwerty"
)
Logger
class is completely taken from halite shard. Thanks icyleaf!
By default, the Crest
does not enable logging. You can enable it per request by setting logging: true
:
Crest.get("http://example.com/resource", logging: true)
You can create the custom logger by integration Crest::Logger
abstract class.
Here has two methods must be implement: Crest::Logger.request
and Crest::Logger.response
.
class MyLogger < Crest::Logger
def request(request)
@logger.info ">> | %s | %s" % [request.method, request.url]
end
def response(response)
@logger.info "<< | %s | %s" % [response.status_code, response.url]
end
end
Crest.get("http://example.com/resource", logging: true, logger: MyLogger.new)
A Crest::Resource
class can be instantiated for access to a RESTful resource,
including authentication, proxy and logging.
Additionally, you can set default params
and headers
separately.
So can use Crest::Resource
to share common headers
and params
.
The final headers
and params
consist of:
- default headers from initializer
- headers provided in call method (
get
,post
etc)
This is especially useful if you wish to define your site in one place and call it in multiple locations.
resource = Crest::Resource.new(
"https://example.com",
params: {"key" => "value"},
headers: {"Content-Type" => "application/json"}
)
response = response["/admin/users"].get(
headers: {"Auth-Token" => "secret"}
)
response = response["/post"].post(
payload: {:height => 100, "width" => "100"},
params: {:secret => "secret"}
)
A block can be passed to the Crest::Resource
instance.
This block will then be called with the Crest::Resource
.
resource = Crest::Resource.new("http://httpbin.org") do |resource|
resource.headers.merge!({"foo" => "bar"})
end
response = resource["/headers"].get
With HTTP basic authentication:
resource = Crest::Resource.new(
"https://httpbin.org/get",
user: "user",
password: "password"
)
With Proxy authentication:
resource = Crest::Resource.new(
"https://httpbin.org/get",
p_host: "localhost",
p_port: 3128
)
Use the []
syntax to allocate subresources:
site = Crest::Resource.new("http://example.com")
response = site["/api/article"].post({:title => "Hello world", :body => "Crystal is awesome!"})
- for result codes between
200
and207
, aCrest::Response
will be returned - for result codes
301
,302
,303
or307
, the redirection will be followed and the request transformed into aGET
- for other cases, a
Crest::RequestFailed
holding the Response will be raised - call
.response
on the exception to get the server's response
Crest.get("http://example.com/nonexistent")
# => HTTP status code 404: Not Found (Crest::NotFound)
begin
Crest.get("http://example.com/nonexistent")
rescue ex : Crest::NotFound
puts ex.response
end
To not raise exceptions but return the Crest::Response
you can set :handle_errors => false
.
response = Crest.get("http://example.com/nonexistent", handle_errors: false)
response.status_code # => 404
By default, crest
will follow HTTP 30x redirection requests.
To disable automatic redirection, set :max_redirects => 0
.
Crest::Request.execute(method: :get, url: "http://httpbin.org/redirect/1", max_redirects: 0)
# => Crest::Found: 302 Found
The result of a Crest::Request
is a Crest::Response
object.
Response objects have several useful methods.
Response#body
: The response body as a stringResponse#status_code
: The HTTP response codeResponse#headers
: A hash of HTTP response headersResponse#cookies
: A hash of HTTP cookies set by the serverResponse#request
: TheCrest::Request
object used to make the requestResponse#http_client_res
: TheHTTP::Client::Response
objectResponse#history
: A list of each response received in a redirection chain
Install dependencies:
shards
To run test:
make test
crystal play
open http://localhost:8080
Then select the Workbook -> Requests from the menu.
- Fork it ( https://github.com/mamantoha/crest/fork )
- Create your feature branch (git checkout -b my-new-feature)
- Commit your changes (git commit -am 'Add some feature')
- Push to the branch (git push origin my-new-feature)
- Create a new Pull Request
- mamantoha Anton Maminov - creator, maintainer
- icyleaf Icyleaf Wang - logging support
- psikoz Logo design
Copyright: 2017-2018 Anton Maminov ([email protected])
This library is distributed under the MIT license. Please see the LICENSE file.