Chapter 14. Web APIs

Eventually, you might want to expand your application beyond your website. Popular web applications usually also have a native mobile client and sometimes even a desktop client. You may also want to integrate data from your application with other websites and applications.

A web application programming interface (or API) makes all of these things possible. Think of an API as a language that applications use to communicate with each other. On the Web, the API is usually a REST protocol using JavaScript Object Notation (JSON) messages.

In this chapter, we’ll explore the GitHub API to see how to access detailed information about users and repositories. After discussing GitHub’s API, you’ll build your own. In the process, I’ll cover details such as JSON, the Hypertext Transfer Protocol (HTTP), and token-based authentication.

The GitHub API

The GitHub code-hosting service has an extensive API. Many of its features are even available without authentication. If you want to continue exploring the GitHub API after working through the examples in this chapter, complete details are available online at https://developer.github.com/.

The GitHub API provides easy access to data about users, organizations, repositories, and other site features. For example, go to https://api.github.com/orgs/rails/ in your web browser to see the Rails organization on GitHub:

 {
   "login": "rails",
➊  "id": 4223,
➋  "url": "https://api.github.com/orgs/rails",
   "repos_url": "https://api.github.com/orgs/rails/repos",
   "events_url": "https://api.github.com/orgs/rails/events",
   "members_url": "https://api.github.com/orgs/rails/me...",
   "public_members_url": "https://api.github.com/orgs/r...",
   "avatar_url": "https://avatars.githubusercontent.com...",
   "name": "Ruby on Rails",
   "company": null,
   "blog": "http://weblog.rubyonrails.org/",
   "location": null,
   "email": null,
   "public_repos": 73,
   "public_gists": 3,
   "followers": 2,
   "following": 0,
   "html_url": "https://github.com/rails",
➌  "created_at": "2008-04-02T01:59:25Z",
➍  "updated_at": "2014-04-13T20:24:49Z",
   "type": "Organization"
}

The data returned should be at least partially familiar to anyone who’s worked with Rails models. You’ll see fields for id ➊, created_at ➌, and updated_at ➍, as seen in all of the models you’ve created so far. The GitHub API also includes several url fields ➋ that you can use to access more data about the organization.

