haskell-servant / servant

Servat is a Haskell DSL for describing, serving, querying, mocking, documenting web applications and more!
https://docs.servant.dev/
1.83k stars 414 forks source link

Dynamic Content Type #501

Open erkez opened 8 years ago

erkez commented 8 years ago

The library is great! But there is one thing that I've been trying to figure out how to do: setting a content type on servant client depending on the input. How do I let the user define the content type? When I create the client routes, the content type is always defined next to ReqBody. Is there any way to do that?

A use case is file upload. For example, an avatar could have many different mime type values.

fisx commented 8 years ago

if you have different things, is perhaps using an ADT with different constructors for different cases an option?

data Thing = Number Int | Boolean Bool

different mime types can be generated like this:

type Api = "number" :> Get '[JSON, PlainText, HTML, CSV] Thing

follow the type errors in the instructions in the tutorial to figure out how to implement all the required encodings.

does that help?

erkez commented 8 years ago

Thanks, but I think that is not really what I wanted. Let's say that an API accepts any image content type and you must provide it together with the image data in the body. The content type must be provided by the user when calling a Servant client generated function. Maybe I missed. Is there a way to do it at the moment?

fisx commented 8 years ago

when i have this situation, i make the server offer all content types in the way i described, and let the client pick one via the accept http header.

i've not used servant-client much, perhaps this is a missing feature. i guess one way this could be done is to translate end-points that respond with more than one content type into functions that have a content type selector argument.

jkarni commented 8 years ago

@erkez there's no way of doing that - servant-client currently only uses the first content-type in the list. Changing this wouldn't be too hard (essentially just adding an argument here) but it's not obvious to me how we can make choosing a content-type optional...

taladar commented 7 years ago

On the server side offering all content types is not always an option either. Take that image upload example and think a step further, downloading the image again. You have an image of one content type only, so you can not offer all, just one of several, which one is not known at compile time or to the client (so using different routes is not really an option either). It would be very useful if you could just manually (in the handler) decide which ones to offer instead of hard-coding the Accept header as the only source of this decision.

alpmestan commented 7 years ago

@taladar That would be nice. Any idea of how that would look and/or work is welcome, same for patches :)

taladar commented 7 years ago

Well, one option would be to have a special return value wrapper that specifies a Content-Type along with the body of the response, overriding the Accept header selection. Another would be to completely remove the Accept-header hard-coding and pass a function handling content type selection to serve/serveWithContext and provide a default implementation that uses the Accept header.

mbg commented 5 years ago

I have run into this issue as well, but it looks like @dzhus has fixed this (based on the issue reference above). Is there a reason why this doesn't seem to have been merged / there doesn't appear to be a PR for it?

dzhus commented 5 years ago

Is there a reason why this doesn't seem to have been merged / there doesn't appear to be a PR for it?

I'd guess it's because no one has made effort to.

alpmestan commented 5 years ago

Indeed! :-)

mbg commented 5 years ago

@dzhus I guess my comment was asking whether you'd want to open a PR for your changes? I think it would be a bit weird for someone else to do that.

gqk007 commented 4 years ago

Wow, it is 2020 now, this issue is still here.

I try to override the Content-Type header in Handler:

-- contentType (Proxy :: Proxy IMAGE) = "image/*"
type GetAPI = Capture "id" Int :> Get '[IMAGE] (Headers '[Header "Content-Type" String] ByteString)

get :: Server GetAPI
get id = return $ addHeader "image/jpeg" $ readFile "/www/images/" ++ (show id) ++ ".jpeg"

then I get two Content-Type headers:

Content-Type: image/*
Content-Type: image/jpeg

I think servant server should not response wildcard Content-Type header, the wildcard media type is mainly used by client, the server side should be responsible for telling the client the media type of the content it sends.

It may break the servant api design pattern, but it is very useful in practice and is more logical.

I made a patch for Servant.Server.Internal #1274

dzhus commented 4 years ago

@gqk007 Could you please make a separate report as yours is about server behaviour, not how servant-client populates content-type header.

gqk007 commented 4 years ago

I created a new issue, see #1275.