【Phoenix】请求的生命周期
本文的目的是讨论Phoenix请求的生命周期。我们实战添加两个新的页面,并讨论整个过程是如何串起来的。
让我们从添加第一个新页面开始。
添加一个新页面
web应用通常通过将HTTP方法和路径映射到应用的某个函数来处理请求。Phoenix通过路由器来实现这个匹配。例如将”/articles”映射到显示文章的函数。因此,添加一个页面首先要添加一个新的路由。
新建路由
控制器和动作通过路由器关联它要处理的HTTP方法和路径。在Phoenix中,控制器对应者Elixir的模块,动作是控制器下定义的方法。
动作本质上就是一个处理请求的函数,在Go语言中,称为处理器函数,Phoenix使用了”action”一词来表述它,翻译为动作确实略显生硬,阅读时可以理解为每个请求对应的动作。但对于其本质一定要拿捏准确。
对于新的应用,Phoenix为我们生成了一个路由器文件 lib/hello_web/router.ex
,它也是本章的主角。
在前面例子中欢迎页的路由如下:
get "/", PageController, :home
让我们看看这个路由干了什么。访问 http://localhost:4000 向跟目录发起一个HTTP GET请求。这个请求会被 lib/hello_web/controllers/page_controller.ex
文件定义的 HelloWeb.PageController
中的 home/2
函数处理。
我们会新建一个页面,当访问 http://localhost:4000/hello 时,输出”Hello World, from Phoenix!”。
我们要做的第一件事是添加一个页面路由。打开 lib/hello_web/router.ex
,对于一个全新的应用,内容如下:
defmodule HelloWeb.Router do use HelloWeb, :routerpipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_live_flash plug :put_root_layout, html: {HelloWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headersendpipeline :api do plug :accepts, ["json"]endscope "/", HelloWeb do pipe_through :browserget "/", PageController, :home end# Other scopes may use custom stacks. # scope "/api", HelloWeb do # pipe_through :api # end# ...
end
暂时忽略 pipeline
和 scope
,在稍后的教程中再讨论它们。
让我们在 scope "/" do
下添加一个路由,将 GET /hello
请求映射到 HelloWeb.HelloController
的 index
方法。
scope "/", HelloWeb do pipe_through :browserget "/", PageController, :home get "/hello", HelloController, :index
end
新建controller
控制器是Elixir模块,动作是模块下的Elixir函数。动作的目的是收集数据并执行渲染。我们需要一个 HelloWeb.HelloController
模块以及一个 index/2
函数。那么动手创建一个 lib/hello_web/controllers/hello_controller.ex
文件,并输入下面的代码:
defmodule HelloWeb.HelloController do use HelloWeb, :controllerdef index(conn, _params) do render(conn, :index)end
end
use HelloWeb
和 :controller
再后面的教程中再详细讨论,先将注意力集中到 index
函数。
一个控制器动作有两个参数,第一个是 conn
,它是一个存储着大量请求数据的结构体;第二个是 params
,它是请求参数。这里为了避免编译器警告,我们在 params
前面加了一个下划线 _
。
函数的核心是 render(conn, :index)
,它告诉Phoenix要渲染 index
模板。表示渲染的模块叫做视图,Phoenix视图默认控制器和视图格式来命名,这里控制器是 HelloController
,视图格式是 HTML
,因此我们需要一个 HelloWeb.HelloHTML
模块并定义个 index/1
函数。
新建视图
Phoenix视图充当的是展示层。例如,我们希望 index
输出的是完整的HTML页面。为了快乐搬砖,我们常常会用模板创建HTML页面。
让我们来创建一个视图,新建 lib/hello_web/controllers/hello_html.ex
文件,输入以下代码:
defmodule HelloWeb.HelloHTML do use HelloWeb, :htmlend
我们可以通过函数或者单独的文件向视图添加模板。
通过函数添加代码如下:
defmodule HelloWeb.HelloHTML do use HelloWeb, :htmldef index(assigns) do ~H"""Hello! """end
end
我们定义了一个接受 assigns
的函数,并使用 ~H
标记添加想要渲染的内容。在 ~H
标记内,我们使用的模板语言叫HEEx,表示”HTML+EEx”。EEx是一个用来嵌入Elixir的库,HTML+EEx是EEx针对HTML的扩展,支持HTML验证,组件,和值的自动转义。后者可使你免受跨站点脚本之类的安全漏洞的影响,而无需额外的工作。
模板文件原理类似。函数方式适用于短小的模板,模板文件适用于有很多标签或当你感觉函数已难以维护时。
让我们试着定义一个模板文件。首先删除 def index(assigns)
函数定义,替换成 embed_templates
声明:
defmodule HelloWeb.HelloHTML do use HelloWeb, :htmlembed_templates "hello_html/*"
end
这里我们告诉 Phoenix.Component
将同级目录 hello_html
下的所有 .heex
模板做为函数定义嵌入我们的模块。
接下来我们需要向 lib/hello_web/controllers/hello_html
目录添加文件。
注意看控制器名称 HelloController
,视图名称 HelloHTML
和模板目录 hello_html
都遵循着相同的命名约定,并且它们在目录树中也在一起。
注意:我们可以任意重命名
hello_html
目录并将它放在lib/hello_web/controllers
子目录下,但是需要更新embed_templates
设置。因此建议保持统一的命名约定以避免歧义。
lib/hello_web
├── controllers
│·····├── hello_controller.ex
│·····├── hello_html.ex
│·····├── hello_html
|·············├── index.html.heex
模板文件名格式为 NAME.FORMAT.TEMPLATING_LANGUAGE
,我们在 lib/hello_web/controllers/hello_html/
目录下创建一个名为 index.html.heex
的文件:
<section> <h2>Hello World, from Phoenix!</h2>
</section>
模板文件会自行编译为模块下的函数,两种方式没有运行时的性能差异。
现在我们有了路由,控制器,视图和模板,我们可以访问 http://localhost:4000/hello 来看看效果了。
这里有些有趣的事情值得我们注意。当我们做这些变更时,不需要停止和重启服务器。没错,Phoenix具有代码热加载!还有即使我们的 index.html.heex
文件只包含一个简单的 section
标签,我们也得到了一个完整的HTML文档。事实上我们的模板是渲染在一个布局中的:首先渲染的是 lib/hello_web/components/layouts/root.html.heex
,然后它会渲染 lib/hello_web/components/layouts/app.html.heex
,最后是我们的内容。如果你打开这些文件看一看,就会在底部发现这样一行代码:
<%= @inner_content %>
它会在HTML被发送到浏览器之前将模板注入到布局中。关于布局我们会在后面的教程中介绍。
从endpoint到视图
我们已经创建了第一个页面,现在可以看看一个请求的生命周期是如何串联起来的了。
所有的HTTP请求都始于应用的endpoint,其实就是 lib/hello_web/endpoint.ex
文件中的 HelloWeb.Endpoint
模块。当我们打开这个文件查看时,就会发现它里面大量调用了 plug
,跟路由挺像的。Plug是一个库,也是组织web应用的说明书。它是Phoenix处理请求的重要部分,有关细节后面的教程中会讲到。
目前,可以说每个plug都定义了一个处理请求的队列。在endpoint中,你会看到大致如下的框架:
defmodule HelloWeb.Endpoint do use Phoenix.Endpoint, otp_app: :helloplug Plug.Static, ... plug Plug.RequestId plug Plug.Telemetry, ... plug Plug.Parsers, ... plug Plug.MethodOverride plug Plug.Head plug Plug.Session, ... plug HelloWeb.Router
end
每个插件都有不同的作用,后面我们会讲到。最后一个插件恰好就是 HelloWeb.Router
模块。它让endpoint将所有请求的后续处理都交给路由器。路由器的主要作用就是将请求映射到处理器。最后处理器告诉视图渲染一个模板。
此时,你可能会想,简单地渲染一个页面怎么需要这么多步骤。但是,当应用变得越来越复杂时,我们会看到每一层都有其特殊的作用:
- endpoint(
Phoenix.Endpoint
) - endpoint包含所有请求的公共和初始路径,用来处理所有请求都要做的事情。 - 路由器(
Phoenix.Router
) - 路由负责将请求分发到控制器,同时也运行我们确定一些功能的范围。比如有些页面需要用户鉴权,有些页面则不需要。 - 控制器(
Phoenix.Controller
) - 控制器的工作是提取请求信息,调用业务领域,并为表示层准备数据。 - 视图 - 视图处理来自控制器的结构化数据,并将其转换为显示给用户的形式。视图通常以它们呈现的内容格式命名。
让我们再添加一个页面,巩固一下最后三个组件是如何协同工作的。
这里我保留了endpoint这个单词,本意为端点、终点,直译不好理解,这里endpoint指的其实就是服务端,或者说是服务所有请求的入口点。
创建新页面
让我们稍微加一点难度,添加一个页面,它会截取URL的一部分并通过控制器传入模板,最后在页面上显示出来。
如前面说的,我们首先要做的是创建一个新的路由。
创建新路由
这里我们复用之前创建的 HelloController
,添加一个新的 show
方法。在之前的路由下添加一行:
scope "/", HelloWeb do pipe_through :browserget "/", PageController, :homeget "/hello", HelloController, :index get "/hello/:messenger", HelloController, :show
end
注意我们在路径中用到了 :messenger
语法,Phoenix会将URL中对应位置的值转成一个变量。例如,我们在浏览器输入 http://localhost:4000/hello/Frank ,messenger的值就是Frank。
新建动作
新路由下的请求会由 HelloWeb.HelloController
的 show
函数处理。我们已经有了一个控制器 lib/hello_web/controllers/hello_controller.ex
,因此我们唯一需要做的就是在控制器下添加一个 show
函数。这一次,我们需要从参数中提取messenger变量,并传递给模板。为此,将下面的函数添加到控制器:
def show(conn, %{"messenger" => messenger}) do render(conn, :show, messenger: messenger)
end
我们给 render
函数传递了第三个参数,一个键值对。其中 :messenger
是键,变量 messenger
是值。
如果我们需要访问除messenger之外的请求参数,可以像下面这样定义 show/2
函数:
def show(conn, %{"messenger" => messenger} = params) do ...
end
要记住, params
的键是字符串,等号不是赋值,而是模式匹配。
新建模板
最后我们需要创建一个新的模板,遵循命名规范,将它放在 lib/hello_web/controllers/hello_html
目录下,命名为 show.html.heex
。唯一与 index.html.heex
不同的是,这次我们需要显示messenger变量。
为此,我们使用特殊的HEEx标签 <%= %>
来求值Elixir表达式。任何出现在标签内的Elixir代码都会被执行,其结果会替换该标签。如果标签内没有等号,代码依然会被执行,但结果不会出现在页面中。
记住我们的模板是用HEEx(HTML+EEx)编写的,HEEx是EEx的超集,因此也继承了 <%= %>
语法。
模板内容如下:
<section><h2>Hello World, from <%= @messenger %>!</h2>
</section>
我们从控制器传入视图的值统称为”assigns”,我们可以通过 assigns.messenger
来访问messenger,但是通过元编程,Phoenix为我们提供了更加干净的 @
语法。
完成。如果我们用浏览器访问 http://localhost:4000/hello/Frank ,应该会看到下面的页面:
相关文章:

【Phoenix】请求的生命周期
本文的目的是讨论Phoenix请求的生命周期。我们实战添加两个新的页面,并讨论整个过程是如何串起来的。 让我们从添加第一个新页面开始。 添加一个新页面 web应用通常通过将HTTP方法和路径映射到应用的某个函数来处理请求。Phoenix通过路由器来实现这个匹配。例如将…...

Ps:利用 AI 技术创建人像皮肤图层蒙版
Photoshop 并没有提供专门选择人像皮肤的工具或命令(色彩范围中的肤色选择非常不精准),但较新版的 Camera Raw 滤镜则提供了基于 AI 技术的选择人物并创建面部和身体皮肤蒙版的功能。 如果能将 Camera Raw 滤镜中创建的 AI 皮肤蒙版转换成 Ps…...

内存泄漏、new、delete
1. 内存泄漏 内存泄漏:指针被销毁,指针指向的空间依旧存在 2. new过程 与内存分配、构造函数有关 1)分配空间:void* mem operator new( sizeof( ) ),内部调用malloc 2)static_cast<目标类型>(mem) …...

