Visualizzazione post con etichetta RESTful. Mostra tutti i post
Visualizzazione post con etichetta RESTful. Mostra tutti i post

mercoledì 21 febbraio 2018

Simple HTTP REST Server on ESP8266








My follower and my friends know that I am not a fan of Web Server on ESP8266 as this has negative architectural implications. In any case this is not the topic of this post.

In the past few days I have received some requests on how to create a Web Server on ESP8266.

As the typical use for ESP8266 is mostly as IoT device I think it make much more sense to show how to create a simple HTTP REST server on ESP8266 in order to manage some resources.

In this post I show how to manage 1 Led through a RESTful API on ESP8266.


Testing Circuit


The testing circuit is quite simple and is shown below











Code


The code of the REST server is available on my GitHub here











Testing


In order to test the REST server I use the curl command from command line (for the one who prefer a graphical interface I suggest the great  postman)

GET ROOT


The first command I execute is a HTTP GET of the server's root

The IP address will be certainly different in your case, so please replace it with your own (it is printed on Serial Monitor during ESP boot).






As you can see the string defined on line 118 is provided back by the server.


GET leds


Now time to query the REST server for leds resource through a HTTP GET





As you can see the HTTP code 204 is provided back meaning that the resource is neither available nor is empty.




POST leds


Now let's create the resource by providing a JSON body as part of the HTTP POST request. The JSON contains the following information:

  • id: a unique identifier of the resource
  • gpio: GPIO number on which the resource (led) is connected
  • status: resource's status (0 or LOW)





Now the server replies back with a 201 meaning that the resource has been successfully created.


Now I query again the resource through GET and this time server replies with 200 and with the JSON representation of the resource.




PUT leds


Now that the resource is available it is possible to modify it with a HTTP PUT


Also in this case I need to pass a JSON representation in the body of the request in which I set the status to 1(HIGH).






The server replies with a 200 code meaning that the update is successful and the led is turning on





In the same manner I can updated it with status = 0






and the led turns off





In the sample code I also manage other cases.

As an example if I update (PUT) a resource with id = 2





Server replies with 404 telling me that the resource is not existing 


I let to the reader the implementation of the DELETE method.

That's it folks!!! :)










giovedì 24 luglio 2014

Build a simple RESTful Web Service JSON API with Ruby and Sinatra

In one my activities I have the needs to quickly prototype a RESTful web service API.
Obviously I want to do it with my current preferred language Ruby.

It is clear I can easily do it with Rails but probably it will be an overkill for what I need to do.
Reading different articles about major differences between Rails and Sinatra micro-framework I have decided to build the simple REST web service I need using Sinatra.

I am here sharing what I have done. This is something I put together quite quickly, so it is not complete.
Anyone of you which wants to help can contribute.

Let's install the necessary ruby gems.

# cd ~
#
# mkdir rails_api
#
# mkdir rails_api/json
#
# sudo gem install sinatra
#
# sudo gem install rabl
#
# sudo gem install oj
#
# sudo gem install data_mapper
#
# sudo gem install dm-sqlite-adapter
#
# sudo gem install thin

I am using Rabl gem as this provides great flexibility in creating JSON replies to API request

Now in the rails_api/json directory create 3 files with the below content

#post.rabl
object @post
attributes :id, :title, :body

#posts.rabl
collection @posts
attributes :id, :title, :body

#error.rabl
object @error
attributes :message

Those files render the JSON replay to our RESTful API call. Now in the rails_api directory create the following file
#rest_api.rb

require 'rubygems'
require 'sinatra'
require 'rabl'
require 'data_mapper'
require 'json' 

######## setup sqlite DB ########
DataMapper.setup(:default, 'sqlite:rest_api.db')

######## Post Model ########
class Post
  include DataMapper::Resource

  property :id,         Serial    # An auto-increment integer key
  property :title,      String    # A varchar type string, for short strings
  property :body,       Text      # A text block, for longer string data.
end

