Last post, I got the code generating CSVs but (at least in my case) my browser chooses a lame filename: just "csv"
. I'd much rather it chose something like "registrations.csv"
. (It decided on "csv" because that was the last component of the URL path.
One way to accomplish this is by adding on a new HTTP header called Content-Disposition
.
Here's a change to the API to specify that header:
type CSVAPI = "admin" :> "csv" :> S.Get '[SC.CSV] (S.Headers '[S.Header "Content-Disposition" String] [Registration])
This changes the previous CSVAPI
in one place - the type that servant
will expect back from the handler code. Why? Because in addition to [Registration]
to be serialised, we also need to return a value to go into this content-disposition header. servant
is going to check at compile time that we have supplied headers as required by the API.
The updated handleCSV
still needs to SELECT
the registrations from the database, but needs to wrap those values with a call to addHeader
.
handleCSV :: S.Handler (S.Headers '[S.Header "Content-Disposition" String] [Registration]) handleCSV = do rs <- liftIO $ bracket (PG.connectPostgreSQL "user='postgres'") PG.close $ \conn -> do PG.query conn "SELECT firstname, lastname, dob FROM registration" () return $ S.addHeader "attachment;filename=\"registrations.csv\"" rs
That was a pretty small change, but now http://localhost:8080/admin/csv should result in a CSV file with a better filename. As the filename is specified as part of the return value of the handler, it should be straightforward to (for example) put a date/time stamp in the filename, or something else like that to disambiguate multiple downloads over time.
No comments:
Post a Comment