Ruby API with Sinatra

ban-ruby-sinatra
Để viết API có rất nhiều cách để viết bạn có thể tạo vởi rails project. Nhưng ở đây mình xin giới thiệu với mọi người cách tạo ra các API 1 cách đơn giản với sinatra.

I. API with Sinatra.

1, Install.

cài đặt sinatra gem file:

gem install sinatra
2, Getting started: Hello World

đầu tiên bạn cần tạo file: app.rb

# app.rb
require 'sinatra'

class HelloWorldApp < Sinatra::Base
  get '/' do
    "Hello, world!"
  end
end

ruby app.rb là có thể chạy được api rồi.

3. Routes

trong sinatra 1 route được định nghĩa với 1 method HTTP

get '/' do
  .. show something ..
end

post '/' do
  .. create something ..
end

put '/' do
  .. replace something ..
end

patch '/' do
  .. modify something ..
end

delete '/' do
  .. annihilate something ..
end

options '/' do
  .. appease something ..
end

link '/' do
  .. affiliate something ..
end

unlink '/' do
  .. separate something ..
end

Trong link cũng có thể bao gồm các parameters. Dữ liệu được lấy thông qua hash: params.

get '/hello/:name' do
  # Khi gọi link "GET /hello/foo" and "GET /hello/bar"
  # params[:name] sẽ lấy được ra là 'foo' or 'bar'
  "Hello #{params[:name]}!"
end

Chúng ta cũng có thể đặt tên cho param chuy cập. như ví dụ trên ta sẽ gắn param name vào biến n.

get '/hello/:name' do |n|
  # khi gọi link "GET /hello/foo" and "GET /hello/bar"
  # params[:name] sẽ là 'foo' hoặc 'bar' và được lưu trong biến n
  "Hello #{n}!"
end

Trong route ta cũng có thể sử dụng ký hiệu *, các giá trị được truyền vào * sẽ dược lưu trong params[:splat] kiểu array.

get '/say/*/to/*' do
  # gọi link /say/hello/to/world
  params[:splat] # = ["hello", "world"]
end

get '/download/*.*' do
  # gọi link /download/path/to/file.xml
  params[:splat] # = ["path/to/file", "xml"]
end

cũng giống như trên ta có thể gắn các * vào các biến khác nhau.

get '/download/*.*' do |path, ext|
  [path, ext] # = ["path/to/file", "xml"]
end

Ta cũng có thể sử dụng matching với Regular Expressions!.

get %r{/get/(\d\d)-(\d\d)-(\d\d\d\d)} do|month,day,year|
   "Get requested from #{month}/#{day} in #{year}"
end

II. Nginx Proxied to Unicorn

Để chạy các ứng dụng API của sinatra trên server 1 cách dễ dàng và tốt hơn người ta thường dùng kết hợp Nginx và Unicorn. Hướng dẫn dưới đây sẽ giúp các bạn thiết lập nó. Trước tiên ban phải cài đặt nginx và uncron.

install nginx.

add-apt-repository ppa:nginx/$nginx
apt-get update 
apt-get install nginx

install uncron

gem install unicorn

Chúng ta sẽ tìm hiểu sâu hơn thông qua ví dụ dưới đây. Chúng ta  tạo ra 1 app với cấu trúc như sau:

app
   |
   +-config.rb
   |
   +-myapp.rb
   |
   +-unicorn.rb
   |
   +-tmp
   |   |
   |   +-shockets
   |   |
   |   +-pids
   |
   +-log

trong config.rb ta dùng đoạn code sau

require "rubygems"
require "sinatra"

require File.expand_path '../myapp.rb', __FILE__

run MyApp

trong file myapp.rb sử dụng code sau

require "rubygems"
require "sinatra/base"

class MyApp \< Sinatra::Base

  get "/" do
    "Hello, nginx and unicorn!"
  end

end

cấu hình trong unicorn.rb

# set path to app that will be used to configure unicorn, 
# note the trailing slash in this example
@dir = "/path/to/app/"

worker_processes 2
working_directory @dir

timeout 30

# Specify path to socket unicorn listens to, 
# we will use this in our nginx.conf later
listen "#{@dir}tmp/sockets/unicorn.sock", :backlog => 64

# Set process id path
pid "#{@dir}tmp/pids/unicorn.pid"

