Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

🔌 A Plug to add Content Negotiation to any Phoenix App so you can render HTML or JSON for the same route.

License

NotificationsYou must be signed in to change notification settings

dwyl/content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

content adds Content Negotiationtoany Phoenix Appso you can render HTML and JSON for thesame route.

GitHub Workflow Statuscodecov.iocontributions welcomeHitCount

Why? 🤷

We need toreduceeliminate duplication of effortwhile building our App+API so we can ship featuresmuch faster.
Using this Plug we are able to build our App (Phoenix Web UI)and a REST (JSON) API in thesame codebase withminimal effort.

What? 💭

A Plug that can be added toany Phoenix Appto render bothHTML andJSON in thesame route/controllerso that we save dev time.By ensuring that all Web UIhas a corresponding JSON responsewe guarantee thateveryone hasaccess to their data in the most convenient way.

Returning anHTML view for people using the App in a Web Browserand returningJSON for people requesting thesame endpointfrom a script (or a totally independent front-end)we guarantee that all features of our Web Appare automatically available in the API.

We have built several Apps and APIs in the pastand felt the pain of having to maintaintwo separate codebases.It's fine formega corpwith hundreds/thousandsof developers to maintain aseparate web UIand API applications.We are a small teamthat has to do (a lot) more with fewer resources!

If you are new to content negotiation ingeneralorhow to implement it in Phoenix from scratch,please see:dwyl/phoenix-content-negotiation-tutorial

Who? 👥

This project is "for us by us".We areusing it in our product in production.It serves our needsexactly.As witheverything we do it's Open Sourceso that anyone else can benefit.If it looks useful to you, use it!If you have any ideas/requests for features,please open anissue.

How? 💡

Inless than2 minutes and 3 easy stepsyou will have content negotiation enabledin your Phoenix Appand can get back to building your app!


1. Install ⬇️

Addcontent to your list of dependencies inmix.exs:

defdepsdo[{:content,"~> 1.3.0"}]end

Then runmix deps.get.


2. Add theContent Plug to yourrouter.ex 🔧

Open therouter.ex file in your Phoenix App.Locate thepipeline :browser do section.And replace it:

Before:

pipeline:browserdoplug:accepts,["html"]plug:fetch_sessionplug:fetch_flashplug:protect_from_forgeryplug:put_secure_browser_headersend

After:

pipeline:anydoplug:accepts,["html","json"]plugContent,%{html_plugs:[&fetch_session/2,&fetch_flash/2,&protect_from_forgery/2,&put_secure_browser_headers/2]}end

Don't forget to change the pipeline you just changed inside the scopes.As such, you should change according to the following:

Before:

scope"/",AppWebdopipe_through(:browser)get("/",PageController,:index)end

After:

scope"/",AppWebdopipe_through(:any)get("/",PageController,:index)end

Pass the plugs you want to run forhtmlashtml_plugs (in the order you want to execute them).

Note: the& and/2 additions to the names of plugsare theElixir way of passing functions by reference.
The& means "capture" and the/2 is thearityof the function we are passing.
We wouldobviously prefer if functions were just variableslike they are in some other programming languages,but thisworks.
See:https://dockyard.com/blog/2016/08/05/understand-capture-operator-in-elixir
and:https://culttt.com/2016/05/09/functions-first-class-citizens-elixir

Example:router.ex#L6-L11


3. Use theContent.reply/5 in your Controller 📣

In your controller(s),add the following line to invokeContent.reply/5
which will renderHTML orJSONdepending on theaccept header:

Content.reply(conn,&render/3,"index.html",&json/2,data)

Again, those& and/3 are just to letElixirknow whichrender andjson function to use.

TheContent.reply/5 accepts the following 5 argument:

  1. conn - thePlug.Conn where we get thereq_headers from.
  2. render/3 - thePhoenix.Controller.render/3 function,or your own implementation of a render function thattakesconn,template anddata as it's 3 params.
  3. template - the.html template to be renderedif theaccept header matches"html"; e.g:"index.html"
  4. json/2 - thePhoenix.Controller.json/2 functionthat rendersjson data.Or your own implementation that accepts the two params:conn anddata corresponding to thePlug.Connand thejson data you want to return.
  5. data - the data we want to render asHTML orJSON.

Example:quotes_controller.ex#L13

If you need more control over the rendering ofHTML orJSON,you can always write custom logic such as:

ifContent.get_accept_header(conn)=~"json"dodata=transform_data(q)json(conn,data)elserender(conn,"index.html",data:q)end

4. Wildcard Routing

If you want to allow people to view theJSON representationofany route in your application in a Web Browserwithout having tomanually set the Accept headertoapplication/json, there's a handy function for you:wildcard_redirect/3

To use it, simply create awildcardroute in yourrouter.ex file.e.g:

get"/*wildcard",QuotesController,:redirect

And create the corresponding controller to handle this request:

defredirect(conn,params)doContent.wildcard_redirect(conn,params,AppWeb.Router)end

The 3 arguments forwildcard_redirect/3 are:

  • conn - aPlug.Conn the usual for a Phoenix controller.
  • params - the params for the request, again standard for a Phoenix controller.
  • router - the router module for your Phoenix App e.g:MyApp.Router

For an example of this in action, see:README.md#10-view-json-in-a-web-browser

Error Handling

If a route does not exist in your app you will see an error.To handle this error you can use aTry Catch,e.g:

trydoContent.wildcard_redirect(conn,params,AppWeb.Router)rescue# below this line will only render if redirect fails:UndefinedFunctionError->conn|>Plug.Conn.send_resp(404,"not found")|>Plug.Conn.halt()end

Alternatively, for a more robust approach toError handling, seeaction_fallback/1:https://hexdocs.pm/phoenix/Phoenix.Controller.html#action_fallback/1


If you get stuck at at any point,please reference our tutorial:/dwyl/phoenix-content-negotiation-tutorial


Docs? 📖

Documentation can be found athttps://hexdocs.pm/content.


Love it? WantMore? ⭐

If you areusing this package in your project,please ⭐ the repo on GitHub.
If you have any questions/requests,please open anissue.

About

🔌 A Plug to add Content Negotiation to any Phoenix App so you can render HTML or JSON for the same route.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages


[8]ページ先頭

©2009-2025 Movatter.jp