Phoenix 多子域名應(yīng)用

現(xiàn)代Web應(yīng)用的一個普遍需求是多子域名,每個用戶可以訪問到用戶特定的子域名撇寞,比如說Slack就為每個聊天室創(chuàng)建了一個單獨的子域名忌堂。這篇文章講述了如何在Phoenix應(yīng)用中設(shè)置多個子域名。

我們知道椒功,Phoenix可以創(chuàng)建Umbrella應(yīng)用白嘁,其下放置多個App坑鱼,每個App分配不同的端口,如4000,4001,4002等絮缅,前端再用Nginx做反向代理鲁沥,這樣也可以實現(xiàn)子域名的功能呼股,但是無法實現(xiàn)類似Slack的那種功能,用戶無法方便地設(shè)置自己的子域名画恰。

創(chuàng)建項目

新建Phoenix項目卖怜,名為subdomainer

mix phoenix.new subdomainer

啟動應(yīng)用

mix phoenix.server

修改hosts,增加如下這條阐枣,我們可以在本地通過這些域名訪問本機127.0.0.1马靠,部署至服務(wù)器上可以使用泛域名解析。

127.0.0.1       subdomainer.dev foo.subdomainer.dev bar.subdomainer.dev

我們可以通過以下三個地址訪問應(yīng)用:

  1. http://subdomainer.dev:4000
  2. http://foo.subdomainer.dev:4000
  3. http://bar.subdomainer.dev:4000
http://subdomainer.dev:4000/

目前這些地址都指向了同一個頁面蔼两,我們將修改代碼來使不同子域名訪問的頁面各不相同甩鳄。

判斷子域名是否設(shè)置

我們首先需要配置應(yīng)用的根域名,因為你沒法保證子域名的數(shù)量额划,在這個例子中妙啃,根域名是subdomainer.dev,子域名是foo.subdomainer.dev俊戳。當(dāng)然揖赴,我們也可以使用app.subdomainer.dev作為根域名,foo.app.subdomainer.dev作為子域名抑胎。也就是將我們的多子域名應(yīng)用放在一個二級域名之下燥滑。以區(qū)別我們的主應(yīng)用,如www.subdomainer.dev阿逃。而www和app兩個應(yīng)用可以放在一個umbrella下铭拧。

修改config/config.exs中的config :subdomain, Subdomain.Endpoint代碼塊:

url: [host: "localhost"],

修改為:

url: [host: "subdomainer.dev"],

我們還需要修改endpoint來獲知URL里是否是子域名。
創(chuàng)建lib/subdomainer/plugs/subdomain.ex

