2016-02-25 5 views
6

Я пытаюсь передать часть ByteString обратно клиенту (браузеру). Клиент не будет знать тип содержимого запрашиваемого документа, поэтому я пытаюсь отправить соответствующий ответ типа контента обратно клиенту. Документом может быть изображение или документ в формате pdf или слова и т. Д.Отправка общего типа содержимого в службе

Например, клиент запросит /document?id=55, и сервер ответит соответствующим типом контента и связанными с ним ByteString.

Я следовал примеру here: и я создал что-то для изображения.

data IMAGE 

instance Accept IMAGE where 
    contentType _ = "image" M.// "jpeg" 

instance MimeRender IMAGE LBS.ByteString where 
    mimeRender _ = id 

Задача заключается клиент не будет посылать запрос с каким-то конкретным Accept: заголовком так что нет никакого способа для меня, чтобы реагировать с соответствующим типом пантомимы, как это делается here. Плюс вышесказанное будет работать только для изображений (при условии, что браузеры выведут png, даже отправлю обратно jpeg), но не для pdf, docx и т. Д.

Я думал о параметризованном типе типа MyDynamicContent String, и я буду передавать тип содержимого во время выполнения, но я не уверен, как я объявлю свой API i.e., что я буду использовать вместо '[JSON]. Не уверен, что такая вещь даже возможна, поскольку примеры - просто простой тип данных.

Так что мой вопрос, если я хочу отправить некоторые ByteString в качестве ответа и установите заголовок Content-Type динамически, то, что будет лучший способ сделать это с помощью servant

Update: Я открыл issue

+0

Как сервер решает, какой тип контента должен отвечать? (В частности, чтобы убедиться, что это невозможно определить статически?) – user2141650

+0

@ user2141650: «Сервер» получает эту информацию из хранилища данных (служба хранилища документов). Он выполняет вызов службы, и служба отвечает типом содержимого, а также строкой байта. Я знаю, что я могу создать конечную точку для каждого типа контента (или, в основном, по меньшей мере), и сначала отправить тип контента, а затем попросить клиента использовать конечную точку, соответствующую типу контента. Это такой плохой взломать, переместив большую часть этой логики на клиента, я чувствую, что он должен быть лучше обработан сервером. – Ecognium

ответ

3

Это возможно, но немного хака:

{-# LANGUAGE DataKinds #-} 
{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE OverloadedStrings #-} 
{-# LANGUAGE OverlappingInstances #-} 
module DynCT where 

import Control.Monad.Trans.Either 
import Data.ByteString.Lazy (ByteString) 
import Servant 
import Servant.API.ContentTypes 
import Network.Wai.Handler.Warp 

data WithCT = WithCT { header :: ByteString, content :: ByteString } 

instance AllCTRender xs WithCT where 
    handleAcceptH _ _ (WithCT h c) = Just (h, c) 

type API = Get '[] WithCT 

api :: Proxy API 
api = Proxy 

server :: Server API 
server = return $ WithCT { header = "example", content = "somecontent" } 

main :: IO() 
main = run 8000 $ serve api server 

тестирования:

% curl localhost:8000 -v 
... 
< HTTP/1.1 200 OK 
... 
< Content-Type: example 
< 
... 
somecontent% 

Идея состоит в том, чтобы переопределить нормальное поведение, объявив перекрывающийся экземпляр для AllCTRender. Обратите внимание, что вам, вероятно, также придется выполнить дополнительную работу с ногами для servant-client, servant-docs и т. Д., Если вы также используете их. Учитывая это, вы можете открыть проблему в репо об этом для более полной поддержки.

+0

Большое вам спасибо. Это действительно работает. Я использую 'servant-docs', и я получаю ошибки типа. Вы уже знали, что это будет проблемой. Не уверен, что мне нужно предоставить, чтобы удовлетворить документы для использования '' [] '. Ошибка, которую я получаю, - 'Не удалось вывести (Servant.API.ContentTypes.IsNonEmpty '[]), возникшую из-за использования« Doc.docs ». Я открою проблему и посмотрю, есть ли чистый способ решить проблему с документами. – Ecognium

+0

Я сделал это с документами. Вместо использования '' [] 'Я просто использовал общий пользовательский тип mime. Тип содержимого экземпляра Accept получает избыток от «AllCTRender», поэтому он работает хорошо.Таким образом, я могу использовать документы и возвращать настраиваемый тип mime. – Ecognium