Lagom is designed with RPC in mind and abstracts from the transport used to exchange messages between services but it defaults to an HTTP/Json transport. File upload is a feature often requested that requires exploiting the power of one of Lagom's building blocks: the Play Framework.
This recipe demonstrates how to add a side-car Controller
using pure Play code to handle file uploads next to your Lagom Service
's. This recipe is inspired on Play's example application Play Scala File Upload Example that is part of Play's great collection of examples.
You can test this recipe using the provided tests:
sbt test
You can also test this recipe manually using 2 separate terminals.
On one terminal start the service:
sbt runAll
On a separate terminal, use curl
to POST a file (in this example we're posting build.sbt
:
curl -X POST -F "[email protected]" -v http://localhost:9000/api/files
You can also exercise a regular Lagom endpoint that coexists with the file upload controller:
curl -X POST -H "Content-Type: text/plain" -d "hello world" http://localhost:9000/api/echo
The changes required on a Lagom service to handle File upload are:
- Add a new Play controller named
FileUploadController
- Create a new
routes
file and add a new route pointing to our side-carFileUploadController
and a default route pointing to the LagomRouter
- Fall back to Play's routing instead of using Lagom's default
Router
There are few more details on the recipe worth mentioning:
- Service ACLs setup
- Automated Tests
This steps only requires creating a new controller based on Play's Upload File example. The final controller is FileUploadController
-
we then create a file named
routes
in./fileupload-impl/src/main/resources
. You can learn more about that file and it's syntax on the docs. In our case we want theroutes
file to contain only two entries:POST /api/files com.example.play.controllers.FileUploadController.uploadFile() -> / com.lightbend.lagom.scaladsl.server.LagomServiceRouter
- in the
build.sbt
project definiton we have to make a few changes. All changes need to be applied on the project definition of the implementation module. First we enable thePlayScala
plugin that will let us useroutes
-based routing. Then, we're configuring Play to use theInjected routes generator
. Finally, we need to disable thePlayLayoutPlugin
because we're usingsbt
's default project structure instead of Play's project structure. The last step is only required because we want to maintain Lagom's project structure. - next, in
FileUploadApplication
we have tooverride
therouter
and create a new instance of theRoutes
router that thePlayScala
sbt plugin will create for us from thesrc/main/resources/routes
file we created above. This classRoutes
is created on the fly by thePlayScala
plugin considering theInjectedRoutesGenerator
setting inbuild.sbt
. Note how it receives three arguments: an error handler, theFileUploadController
instance and the Lagom Router.
We also set up the Service ACLs manually to add the /api/files
endpoint on the ACLs' list so that the Service Gateway can reverse proxy external requests into the File Upload service.
- In FileUploadService the Lagom Descriptor is built. There we added a dummy endpoint to demonstrate how the
FileUploadController
doesn't interfere with regular Service Implementations. The important detail on thisService.Descriptor
is that aServiceAcl
for/api/files
is added manually to theService.Descriptor
.
The recipe includes a couple of tests in ./fileupload-impl/src/test/scala/
where you an see how the test code doesn't change when the lay Controller side-car is added to the Application
. Note though, how the tests for the file upload can't use the Lagom Client and use a plain PlayWS
client to have complete control over the HTTP request built to upload the file.
This recipe uses default values that will limit the size of the uploaded file and doesn't dive deep into tuning options. Here's some resources in case you want to know more about the features Play provides to handle file upload in either the client or the server sides.
To know more about tuning file upload in Play see:
You may also be interested in the Play-specific example on handling file uploads.
Finally, here's some more Lagom recipes that demonstrate how to mix Lagom and Play features: