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 413 forks source link

Incompatibility between servant-server and Vanilla JS generator on server-side errors #296

Open utdemir opened 8 years ago

utdemir commented 8 years ago

Hi.

When any endpoint on servant-server returns a ServantErr, it just puts the errBody as is. But the JS client generated with servant-js expects a JSON object and tries to decode it; then it fails with a syntax error.

I think either the server should encode the errors conforming with Content-type, or generated JavaScript client must not convert it from JSON and just give the response body to error callback.

Example:

{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators     #-}

module Main where

--------------------------------------------------------------------------------                                                                  
import           Control.Monad.Except
import           Control.Monad.Trans.Class
import           Data.Proxy
import           Data.Text
import           Network.Wai.Handler.Warp
import           Servant
import           Servant.JS
--------------------------------------------------------------------------------                                                              
type API = "stuff" :> Get '[JSON] ()

api :: Proxy API
api = Proxy

server :: Server API
server = throwError $ err500 { errBody="not possible" }

main :: IO ()
main = do
  putStrLn . unpack $ jsForAPI api vanillaJS
  run 2222 $ serve api server

When calling the API with generated JavaScript, instead of calling the error handler, it fails with syntax error.

2015-12-16-144902_635x380_scrot

arianvp commented 8 years ago

Edit: Moved this comment to https://github.com/haskell-servant/servant/issues/294

utdemir commented 8 years ago

@arianvp I guess you wrote this for #294 .

gasi commented 8 years ago

Relating to this, is there a way to return JSON encodable errors using ServantErr? Based on the tutorial, it seems like ServantErr always has a string based errBody which is not how most APIs are built :smile:

arianvp commented 8 years ago

Something like


data ServantErr e = ServantErr { errHTTPCode     :: Int
                               , errReasonPhrase :: String
                               , errBody         ::  e
                               , errHeaders      :: [HTTP.Header]
                               }  deriving (Show, Eq, Read)

Such that e can only be a value that corresponds to values that are accepted by the MimeRender instance of the resource? Yes I think this would be extremely useful.

I don't think it would be too hard to implement. Right?

gasi commented 8 years ago

Yes, that looks great, @arianvp. To clarify, does MimeRender refer to the following?

type UserAPI = "users" :> QueryParam "sortby" SortBy :> Get '[JSON] [User]
                                                            ^^^^^^^^^^^^^^

I’d give it a shot to implement but I am still a beginner Haskell programmer. Happy to help in other ways though: I have quite a bit of experience building REST APIs with other languages, so I can share that and could also help out with docs, etc. Let me know.

jkarni commented 8 years ago

I'm not entirely sure we could have a fully polymorphic e, but if we can't, then:

data ServantErr cts = forall e. AllMimeRender e cts => 
               ServantErr { errHTTPCode     :: Int
                          , errReasonPhrase :: String
                          , errBody         :: e
                          , errHeaders      :: [HTTP.Header]
                          }  deriving (Show, Eq, Read)
gasi commented 8 years ago

REST API error handling:

arianvp commented 8 years ago

@gasi Yes it does. Check out the tutorial, it explains how JSON relates to MimeRender http://haskell-servant.github.io/tutorial/server.html#using-content-types-with-your-data-types

gasi commented 8 years ago

@arianvp 👍 I’ll check it out.