Phoenix 1.3
Elixir 1.5.2 compiled with Erlang 20
I have an Android mobile app which sends and receives MsgPack with a HTTP server. Now we are planning to integrate Phoenix framework for some parts. But sending binaries specifically MsgPack is troublesome on Phoenix framework.
How to use custom (de-)serializers like MsgPack in Phoenix?
If it is of any relevance:
1. Using MsgPax library
2. Checked this link on stoiximan.gr
I've used MsgPax to do this, and it works just fine. First add it to your mix.exs file.
To use a custom serializer:
Phoenix v1.4+
configure serlirizer in your Phoenix.Endpoint
socket "/socket", YourApp.UserSocket,
websocket: [
connect_info: [:peer_data, :x_headers, :uri, session: @session_options],
serializer: [{YourApp.MsgpaxSerializer, "2.0.0"}]
]
Phoenix v1.3 and older
use Phoenix.Socket.transport
macro in your user_socket.ex file, like so:
defmodule YourApp.UserSocket do
use Phoenix.Socket
# Your channels here
# Take out whichever version you're not using
## Transports for Phoenix 1.2
transport :websocket, Phoenix.Transports.WebSocket, timeout: 45_000,
serializer: YourApp.MsgpaxSerializer
## Transports for Phoenix 1.3
transport :websocket, Phoenix.Transports.WebSocket, timeout: 45_000,
serializer: [{YourApp.MsgpaxSerializer, "~> 2.0.0"}]
# Other user socket stuff
end
Then, as according to the docs, your serializer has to have 3 functions: decode!/2
, encode!/1
, and fastlane!/1
. So, copying mostly from phoenix's own implementation, your serializer might look something like this:
defmodule YourApp.MsgpaxSerializer do
@behaviour Phoenix.Transports.Serializer
alias Phoenix.Socket.Reply
alias Phoenix.Socket.Message
alias Phoenix.Socket.Broadcast
def fastlane!(%Broadcast{} = msg) do
msg = %Message{topic: msg.topic, event: msg.event, payload: msg.payload}
{:socket_push, :binary, encode_to_binary(msg)}
end
def encode!(%Reply{} = reply) do
msg = %Message{
topic: reply.topic,
event: "phx_reply",
ref: reply.ref,
payload: %{status: reply.status, response: reply.payload}
}
{:socket_push, :binary, encode_to_binary(msg)}
end
def encode!(%Message{} = msg) do
{:socket_push, :binary, encode_to_binary(msg)}
end
defp encode_to_binary(msg) do
msg |> Map.from_struct() |> Msgpax.pack!()
end
def decode!(message, _opts) do
message
|> Msgpax.unpack!()
|> Phoenix.Socket.Message.from_map!()
end
end
Finally, if you are encoding structs, you may have to add @derive Msgpax.Packer
attribute to the modules containing the structs as detailed in Msgpax's documentation.