php在线审稿系统mysql数据库web结构layUI布局apache计算机软件工程网页wamp
一、源码特点 php在线审稿系统是一套完善的web设计系统mysql数据库 ,对理解php编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。 php在线审稿系统 代码 https://download.csdn.net/download/qq_41221322/885…...

【华为HCIP | 华为数通工程师】ISIS 高频题(1)
个人名片: 🐼作者简介:一名大三在校生,喜欢AI编程🎋 🐻❄️个人主页🥇:落798. 🐼个人WeChat:hmmwx53 🕊️系列专栏:🖼️…...

Netty+SpringBoot 打造一个 TCP 长连接通讯方案
项目背景 最近公司某物联网项目需要使用socket长连接进行消息通讯,捣鼓了一版代码上线,结果BUG不断,本猿寝食难安,于是求助度娘,数日未眠项目终于平稳运行了,本着开源共享的精神,本猿把项目代码…...

2023.11.15 每日一题(AI自生成应用)【C++】【Python】【Java】【Go】 动态路径分析
目录 一、题目 二、解决方法 三、改进 一、题目 背景: 在一个城市中,有数个交通节点,每个节点间有双向道路相连。每条道路具有一个初始权重,代表通行该路段的成本(例如时间、费用等)。随着时间的变化&am…...