For example, go to the repos_url (https://api.github.com/orgs/rails/repos/) to see a list of source code repositories belonging to the Rails organization. From there, you can access the details of an individual repository by going to its url, such as https://api.github.com/repos/rails/rails/.

Go to https://api.github.com/users/username/ to access information about an individual user. To see my GitHub account, visit https://api.github.com/users/anthonylewis/ in your browser.

Note

The data returned by these requests is in JavaScript Object Notation (JSON) format, which is based on a subset of the JavaScript programming language. In JSON format, data between curly braces is a single JavaScript object with various named properties. Each property consists of a name, followed by a colon, and the property value. This format is quite similar to a hash in Ruby.

In addition to the simple requests for data you’ve made so far, the GitHub API also supports creating and updating objects using the appropriate requests. These actions require authentication, of course. But before I can cover API authentication, I need to tell you a little more about HTTP.

HTTP

HTTP is the language of the Web. Web servers and browsers use this protocol to communicate. I’ve discussed some aspects of HTTP already, such as the HTTP verbs (GET, POST, PATCH, and DELETE), while covering the REST architecture in Chapter 4.

In addition to the data you’ve seen so far, an HTTP response also contains a header with more detailed information. You’re probably familiar with part of the data in an HTTP response header. Anyone who’s spent any time on the Web has probably seen a 404 or 500 response from a web server. Status codes such as these are included in every response from a web server.

Status Codes

The first line of every response includes an HTTP status code. This three-digit numeric code tells the client the type of response to expect.

Status codes are broken up into five categories based on their first digit:

  • 1xx Informational

  • 2xx Success

  • 3xx Redirection

  • 4xx Client Error

  • 5xx Server Error

You shouldn’t encounter any status codes in the 1xx range while working with APIs. The original HTTP 1.0 specification did not define any codes in this range, and in my experience, they are rarely used.

Status codes in the 2xx range indicate a successful request. Hopefully, you’ll encounter many of these. Common codes include 200 OK, which indicates a successful response, typically to a GET request; 201 Created, which is returned when an object is created on the server in response to a POST request; and 204 No Content, which indicates that a request was successful, but there is no additional data in the response.

The 3xx range of status codes indicates a redirect to a different address. Rails issues a 302 Found response any time you use redirect_to in your application. To see this in action, log in to your application and watch the log for the redirect.

Status codes in the 4xx range indicate some kind of client error. In other words, the user made a mistake. 401 Unauthorized is returned in response to a request for a URL that requires authentication. The 403 Forbidden status code is similar to 401, except the server will not complete the request even if the client successfully authenticates. The 404 Not Found is sent when a client attempts to access a URL that does not exist. As you work with APIs, you may encounter the 406 Not Acceptable status code for an invalid request or the 422 Unprocessable Entity status code, which means the request is valid, but the included data could not be processed.

The 5xx range of status codes indicates an error on the server. The 500 Internal Server Error code is the most commonly used. It is a general message that does not provide any additional data. The 503 Service Unavailable status code indicates a temporary problem with the server.

To see these codes, you need to examine the HTTP header sent with a response. These are not normally displayed by web browsers. Luckily, tools exist that make examining HTTP headers easy. One of the most popular is the command-line program known as Curl.

Curl

Curl is a free command-line tool for network communication. Curl is included with Mac OS X and Linux, and Windows users can download the tool from http://curl.haxx.se/. Curl uses URL syntax, making it an ideal tool for testing web APIs.

Open a terminal window and try a few curl commands. Let’s start with the GitHub API you just looked at.

$ curl https://api.github.com/users/anthonylewis
{
  "login": "anthonylewis",
  "id": 301,
  --snip--
}

This example shows how to retrieve information about a particular user account from GitHub. Curl only shows the response data by default; enter curl -i to include the HTTP headers with the response:

  $ curl -i https://api.github.com/users/anthonylewis
➊ HTTP/1.1 200 OK
  Server: GitHub.com
  Date: Thu, 17 Apr 2014 00:36:29 GMT
  Content-Type: application/json; charset=utf-8
  Status: 200 OK
➋ X-RateLimit-Limit: 60
  X-RateLimit-Remaining: 58
➌ X-RateLimit-Reset: 1397696651
  --snip--

  {
   "login": "anthonylewis",
   "id": 301,
   --snip--
  }

The response headers start with the status code of 200 OK ➊. Also note that GitHub API requests are rate limited. The X-RateLimit-Limit: 60 line ➋ indicates that you are limited to 60 requests over a certain period of time. The next line says you have 58 requests remaining. Your rate limit resets automatically at the time given by the X-RateLimit-Reset: 1397696651 line ➌.

Note

The number 1397696651 is a Unix timestamp. You can convert it to a normal time by entering Time.at 1397696651 in an IRB session or Rails console.

Authentication

So far, you’ve only read public data from the GitHub API. You can also use the GitHub API to read private data about users and repositories and to create or update information, but these actions require authentication.

I covered user authentication in Chapter 9. Users expect to log in to an application once and then browse a site for some time. You maintain a user’s log in state in the session, which is stored in a cookie that the browser automatically includes with every request.

API requests don’t maintain a session. Applications accessing an API need to provide authentication credentials with each request. A popular choice for API requests is token-based authentication. In token-based authentication, users include a unique API token with each request.

You can use the curl command to test token-based authentication on GitHub. First, you need to generate a personal access token on GitHub’s Application Settings page. Log in to GitHub, if necessary, and go to https://github.com/settings/applications/. On that page, click the Generate New Token button. Next, you provide a description for this token; something like API Testing should be fine. Finally, confirm that the checkboxes beside “repo” and “user” are checked, and click the Generate Token Button.

GitHub should take you back to the Application Settings page and present you with a new 40-digit hexadecimal token. Copy your new token and paste it into a text file so you can keep up with it. As the on-screen message says, you won’t be able to see it again!

To verify your token is working, enter the following curl command in your terminal. Replace the word token with your actual token in all of these requests:

$ curl -H "Authorization: Token token" https://api.github.com/user
{
  "login": "anthonylewis",
  "id": 301,
  --snip--

Here, I’ve used the -H parameter to curl to pass custom header data to the server, and, in this case, that data is the Authorization: Token header followed by my token.

You should see information about your own account, even though you didn’t specify a username. GitHub uses your personal access token to authenticate the request.

You can now use the token to access private information, such as the list of Git repositories associated with your account.

$ curl -H "Authorization: Token token" https://api.github.com/user/repos
[
  {
    "id": 6289476,
    "name": "blog",
    "full_name": "anthonylewis/blog",
    "owner": {
      "login": "anthonylewis",
      "id": 301,
      --snip--

GitHub should return an array of repositories created with your account. Depending on how many repositories you’ve created, this could be a lot of data.

Now that you have a token, you can also add another repository to your account using a POST request. As you learned in Chapter 4, POST means create in REST.

➊ $ curl -i -d '{"name":"API Test"}' 
         -H "Authorization: Token token" 
         https://api.github.com/user/repos
➋ HTTP/1.1 201 Created
  Server: GitHub.com
  Date: Mon, 21 Apr 2014 23:47:59 GMT
  Content-Type: application/json; charset=utf-8
  Status: 201 Created
  --snip---
➌ {
    "id": 18862420,
    "name": "API-Test",
    "full_name": "anthonylewis/API-Test",
    "owner": {
      "login": "anthonylewis",
    "id": 301,
  --snip--

The -d option to curl specifies data to be included with the request. Here, you send a JSON string with the name "API Test" for the new repository ➊. Because you’re sending data, curl automatically uses a POST request. GitHub responds to the request with headers indicating HTTP status 201 Created ➋, followed by information about the newly created repository ➌.

Now that you have some experience with an existing API, let’s create our own API for our social application.

Your Own API

You may remember from Chapter 4 that the Rails scaffold generator used the respond_to method inside the PostsController to return different data based on the type of request. This approach is fine for some applications, but the addition of user authentication and sessions in your application leads to problems.

The existing controllers authenticate users by calling the authenticate_user! method before every action. Your API will use a different method to support token-based authentication. The existing controllers also display data, such as posts, based on the value of current_user. Your API will display all posts when requested.

Rather than use the same controllers for the application and the API, you can build separate controllers for each. Because your application is mainly about posts, you’ll start there when building your API.

API Routes

Start by adding routes for API requests. The GitHub API used a subdomain for API requests. Because you haven’t set up your own domain, you’ll use a separate path for API requests. Open the file config/routes.rb and add the following block near the end:

  Social::Application.routes.draw do
  --snip--namespace :api do
      resources :posts
    end
  end

The namespace :api block ➊ indicates that all routes created for the resources it contains start with the path api/. Additionally, the controller files for those resources should be inside a directory named api, and the controller classes should be inside a module named Api.

You can enter the bin/rake routes command in a terminal to see the newly created routes.

API Controllers

Now that you’ve defined the routes, you need to create a controller to handle these actions. First, create a directory for the API controllers by entering the following command:

$ mkdir app/controllers/api

Then create a new file named app/controllers/api/posts_controller.rb and add the code for the API PostsController, as shown here:

  module Api
    class PostsController < ApplicationControllerrespond_to :jsondef index
      @posts = Post.allrespond_with @posts
    end
  end
end

The file starts with module Api to indicate this class belongs to the API namespace. Inside the PostsController class is a call to the respond_to class method. Calling respond_to :json, indicates that the actions in this controller return JSON data ➊.

The class then defines the index action ➋. The index action retrieves all posts and then uses the respond_with method to send them to the client ➌. The respond_with method automatically formats the data based on the format and HTTP verb used in the request. In this case, it should return JSON data in response to a GET request for the index action.

After you save this file, start the Rails server if it isn’t already started. Then you can use curl to test your API by entering this command:

$ curl http://localhost:3000/api/posts
[{"id":1,"title":"First Post","body":"Hello, World!"...

The API returns an array of posts in response to the posts index action.

The data is compact and on a single line, which can be hard to read, but several free tools can pretty-print JSON data for you. For example, jq is a JSON processor that pretty-prints JSON data and adds syntax highlighting. Download jq from http://stedolan.github.io/jq/. Once installed, you can pipe the output through jq’s basic filter by adding | jq '.' to the end of the command:

$ curl http://localhost:3000/api/posts | jq '.'
[
  {
   "id": 1,
   "title": "First Post",
   "body": "Hello, World!",
   "url":null,
   "user_id":1,
   --snip--

The remaining examples in this chapter are pretty-printed. I leave off the | jq '.' for brevity, but you should include it if you want your output to look like what you see in the book. You can also see JSON output in your web browser. Entering http://localhost:3000/api/posts in your web browser causes an ActionController::UnknownFormat error. If you check the server output in your terminal, you’ll see this is a 406 Not Acceptable error, as discussed earlier in this chapter. This error occurs because the controller only responds to JSON requests, but your web browser asks for HTML by default.

Specify a different content type by adding an extension to the URL in the address bar. Browsing to http://localhost:3000/api/posts.json returns a JSON array of posts as expected.

Customizing JSON Output

So far your API returns all of the data associated with each post. You may want to include additional data with each record, and, in some cases, you may want to exclude data from some fields. For example, including data about the author of each post is helpful, but you don’t want to include the user’s password_digest or api_token.

You can customize the output from your API built in to Rails in a couple of ways. Which method you use depends on how much customization you need and your personal preference.

as_json

Because this API returns JSON data, you can easily customize the output by changing the way Rails converts a model to JSON. Rails first calls the as_json method on a model to convert it to a hash, which is then converted to a JSON string.

You can override the as_json method in the Post model to customize the data returned for each post. Open the file app/models/post.rb and add the as_json method, shown here, to force the method to show only each post’s id and title:

  class Post < ActiveRecord::Base
    --snip--def as_json(options={})super(only: [:id, :title])
    end

    --snip--
  end

Be sure to include the options parameter with a default value of {} ➊ because the original as_json includes it. You aren’t using the options parameter, but because you’re overriding an existing method, your definition must match the original. Your as_json method calls super, which invokes the original as_json method defined by Active Record, with the parameter only: [:id, :title] ➋.

With this method in place, your API should only return the id and title of each post. Use the curl command to verify the change:

$ curl http://localhost:3000/api/posts
[
  {"id": 1, "title": "First Post"},
  {"id": 2, "title": "Google Search"}
]

The as_json method supports several additional options. Instead of specifying fields to include with :only, you could exclude fields with the :except option. You can also include associated models with the :include option. For example, update the as_json method, as shown here, to exclude the user_id field and include the post’s associated user model:

def as_json(options={})
  super(except: [:user_id], include: :user)
end

The :methods option calls a list of methods and includes their return values in the output. For example, you can use this option to call the cached_comment_count method you added in Chapter 12:

def as_json(options={})
  super(except: [:user_id], include: :user,
    methods: :cached_comment_count)
end

This option will include the cached number of comments associated with this post in the output.

Overriding as_json certainly works, but depending on the level of customization required, this can get a bit messy. Fortunately, Rails provides a way to customize fully the JSON data returned by your API. Remove the as_json method from the Post model and let’s cover jbuilder.

Jbuilder

Jbuilder is a domain-specific language for generating JSON output. The jbuilder gem is included by default in the Gemfile generated by the rails new command. Using jbuilder, you can create views for each of your API actions, just as you used ERB to create views for web actions.

As with your other views, you need to create a directory for your jbuilder views. The view directory must match the controller name. Enter the following commands to create a directory for API views and a subdirectory for the PostsController views:

$ mkdir app/views/api
$ mkdir app/views/api/posts

With these directories in place, you can create your first jbuilder view. Create a new file named app/views/api/posts/index.json.jbuilder and open it in your editor. Add this single line of code and save the file:

json.array! @posts

The json.array! method tells jbuilder to render the value of @posts as a JSON array. Use Curl to check the output of the index action:

$ curl http://localhost:3000/api/posts
[
  {
    "id": 1,
    "title": "First Post",
    "body": "Hello, World!",
    "url":null,
    "user_id":1,
    --snip--

The output is the same as when you started. Now let’s see about customizing this output.

The json.array! method also accepts a block. Inside the block, you can access each individual record in the array. You can then use the json.extract! method to include only certain fields from the post:

json.array! @posts do |post|
  json.extract! post, :id, :title, :body, :url
end

This example renders the id, title, body, and url fields from each post as JSON.

All of the usual view helpers are also available in jbuilder views. For example, you can include a URL for each post using the api_post_url helper method:

  json.array! @posts do |post|
    json.extract! post, :id, :title, :body, :url
➊   json.post_url api_post_url(post)
end

The output of method calls, such as api_post_url(post) ➊, are automatically converted to JSON format. The next example adds some data about the author of each post:

json.array! @posts do |post|
  json.extract! post, :id, :title, :body, :url
  json.post_url api_post_url(post)

  json.user do
    json.extract! post.user, :id, :name, :email
  end
end

Here, I’ve used the json.extract! method again to include only specific fields for each user. You don’t want to make the password_digest for users available through your public API.

Token-Based Authentication

Now let’s add authentication so you can also create posts through your API. You’ll add token-based authentication, like you used earlier when accessing the GitHub API.

Generating Tokens

First, add a field for the api_token string to the User model by generating a database migration:

$ bin/rails g migration add_api_token_to_users api_token:string

Remember to enter the bin/rake db:migrate command after generating this migration to update your database.

Now update the User model by opening app/models/user.rb in your editor and adding a validation for the api_token field and a before_validation call-back to generate the API token:

   class User < ActiveRecord::Base
     --snip--validates :api_token, presence: true, uniqueness: truebefore_validation :generate_api_token

     --snip--

First, you need to validate that the api_token is present and unique ➊. Because you’re using this value to authenticate, no two users can have the same api_token.

Next, you use a before_validation callback to call a method to generate the api_token if it doesn’t already exist ➋. Add the generate_api_token method at the bottom of the User model as shown here:

  class User < ActiveRecord::Base

    --snip--

    def generate_api_tokenreturn if api_token.present?

      loop doself.api_token = SecureRandom.hexbreak unless User.exists? api_token: api_token
      end
    end

  end

The generate_api_token method returns immediately if the api_token already has a value ➊. If a value is not present for the api_token, the method calls SecureRandom.hex inside an endless loop to generate a value ➋. The SecureRandom class uses the most secure random-number generator available on your computer to generate values. On Unix computers, it uses the /dev/urandom device; on Windows, it uses the Win32 Cryptographic API. The SecureRandom class also includes several methods for formatting random values. The hex method returns a random 32-character hexadecimal value. Finally, if a user with this api_token doesn’t exist, break out of the loop ➌.

Now open a Rails console and update the existing users:

➊ irb(main):001:0> user = User.first
    User Load (0.2ms) SELECT "users".* ...
  => #<User id: 1, ... api_token: nil>
➋ irb(main):002:0> user.save
     (0.1ms) begin transaction
    User Exists (0.2ms) SELECT 1 AS one FROM ...
    User Exists (0.1ms) SELECT 1 AS one FROM ...
    User Exists (0.1ms) SELECT 1 AS one FROM ...
    SQL (1.3ms) UPDATE "users" SET "api_token" ...
     (1.7ms)  commit transaction
  => true

Because the generate_api_token method is called automatically using a before_validation callback, you simply need to load the user into a variable ➊ and then save it to the database ➋ to update it. Do this for each of your users. If any user doesn’t have a value for api_token, it will be created.

Now update the user show view to display the api_token when a user views his or her own account. Update app/views/users/show.html.erb as shown here:

  <div class="page-header">
    <h1>User</h1>
  </div>

  <p class="lead"><%= @user.email %></p>

➊ <% if @user == current_user %>
    <p class="lead">API Token: <%= @user.api_token %></p>
  <% end %>

--snip--

Because API tokens are essentially passwords, you want to protect them by only showing them when the user being displayed is equal to the current_user ➊.

Authenticating Requests

Now that all users have an API token, let’s put those tokens to use. The process of authenticating with a token is similar to the username and password authentication you already created. Because you may have more than one controller for your API, you should include the authentication method in ApplicationController, which is the parent class of all other controllers.

First, you need a method to authenticate using an api_token. Luckily, Rails has a built-in method called authenticate_or_request_with_http_token to handle the details for you. Open the file app/controllers/application_ controller.rb and add the following method to see how this works:

  class ApplicationController < ActionController::Base
    # Prevent CSRF attacks by raising an exception.
    # For APIs, you may want to use :null_session instead.
    protect_from_forgery with: :exception

    private

    def authenticate_token!
      authenticate_or_request_with_http_token do |token, options|@api_user = User.find_by(api_token: token)
      end
    end

    --snip--

This method is named authenticate_token! to match the authenticate_user! method you added in Chapter 9. The authenticate_or_request_with_http_token retrieves the token included in the request’s Authorization header and passes it to a block. Inside the block, you try to find a user in the database using the given token ➊. The find_by method returns a User object if a matching user is found, or nil otherwise. This value is assigned to the @api_user instance variable and returned from the block. If the block returns a false value, such as nil, the method knows that authentication failed and sends a 401 Unauthorized response to the client.

You wrote a helper method called current_user for accessing the authenticated user in Chapter 9. For API requests, the authenticated user is already assigned to the @api_user instance variable, so you can use this variable.

Your token-based authentication solution is ready to go now. Let’s try it out by adding the ability to create text posts through your API.

Using Token-Based Authentication

First, you need to add routes for text posts, so open config/routes.rb and add the text_posts resources inside the :api namespace:

Social::Application.routes.draw do
--snip--

  namespace :api do u
    resources :posts
    resources :text_posts
  end
end

Now you need a controller for text posts. Remember, it needs to be inside the api/ directory because the routes are in the :api namespace. Create a file named app/controllers/api/text_posts_controller.rb and add the following code:

  module Api
    class TextPostsController < ApplicationController
      respond_to :jsonbefore_action :authenticate_token!

    end
  end

This controller starts the same as the API posts controller. The TextPostsController class must be inside a module called Api. It also includes respond_to :json. The first change is the addition of before_action :authenticate_token! ➊. The controller calls the authenticate_token! method before each action.

You want to create text posts, so add the create method:

  module Api
    class TextPostsController < ApplicationController
      respond_to :json
      before_action :authenticate_token!
➊   def create
      @text_post = @api_user.text_posts.create(text_post_params)
      respond_with @text_post
    end
  end
end

The create method uses the @api_user instance variable set inside authenticate_token! to create a new text post ➊. You then use respond_with to send the new text post back to the client. Note that you don’t check to see whether the text post was actually created. The respond_with method automatically sends the appropriate error response if @text_post contains errors.

Because you also want to specify permitted parameter values, your final addition is a text_post_params method:

  module Api
    class TextPostsController < ApplicationController
      before_action :authenticate_token!

      respond_to :json

      def create
        @text_post = @api_user.text_posts.build(text_post_params)
        respond_with @text_post
      end

     privatedef text_post_params
       params.require(:text_post).permit(:title, :body)
     end
  end
end

The text_post_params method permits data for a :title and :body in a nested hash with the key :text_post ➊. This is the same as the text_post_params method in the controller for web requests.

Enter the curl command to try out the new API. Make sure to set the Content-Type header to application/json when you run the command, so Rails automatically parses the JSON data included with your request. Replace the word token with the actual api_token from one of your application’s users.

  $ curl -i 
       -d '{"text_post":{"title":"Test","body":"Hello"}}' 
       -H "Content-Type: application/json" 
       -H "Authorization: Token token" 
       http://localhost:3000/api/text_posts
1 HTTP/1.1 422 Unprocessable Entity
--snip--

Something went wrong: The status code 422 Unprocessable Entity ➊ means the data the client passed to the server is not valid. Check the server output in your terminal for more information.

  Started POST "/api/text_posts" for 127.0.0.1 at 2014-04-23 19:39:09 -0500
  Processing by Api::TextPostsController#create as */*
    Parameters: {"text_post"=>{"title"=>"Test", "body"=>"Hello"}}
➊ Can't verify CSRF token authenticity
  Completed 422 Unprocessable Entity in 1ms

--snip--

The data passed to the server is valid but didn’t include a CSRF token ➊. Remember, this token is not the same as the API token. The CSRF token is another unique token that is sent automatically when you submit form data in your application. Because you aren’t submitting a form, you have no way of knowing the correct CSRF token.

When you were updating the ApplicationController earlier, you may have noticed a helpful comment at the top of the class. Rails normally prevents CSRF attacks by raising an exception. This is great for a web application, but it won’t work for an API. Instead of raising an exception, you can prevent CSRF attacks by clearing out the user’s session data. Now any time the application receives data from a user that does not include the CSRF token, it clears the user’s session, effectively logging the user out of the application and preventing the attack.

Fortunately, rather than store authentication data in the session, API clients include the correct API token with each request. So API requests should work fine with a null session. Open app/controllers/application_controller.rb in your editor and make the following update:

  class ApplicationController < ActionController::Base
    # Prevent CSRF attacks by raising an exception.
    # For APIs, you may want to use :null_session instead.
➊   protect_from_forgery with: :null_session

    --snip--

In the protect_from_forgery method call ➊, change the value of the :with option to :null_session, and then try the same request again using curl:

  $ curl -i 
         -d '{"text_post":{"title":"Test","body":"Hello"}}' 
         -H "Content-Type: application/json" 
         -H "Authorization: Token token" 
         http://localhost:3000/api/text_posts
➊ HTTP/1.1 201 Created
  --snip--
➋ {
    "id":5,
    "title":"Test",
    "body":"Hello",
    "url":null,
    "user_id":1,
    "created_at":"2014-04-24T00:33:35.874Z",
    "updated_at":"2014-04-24T00:33:35.874Z"
  }

The status code is now 201 Created, which means success ➊. The HTTP headers are followed by a JSON representation of the new text post ➋. Because you didn’t create a jbuilder view for this action, the default JSON representation is used.

You can also open the posts index page in your browser, or issue an API request for all posts with the command curl http://localhost:3000/api/posts, to verify the text post was created successfully.

Summary

A Web API can open up your application to collaborations from both your customers and third-party applications. With an effective API, you can also build native mobile or desktop clients for your application. You could even use another application’s API to integrate its data into yours.

In this chapter, we discussed the GitHub API and used it to access detailed data about users and repositories. After covering the Hypertext Transfer Protocol and token-based authentication, you built your own API for your social network application.

In the next chapter, you’ll learn how to set up your own server to host Rails applications and use the Capistrano remote server automation tool to deploy and maintain your applications.

Exercises

Q:

1. Verify that your token-based authentication is really working by issuing a POST request with a fake token. Use the curl command to send the request, and be sure to check both the status code in the headers and the response body.

Q:

2. Try to create a text post with invalid data and see what happens. You can check the validation for text posts in app/models/text_post.rb. Again, use the curl command to send the request and be sure to check the status code in both the headers and the response body.

Q:

3. Extend the API by adding a show action to the posts controller. This action should find the correct post using params[:id] and then use the respond_with method to send the post back to the client. Because this is a GET request, you can check it with curl or in your web browser.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.68.18