defmodule Subdomainer.Plug.Subdomain do
  import Plug.Conn

  @doc false
  def init(default), do: default

  @doc false
  def call(conn, router) do
    case get_subdomain(conn.host) do
      subdomain when byte_size(subdomain) > 0 ->
        conn
        |> router.call(router.init({}))
      _ -> conn
    end
  end

  defp get_subdomain(host) do
    root_host = Subdomainer.Endpoint.config(:url)[:host]
    String.replace(host, ~r/.?#{root_host}/, "")
  end
end

這里我們實現(xiàn)了plug必須的call/2函數(shù)恃锉。這里第二個參數(shù)是如果子域名存在搀菩,我們將使用的module。

String.replace(host, ~r/.?#{root_host}/, "")返回了子域名名稱:

"foo.subdomainer.com" -> "foo"
"foo.app.subdomainer.com" -> "foo.app"

如果subdomain長度大于0破托,即URL里包含subdomain肪跋,那么進(jìn)入router。

lib/subdomainer/endpoint.explug :router, Subdomainer.Router之前增加

plug Subdomainer.Plug.Subdomain, Subdomainer.SubdomainRouter

這里我們指定SubdomainRouter模塊作為子域名的Router土砂。
現(xiàn)在我們運行應(yīng)用會有如下錯誤提示:

undefined function: Subdomainer.SubdomainRouter.init/1 (module Subdomainer.SubdomainRouter is not available)

因為我們還未創(chuàng)建這個router州既。

添加子域名路由

創(chuàng)建web/subdomain_router.ex用于存放子域名的router。

defmodule Subdomainer.SubdomainRouter do
  use Subdomainer.Web, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
  end

  scope "/", Subdomainer do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
  end

  # Other scopes may use custom stacks.
  # scope "/api", Subdomainer do
  #   pipe_through :api
  # end
end

現(xiàn)在就可以運行了瘟芝。我們還想讓subdomain訪問不同的頁面易桃。

  scope "/", Subdomainer.Subdomain do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
  end

這里改變了scope后面的Subdomainer.Subdomain褥琐,所以PageController就會在subdomain文件夾下尋找對應(yīng)文件锌俱。

創(chuàng)建web/controllers/subdomain/page_controller.ex,為子域名創(chuàng)建特定的Controller

defmodule Subdomainer.Subdomain.PageController do
  use Subdomainer.Web, :controller

  def index(conn, _params) do
    text(conn, "Subdomain home page")
  end

end

現(xiàn)在也可以運行敌呈,訪問子域名贸宏,會看到如下錯誤提示:

(exit) an exception was raised: ** (Plug.Conn.AlreadySentError) the response was already sent

這也容易解決造寝,我們只需要避免找到子域名后的plugs的運行即可。

lib/subdomainer/plugs/subdomain.ex添加Plug.Conn.halt/1

  def call(conn, router) do
    case get_subdomain(conn.host) do
      subdomain when byte_size(subdomain) > 0 ->
        conn
        |> router.call(router.init({}))
        |> halt
      _ -> conn
    end
  end

自定義子域名響應(yīng)

最后要做的是根據(jù)子域名響應(yīng)對應(yīng)的內(nèi)容吭练。我們可以把subdomain信息添加到Plug.Conn的private storage中

  def call(conn, opts) do
    case get_subdomain(conn.host) do
      subdomain when byte_size(subdomain) > 0 ->
        conn
        |> put_private(:subdomain, subdomain)
        |> router.call(router.init({}))
        |> halt
      _ -> conn
    end
  end

然后在Subdomainer.Subdomain.PageController中獲取信息:

  def index(conn, _params) do
    text(conn, "Subdomain home page for #{conn.private[:subdomain]}")
  end

全部完成诫龙!現(xiàn)在我們再次訪問 http://subdomainer.dev:4000, http://foo.subdomainer.dev:4000 http://bar.subdomainer.dev:4000
來看看效果吧!

最終效果

http://foo.subdomainer.dev:4000/
http://bar.subdomainer.dev:4000/

這僅僅是一個開始鲫咽,你可以根據(jù)此來擴展引用签赃。一個常見需求是從數(shù)據(jù)庫中搜索subdomain是否存在,如果不存在分尸,則返回404锦聊。Subdomainer.SubdomainRouter在請求中獲取了subdomain,你可以添加一個plug到pipeline中箩绍,來在controller動作之前檢查subdomain是否存在孔庭。

參考

[1] http://blog.gazler.com/blog/2015/07/18/subdomains-with-phoenix/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市材蛛,隨后出現(xiàn)的幾起案子圆到,更是在濱河造成了極大的恐慌,老刑警劉巖卑吭,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芽淡,死亡現(xiàn)場離奇詭異,居然都是意外死亡豆赏,警方通過查閱死者的電腦和手機吐绵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來河绽,“玉大人己单,你說我怎么就攤上這事“沂危” “怎么了纹笼?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長苟跪。 經(jīng)常有香客問我廷痘,道長,這世上最難降的妖魔是什么件已? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任笋额,我火速辦了婚禮,結(jié)果婚禮上篷扩,老公的妹妹穿的比我還像新娘兄猩。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布枢冤。 她就那樣靜靜地躺著鸠姨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪淹真。 梳的紋絲不亂的頭發(fā)上讶迁,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機與錄音核蘸,去河邊找鬼巍糯。 笑死,一個胖子當(dāng)著我的面吹牛客扎,可吹牛的內(nèi)容都是我干的鳞贷。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼虐唠,長吁一口氣:“原來是場噩夢啊……” “哼搀愧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起疆偿,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤咱筛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后杆故,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體迅箩,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年处铛,在試婚紗的時候發(fā)現(xiàn)自己被綠了饲趋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡撤蟆,死狀恐怖奕塑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情家肯,我是刑警寧澤龄砰,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站讨衣,受9級特大地震影響换棚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜反镇,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一固蚤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧歹茶,春花似錦夕玩、人聲如沸你弦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鳖目。三九已至扮叨,卻和暖如春缤弦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背彻磁。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工碍沐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人衷蜓。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓累提,卻偏偏與公主長得像,于是被迫代替她去往敵國和親磁浇。 傳聞我的和親對象是個殘疾皇子斋陪,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容