【libGDX】初识libGDX
1 前言 libGDX 是一个开源且跨平台的 Java 游戏开发框架,于 2010 年 3 月 11 日推出 0.1 版本,它通过 OpenGL ES 2.0/3.0 渲染图像,支持 Windows、Linux、macOS、Android、iOS、Web 等平台,提供了统一的 API,用户只需要…...

VIVADO+FPGA调试记录
vivadoFPGA调试记录 vitis编译vivado导出的硬件平台,提示xxxx.h file cant find vitis编译vivado导出的硬件平台,提示’xxxx.h file cant find’ 此硬件平台中,包含有AXI接口类型的ip。在vitis编译硬件平台时,经常会报错…...

Android——Gradle插件gradle-wrapper.properties
一、Android Studio版本,Android Gradle插件版本,Gradle版本 Android Studio 通过Android Gradle插件 使用 Gradle来构建代码; Android Studio每次升级后, Android Gradle 插件自动更新,对应的Gradle版本也会变动&…...

iOS应用加固方案解析:ipa加固安全技术全面评测
在移动应用开发领域,iOS应用的安全性一直备受关注。ipaguard作为一款专业的iOS应用加固方案,采用混淆加密技术,旨在保护应用免受破解、逆向和篡改等风险。本文将深入探讨ipaguard的产品功能、安全技术及其在iOS应用加固领域中的核心优势和特色…...