DataMapper.finalize
Post.auto_upgrade!


######## Very simple Error class ########
class Error 
 attr_accessor :message
 def initialize(mex)
  @message = mex
 end
end

######## register Rabl. This is required for Sinatra ########
Rabl.register!

######## retrun 404 in case of invalid URL request ########
not_found do
 status 404 #page not found
end

######## simple helper to return HTTP 400 ########
def status_400(mex)
 @error = Error.new(mex)
 status 400
 rabl :'error', :format => "json", :views => 'json' 
end

######## simple helper to return HTTP 404 ########
def status_404(mex)
 @error = Error.new(mex)
 status 404
 rabl :'error', :format => "json", :views => 'json' 
end

######## simple helper to return HTTP 200 ########
def status_200
 status 200
end

######## simple helper to return HTTP 201 ########
def status_201(link)
 status 201
 #headers "location:/post/43"
 headers "Location"   => link
end

######## REST CRUD APIs for Post resource ########

######## index of Post ########
#GET post
get '/post' do 
 @posts = Post.all
 if @posts.empty?
  status_404("The reuqested resource does not exists")
 else
  status_200
    rabl :'posts', :format => "json", :views => 'json'
   end
end

######## show one Post ########
# GET /post/:id
get '/post/:id' do
 @post = Post.get(params[:id])
 if @post.nil?
  status_404("The reuqested resource does not exists")
 else
  status_200
    rabl :'post', :format => "json", :views => 'json'
   end
end

######## create a Post ########
#POST /post
post '/post' do
 begin
  post_data =  JSON.parse request.body.read 

  if post_data.nil? or !post_data.has_key?('title') or !post_data.has_key?('body')
   raise "exception"
  else
   post = Post.create(:title => post_data['title'], :body => post_data['body'])
   post.save
   status_201("/post/#{post.id}")
  end
 rescue => ex
  status_400("Invalid client request")
 end
end

######## Update a Post ########
#PUT /post/:id
put '/post/:id' do
 begin
  put_data =  JSON.parse request.body.read 

  if put_data.nil? or !put_data.has_key?('title') or !put_data.has_key?('body')
   raise "exception"
  else
   post = Post.get(params[:id])
   if post.nil?
    status_404("The reuqested resource does not exists")
   else
    post.title = put_data['title']
    post.body = put_data['body']
    post.save
    status_200
   end
  end
 rescue => ex
  status_400("Invalid client request")
 end

end

######## Remove a Post ########
#DELETE /post/:id
delete '/post/:id' do
 post = Post.get(params[:id])
 if post.nil?
  status_404("The reuqested resource does not exists")
 else
  post.destroy
  status_200
 end
end
Ok now just run it with
# ruby rest_api.rb -o 0.0.0.0
You should get something like this
== Sinatra/1.4.5 has taken the stage on 4567 for development with backup from Thin
Thin web server (v1.6.2 codename Doc Brown)
Maximum connections set to 1024
Listening on 0.0.0.0:4567, CTRL+C to stop

Let's now test our RESTful Web Service. You can use your preferred tool like POSTMAN in Chrome.
I will use the always available curl to test the typical CRUD functions

CREATE

# curl -H "Content-type: application/json" -X POST  -d '{"title": "post1", "body": "this is post 1"}'  http://127.0.0.1:4567/post
READ
# curl  http://127.0.0.1:4567/post

# curl  http://127.0.0.1:4567/post/1
UPDATE
# curl -H "Content-type: application/json" -X PUT  -d '{"title": "post 5", "body": "this is post #5"}'  http://127.0.0.1:4567/post/1
DELETE
# curl -X DELETE http://127.0.0.1:4567/post/1

I am sure it is quite easy to argue that the HTTP return code I have used are not correct. Looks like this is quite an open topic.

In my example here I have mapped HTTP Status Code to REST call according to what described in this article (Using HTTP Status Codes correctly in your REST Web API)

As always let me know about any improvement, suggestion or correction.

Hope this helps someone out there.