0
# Web Foundation and Request Handling
1
2
Phoenix's web foundation provides the core infrastructure for handling HTTP requests, routing URLs to controllers, and generating responses. The system is built on a plug-based architecture that processes requests through configurable pipelines.
3
4
## Endpoint Configuration and Management
5
6
The endpoint is the entry point for all web requests and defines the foundational configuration.
7
8
### Phoenix.Endpoint
9
10
```elixir { .api }
11
defmodule Phoenix.Endpoint do
12
# Supervision and lifecycle
13
@callback start_link(keyword) :: Supervisor.on_start()
14
@callback config(atom, term) :: term
15
@callback config_change(term, term) :: term
16
17
# URL and path generation
18
@callback url() :: String.t()
19
@callback struct_url() :: URI.t()
20
@callback path(String.t()) :: String.t()
21
@callback static_url() :: String.t()
22
@callback static_path(String.t()) :: String.t()
23
@callback static_integrity(String.t()) :: String.t() | nil
24
@callback host() :: String.t()
25
@callback script_name() :: [String.t()]
26
27
# Server information
28
@callback server_info(Plug.Conn.scheme()) :: {:ok, {String.t(), pos_integer}} | :error
29
30
# PubSub integration
31
@callback subscribe(binary, keyword) :: :ok | {:error, term}
32
@callback unsubscribe(binary) :: :ok | {:error, term}
33
@callback broadcast(binary, binary, term) :: :ok | {:error, term}
34
@callback broadcast!(binary, binary, term) :: :ok
35
@callback broadcast_from(pid, binary, binary, term) :: :ok | {:error, term}
36
@callback broadcast_from!(pid, binary, binary, term) :: :ok
37
@callback local_broadcast(binary, binary, term) :: :ok
38
@callback local_broadcast_from(pid, binary, binary, term) :: :ok
39
end
40
```
41
42
### Usage Example
43
44
```elixir
45
defmodule MyAppWeb.Endpoint do
46
use Phoenix.Endpoint, otp_app: :my_app
47
48
# Static file serving
49
plug Plug.Static,
50
at: "/", from: :my_app, gzip: false,
51
only: ~w(assets fonts images favicon.ico robots.txt)
52
53
# Session configuration
54
plug Plug.Session,
55
store: :cookie,
56
key: "_my_app_key",
57
signing_salt: "signing_salt"
58
59
# Router
60
plug MyAppWeb.Router
61
end
62
63
# Start endpoint in application supervisor
64
children = [
65
MyAppWeb.Endpoint
66
]
67
68
Supervisor.start_link(children, strategy: :one_for_one)
69
```
70
71
## Routing System
72
73
Phoenix's router maps URLs to controller actions through a declarative DSL with support for pipelines, scoping, and resource routing.
74
75
### Phoenix.Router
76
77
```elixir { .api }
78
defmodule Phoenix.Router do
79
# HTTP verb macros
80
defmacro get(path, plug, plug_opts \\ [], options \\ [])
81
defmacro post(path, plug, plug_opts \\ [], options \\ [])
82
defmacro put(path, plug, plug_opts \\ [], options \\ [])
83
defmacro patch(path, plug, plug_opts \\ [], options \\ [])
84
defmacro delete(path, plug, plug_opts \\ [], options \\ [])
85
defmacro options(path, plug, plug_opts \\ [], options \\ [])
86
defmacro head(path, plug, plug_opts \\ [], options \\ [])
87
defmacro match(verb_or_verbs, path, plug, plug_opts \\ [], options \\ [])
88
89
# Resource routing
90
defmacro resources(path, controller, opts \\ [])
91
defmacro resources(path, controller, opts, do_block)
92
93
# Scoping and organization
94
defmacro scope(options \\ [], do: context)
95
defmacro scope(path, options \\ [], do: context)
96
defmacro scope(path, alias, options \\ [], do: context)
97
98
# Pipeline system
99
defmacro pipeline(name, do: block)
100
defmacro plug(plug, opts \\ [])
101
defmacro pipe_through(pipes)
102
103
# Forwarding
104
defmacro forward(path, plug, plug_opts \\ [], router_opts \\ [])
105
end
106
```
107
108
### Usage Examples
109
110
```elixir
111
defmodule MyAppWeb.Router do
112
use Phoenix.Router
113
114
# Define pipelines
115
pipeline :browser do
116
plug :accepts, ["html"]
117
plug :fetch_session
118
plug :fetch_live_flash
119
plug :put_root_layout, {MyAppWeb.LayoutView, :root}
120
plug :protect_from_forgery
121
plug :put_secure_browser_headers
122
end
123
124
pipeline :api do
125
plug :accepts, ["json"]
126
end
127
128
# Browser routes
129
scope "/", MyAppWeb do
130
pipe_through :browser
131
132
get "/", PageController, :index
133
resources "/users", UserController
134
resources "/posts", PostController, only: [:index, :show]
135
end
136
137
# API routes
138
scope "/api/v1", MyAppWeb do
139
pipe_through :api
140
141
resources "/users", UserController, except: [:new, :edit]
142
end
143
144
# Admin routes with scoping
145
scope "/admin", MyAppWeb.Admin, as: :admin do
146
pipe_through [:browser, :require_admin]
147
148
resources "/users", UserController
149
resources "/settings", SettingController
150
end
151
end
152
```
153
154
## Controller System
155
156
Controllers handle incoming requests, process parameters, interact with contexts, and render responses.
157
158
### Phoenix.Controller
159
160
```elixir { .api }
161
defmodule Phoenix.Controller do
162
# Request introspection
163
def action_name(Plug.Conn.t()) :: atom
164
def controller_module(Plug.Conn.t()) :: atom
165
def router_module(Plug.Conn.t()) :: atom
166
def endpoint_module(Plug.Conn.t()) :: atom
167
168
# Response rendering
169
def render(Plug.Conn.t(), binary | keyword) :: Plug.Conn.t()
170
def render(Plug.Conn.t(), binary, keyword) :: Plug.Conn.t()
171
def render(Plug.Conn.t(), atom, binary, keyword) :: Plug.Conn.t()
172
173
# Format-specific responses
174
def json(Plug.Conn.t(), term) :: Plug.Conn.t()
175
def text(Plug.Conn.t(), iodata) :: Plug.Conn.t()
176
def html(Plug.Conn.t(), iodata) :: Plug.Conn.t()
177
def redirect(Plug.Conn.t(), keyword) :: Plug.Conn.t()
178
179
# View and layout management
180
def put_view(Plug.Conn.t(), atom | keyword) :: Plug.Conn.t()
181
def put_new_view(Plug.Conn.t(), atom | keyword) :: Plug.Conn.t()
182
def view_module(Plug.Conn.t(), atom | nil) :: atom | nil
183
def put_layout(Plug.Conn.t(), binary | false | {atom, binary}) :: Plug.Conn.t()
184
def put_new_layout(Plug.Conn.t(), binary | false | {atom, binary}) :: Plug.Conn.t()
185
def put_root_layout(Plug.Conn.t(), binary | false | {atom, binary}) :: Plug.Conn.t()
186
def layout(Plug.Conn.t(), atom | nil) :: binary | false | {atom, binary} | nil
187
def root_layout(Plug.Conn.t(), atom | nil) :: binary | false | {atom, binary} | nil
188
189
# URL helpers
190
def put_router_url(Plug.Conn.t(), binary) :: Plug.Conn.t()
191
def put_static_url(Plug.Conn.t(), binary) :: Plug.Conn.t()
192
def current_path(Plug.Conn.t()) :: binary
193
def current_path(Plug.Conn.t(), map | keyword) :: binary
194
def current_url(Plug.Conn.t()) :: binary
195
196
# Content negotiation
197
def accepts(Plug.Conn.t(), [binary]) :: Plug.Conn.t()
198
def put_format(Plug.Conn.t(), atom) :: Plug.Conn.t()
199
def get_format(Plug.Conn.t()) :: atom
200
201
# Security
202
def protect_from_forgery(Plug.Conn.t(), keyword) :: Plug.Conn.t()
203
def put_secure_browser_headers(Plug.Conn.t(), map) :: Plug.Conn.t()
204
def scrub_params(Plug.Conn.t(), binary) :: Plug.Conn.t()
205
206
# Flash messages
207
def fetch_flash(Plug.Conn.t(), keyword) :: Plug.Conn.t()
208
def put_flash(Plug.Conn.t(), atom | binary, term) :: Plug.Conn.t()
209
def get_flash(Plug.Conn.t()) :: map
210
def get_flash(Plug.Conn.t(), atom | binary) :: term
211
def clear_flash(Plug.Conn.t()) :: Plug.Conn.t()
212
def merge_flash(Plug.Conn.t(), Enumerable.t()) :: Plug.Conn.t()
213
214
# File downloads
215
def send_download(Plug.Conn.t(), atom, keyword) :: Plug.Conn.t()
216
217
# JSONP support
218
def allow_jsonp(Plug.Conn.t(), keyword) :: Plug.Conn.t()
219
end
220
```
221
222
### Usage Examples
223
224
```elixir
225
defmodule MyAppWeb.UserController do
226
use Phoenix.Controller, formats: [:html, :json]
227
228
def index(conn, _params) do
229
users = MyApp.Accounts.list_users()
230
render(conn, "index.html", users: users)
231
end
232
233
def show(conn, %{"id" => id}) do
234
case MyApp.Accounts.get_user(id) do
235
nil ->
236
conn
237
|> put_flash(:error, "User not found")
238
|> redirect(to: "/users")
239
240
user ->
241
case get_format(conn) do
242
"html" -> render(conn, "show.html", user: user)
243
"json" -> json(conn, %{user: user})
244
end
245
end
246
end
247
248
def create(conn, %{"user" => user_params}) do
249
case MyApp.Accounts.create_user(user_params) do
250
{:ok, user} ->
251
conn
252
|> put_flash(:info, "User created successfully")
253
|> redirect(to: "/users/#{user.id}")
254
255
{:error, changeset} ->
256
render(conn, "new.html", changeset: changeset)
257
end
258
end
259
260
def delete(conn, %{"id" => id}) do
261
user = MyApp.Accounts.get_user!(id)
262
{:ok, _user} = MyApp.Accounts.delete_user(user)
263
264
conn
265
|> put_flash(:info, "User deleted successfully")
266
|> redirect(to: "/users")
267
end
268
end
269
```
270
271
## Verified Routes
272
273
Phoenix provides compile-time route verification using the `~p` sigil.
274
275
### Phoenix.VerifiedRoutes
276
277
```elixir { .api }
278
defmodule Phoenix.VerifiedRoutes do
279
defmacro sigil_p(path, modifiers)
280
281
def path(Plug.Conn.t() | Phoenix.Socket.t(), binary) :: binary
282
def url(Plug.Conn.t() | Phoenix.Socket.t(), binary) :: binary
283
end
284
```
285
286
### Usage Examples
287
288
```elixir
289
# In controllers, views, templates
290
def show(conn, %{"id" => id}) do
291
user = MyApp.Accounts.get_user!(id)
292
redirect(conn, to: ~p"/users/#{user}")
293
end
294
295
# In templates
296
<%= link "View User", to: ~p"/users/#{@user}" %>
297
<%= link "Edit", to: ~p"/users/#{@user}/edit" %>
298
299
# With query parameters
300
~p"/search?#{[q: @query, page: @page]}"
301
302
# Static paths (verified at compile time)
303
~p"/assets/app.css"
304
```
305
306
## Configuration Options
307
308
### Endpoint Configuration
309
310
```elixir
311
# config/config.exs
312
config :my_app, MyAppWeb.Endpoint,
313
url: [host: "localhost"],
314
secret_key_base: "your_secret_key_base",
315
render_errors: [view: MyAppWeb.ErrorView, accepts: ~w(html json), layout: false],
316
pubsub_server: MyApp.PubSub,
317
live_view: [signing_salt: "your_signing_salt"],
318
server: true
319
```
320
321
### Router Configuration
322
323
```elixir
324
# Enable helpers in controllers and views
325
use Phoenix.Router, helpers: false # Disable route helpers
326
327
# Custom pipeline plugs
328
pipeline :auth do
329
plug MyAppWeb.Plugs.RequireAuth
330
plug MyAppWeb.Plugs.LoadCurrentUser
331
end
332
```
333
334
## Error Handling
335
336
```elixir
337
# Custom errors
338
defmodule Phoenix.Router.NoRouteError do
339
@moduledoc "Exception raised when no route is found"
340
defexception [:conn, :router]
341
end
342
343
defmodule Phoenix.MissingRequiredKeys do
344
@moduledoc "Exception raised when required configuration is missing"
345
defexception [:keys]
346
end
347
348
# In controllers
349
def show(conn, %{"id" => id}) do
350
case MyApp.Accounts.get_user(id) do
351
nil ->
352
raise Phoenix.Router.NoRouteError, conn: conn, router: __MODULE__
353
user ->
354
render(conn, "show.html", user: user)
355
end
356
end
357
```