过滤器模式 rust和java的实现
文章目录 过滤器模式实现 过滤器模式实现javarustjavarust rust代码仓库 过滤器模式 过滤器模式(Filter Pattern)或标准模式(Criteria Pattern)是一种设计模式,这种模式允许开发人员使用不同的标准来过滤一组对象&…...

Feature Pyramid Networks for Object Detection(2017.4)
文章目录 Abstract1. Introduction3. Feature Pyramid NetworksBottom-up pathwayTop-down pathway and lateral connections 7. Conclusion FPN Abstract 特征金字塔是识别系统中检测不同尺度物体的基本组成部分。但最近的深度学习对象检测器避免了金字塔表示,部分…...
Python3基础模块 random
Python3基础模块 random import random #作用:生成随机数使用dir(module)查看模块内容 >>> import random >>> dir(random) [BPF, LOG4, NV_MAGICCONST, RECIP_BPF, Random, SG_MAGICCONST, SystemRandom, TWOPI, _BuiltinMethodType, _MethodT…...
ubuntu安装pgsql16
ubuntu安装postgresSQL 官网地址: https://www.postgresql.org/download/ 1.安装 # 添加源 sudo sh -c echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list # 安装数字签名 w…...
数据管理70个名词解析
数据标准化70个名词解析 1、数据 是指任何以电子或者其他方式对信息的记录。在计算机科学技术中,“数据”是客观事物的符号表示,指所有可被输入到计算机中并可被计算机程序处理的符号的总称;在管理科学技术中,“数据”是描述事件或事物的属性…...

线性代数本质系列(二)矩阵乘法与复合线性变换,行列式,三维空间线性变换
本系列文章将从下面不同角度解析线性代数的本质,本文是本系列第二篇 向量究竟是什么? 向量的线性组合,基与线性相关 矩阵与线性相关 矩阵乘法与复合线性变换 三维空间中的线性变换 行列式 逆矩阵,列空间,秩与零空间 克…...
Linux-CentOS重要模块
软件包管理器:CentOS使用Yum(Yellowdog Updater, Modified)作为其包管理器。Yum提供了一种方便的方式来安装、更新和删除软件包,并自动解决依赖关系。 RPM:RPM(RPM Package Manager)是CentOS中…...

posix定时器的使用
POSIX定时器是基于POSIX标准定义的一组函数,用于实现在Linux系统中创建和管理定时器。POSIX定时器提供了一种相对较高的精度,可用于实现毫秒级别的定时功能。 POSIX定时器的主要函数包括: timer_create():用于创建一个定时器对象…...

安科瑞煤矿电力监控系统的研究与应用
摘要:作为一个巨大的能源消耗国家,我国每年对煤炭的市场需求巨大。煤炭作为我国点力气和供暖企业的重要原材料,煤矿的开采过程存在着难以消除的风险,我国的煤炭安全问题长期困扰着相关企业和监督部门,也受到社会的广泛…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...

FFmpeg avformat_open_input函数分析
函数内部的总体流程如下: avformat_open_input 精简后的代码如下: int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...

Matlab实现任意伪彩色图像可视化显示
Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中,如何展示好看的实验结果图像非常重要!!! 1、灰度原始图像 灰度图像每个像素点只有一个数值,代表该点的亮度(或…...