# Set log file paths
stderr_path "#{@dir}log/unicorn.stderr.log"
stdout_path "#{@dir}log/unicorn.stdout.log"

Trong /etc/nginx/ ta sử file config như sau:

# this sets the user nginx will run as, 
#and the number of worker processes
user nobody nogroup;
worker_processes  1;

# setup where nginx will log errors to 
# and where the nginx process id resides
error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;

events {
  worker_connections  1024;
  # set to on if you have more than 1 worker_processes 
  accept_mutex off;
}

http {
  include       /etc/nginx/mime.types;

  default_type application/octet-stream;
  access_log /tmp/nginx.access.log combined;

  # use the kernel sendfile
  sendfile        on;
  # prepend http headers before sendfile() 
  tcp_nopush     on;

  keepalive_timeout  5;
  tcp_nodelay        on;

  gzip  on;
  gzip_vary on;
  gzip_min_length 500;

  gzip_disable "MSIE [1-6]\.(?!.*SV1)";
  gzip_types text/plain text/xml text/css
     text/comma-separated-values
     text/javascript application/x-javascript
     application/atom+xml image/x-icon;

  # use the socket we configured in our unicorn.rb
  upstream unicorn_server {
    server unix:/path/to/app/tmp/sockets/unicorn.sock
        fail_timeout=0;
  }

  # configure the virtual host
  server {
    # replace with your domain name
    server_name localhost:8081; # tên URL
    # replace this with your static Sinatra app files, root + public 
    root /path/to/app; # dẫn tới nơi để project của bạn.
    # port to listen for requests on
    listen 8081; # nghe ở cổng: 8081 ban có thể lựa chon cổng cho riêng mình.
    # maximum accepted body size of client request 
    client_max_body_size 4G;
    # the server will close connections after this time 
    keepalive_timeout 5;

    location / {
      try_files $uri @app;
    }

    location @app {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      # pass to the upstream unicorn server mentioned above 
      proxy_pass http://unicorn_server;
    }
  }
}

Để chạy chương trình ta có thể sử dụng 1 file sh(run.sh) như sau

#!/bin/sh

### BEGIN INIT INFO
# Provides: unicorn
# Required-Start: $local_fs $remote_fs
# Required-Stop: $local_fs $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts unicorn
# Description: starts uniconr using start-stop-daemon
### END INIT INFO

set -u
set -e

export PATH=/usr/local/rbenv/bin:$PATH
export RBENV_DIR=/usr/local/rbenv
export RBENV_ROOT=/usr/local/rbenv

APP_ROOT=/path/to/app/ # đường link tới app.
PID=APP_ROOT/tmp/pids/unicorn.pid
RAILS_ENV=development

export PATH="$RBENV_ROOT/shims:$RBENV_ROOT/rbenv:$PATH"
CMD="bundle exec unicorn -D -E $RAILS_ENV -c $APP_ROOT/unicorn.rb"

old_pid="$PID.oldbin"

cd $APP_ROOT || exit 1

sig () {
test -s "$PID" && kill -$1 `cat $PID`
}

oldsig () {
test -s $old_pid && kill -$1 `cat $old_pid`
}

case $1 in
start)
sig 0 && echo >&2 "Already running" && exit 0
$CMD
;;
stop)
sig QUIT && exit 0
echo >&2 "Not running"
;;
force-stop)
sig TERM && 0
echo >&2 "Not running"
;;
restart|reload)
sig HUP && echo reloaded OK && exit 0
echo >&2 "Couldn't reload, starting '$CMD' instead"
$CMD
;;
upgrade)
sig USR2 && exit 0
echo >&2 "Couldn't upgrade, starting '$CMD' instead"
;;
rotate)
sig USR1 && echo rotated logs OK && exit 0
echo >&2 "Couldn't rotate logs" && 1
;;
*)
echo >&2 "Usage $0 <start|stop|restart|upgrade|rotate|force-stop>"
exit 1
;;
esac

Starting the server

đầu tiên ta khởi dộng nginx:
/etc/init.d/nginx start

sau đó chạy lệnh: ./run.sh start

Stopping the server

rất đơn giản khi muốn dừng server ta chỉ cẩn chạy lệnh:
./run.sh stop

Trên đây là một số hướng dẫn đơn giản của mình về việc tạo và chạy 1 API với sinatra, nginx và uncron. Bài viết còn sơ sài rất mong mọi người góp ý.

Add a Comment