Lesson 5

Supporting new forms, create actions and CSRF

In order to allow the application to accept user generated quotes, we will need to build form where the user is prompted to enter details about the quote they want to add. We also will need to store the quote in the database when the form button is pressed. We will accomplish both in this lesson.

Building a new form

Right now if you navigate to localhost:4000/quotes/new, you'll see an error message indicating we need to add the new action to the controller.

Edit web/controllers/quote_controller.ex and add the new action to it:

defmodule Splurty.QuoteController do
  use Phoenix.Controller

  plug :action

  def homepage(conn, _params) do
    render conn, "homepage.html"
  end

  def index(conn, _params) do
    conn
    |> assign(:quotes, Repo.all(Splurty.Quote))
    |> render("index.html")
  end

def new(conn, _params) do render conn, "new.html" end
end

Save the file.

Refresh the page. The error message will change, and it will say that the new.html template doesn't exist. This means we will need to create a file called new.html.eex. To start with create the web/templates/quote/new.html.eex file with the following content:

Hello!

Save the file and refresh the page. We no longer are presented with an error message, instead we see the text: Hello!

In this lesson we'll want to use routing helpers to build URLs in our controller. To do that, we'll want to include the routing helpers by adding the following lines of code to web/controllers/quote_controller.ex:

defmodule Splurty.QuoteController do
  use Phoenix.Controller

alias Splurty.Router import Splurty.Router.Helpers
plug :action def homepage(conn, _params) do render conn, "homepage.html" end def index(conn, _params) do conn |> assign(:quotes, Repo.all(Splurty.Quote)) |> render("index.html") end def new(conn, _params) do render conn, "new.html" end end

Save the file and restart the server.

Next let's add the form to the view. Edit web/templates/quote/new.html.eex so it looks like this:

<h1>New Quote</h1>
<form action="<%= quote_path(@conn, :create) %>" method="post">
  <div class="form-group">
    <label for="quote[saying]">saying</label>
    <input type="text" name="quote[saying]" class="form-control" />

    <label for="quote[author]">author</label>
    <input type="text" name="quote[author]" class="form-control" />

  </div>
  <button type="submit" class="btn btn-primary">Save</button>
</form>

Save the file and refresh the page. Awesome, the form appeared!

Dealing with CSRF

Next, we should plan to support the creation of a quote in the database. Navigate to localhost:4000/quotes/new, populate the form with data and press the submit button.

An error message is displayed, indicating problems with CSRF. This is because we're not passing along a valid CSRF token in the form we're submitting.

Add the following helper method to web/view.ex, to allow us to get the CSRF token from the template:

defmodule Splurty.View do
  use Phoenix.View, root: "web/templates"

  # The quoted expression returned by this block is applied
  # to this module and all other views that use this module.
  using do
    quote do
      # Import common functionality
      import Splurty.Router.Helpers

      # Use Phoenix.HTML to import all HTML functions (forms, tags, etc)
      use Phoenix.HTML
    end
  end

def csrf_token(conn) do Plug.Conn.get_session(conn, :csrf_token) end
# Functions defined here are available to all other views/templates end

Save the file.

Add a hidden form field to pass through the CSRF token when the form is submitted by adding this line to the web/templates/quote/new.html.eex file:

<h1>New Quote</h1>
<form action="<%= quote_path(@conn, :create) %>" method="post">
  <div class="form-group">
<input type="hidden" name="csrf_token" value="<%= csrf_token(@conn) %>">
<label for="quote[saying]">saying</label> <input type="text" name="quote[saying]" class="form-control" /> <label for="quote[author]">author</label> <input type="text" name="quote[author]" class="form-control" /> </div> <button type="submit" class="btn btn-primary">Save</button> </form>

Save the file.

Navigate to localhost:4000/quotes/new and refresh the page. Then fill out the form and press the submit button. The error message changes, and indicates the problem now is that the controller action is undefined. This means we've properly dealt with CSRF.

Adding the create action

We'll need to add an action that will be triggered when someone pressed the submit button on the new form. Edit web/controllers/quote_controller.ex and add the create method, which will create a quote in the database and redirect the user to the index page:

defmodule Splurty.QuoteController do
  use Phoenix.Controller

  alias Splurty.Router
  import Splurty.Router.Helpers

  plug :action

  def homepage(conn, _params) do
    render conn, "homepage.html"
  end

  def index(conn, _params) do
    conn
    |> assign(:quotes, Repo.all(Splurty.Quote))
    |> render("index.html")
  end

  def new(conn, _params) do
    render conn, "new.html"
  end

def create(conn, %{"quote" => %{"saying" => saying, "author" => author}}) do q = %Splurty.Quote{saying: saying, author: author} Repo.insert(q) redirect conn, to: quote_path(conn, :index) end
end

Save the file. Navigate to localhost:4000/quotes/new, fill out the form and press the submit button. You will be redirected to the index page and see the quote you just added displayed on the page.

Your application should look just like this at this step.

Next Lesson