Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client calls OPTIONS verb instead of POST #73

Open
cah6 opened this issue Oct 21, 2018 · 7 comments
Open

Client calls OPTIONS verb instead of POST #73

cah6 opened this issue Oct 21, 2018 · 7 comments

Comments

@cah6
Copy link

cah6 commented Oct 21, 2018

Hello there! So I have a servant CRUD api on a data type called "HappyHour", the servant definition looks like:

hhApi :: Proxy HappyHourApi
hhApi = Proxy

type HappyHourApi =
      -- createHH
      "happy-hours" :> ReqBody '[JSON] HappyHour :> Post '[JSON] UUID
      -- queryHHs
  :<|>"happy-hours" :> Get '[JSON] [HappyHour]

and then I'm trying to generate a client with this library with:

apiClients :: forall t m. (MonadWidget t m) => _
apiClients = client hhApi (Proxy @m) (Proxy @()) (constDyn url)
  where url :: BaseUrl
        url = BaseFullUrl Http "localhost" 3000 "/"

createHH :: MonadWidget t m 
  => Dynamic t (Either T.Text HappyHour) 
  -> Event t () 
  -> m (Event t (ReqResult () UUID))
queryHH :: MonadWidget t m 
  => Event t () 
  -> m (Event t (ReqResult () [HappyHour]))
(createHH :<|> updateHH :<|> deleteHH :<|> getHH :<|> queryHH) = apiClients

While the queryHH call works just fine, strangely enough when my reflex code calls createHH, the network call it's making is OPTIONS http://localhost:3000/happy-hours instead of POST ..., seen by inspecting network calls on Chrome. I know my server is fine, because I can make clients that work correctly with servant-client/servant-client-ghcjs.

Any ideas what's could be going on here? Another thing is that in the network tab it does say the generic CORS exception Failed to load http://localhost:3000/happy-hours: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access. after the OPTIONS request, but I think that's just because my server doesn't give anything useful back on an OPTIONS request, as that doesn't happen on the queryHH call or POST call if it was made with servant-client.

@imalsogreg
Copy link
Owner

That's a new one for me!

One thing I notice is that your API type has two endpoints (one POST, one GET), but your clientside function is building functions for 5 endpoints. I would expect a type error there. Could the versions of frontend and server be out of sync?

Still, the appearance of an OPTIONS request is very weird - I'd like to try to reproduce that if you have a shareable repository.

@cah6
Copy link
Author

cah6 commented Oct 22, 2018

Heh, little github issues last night!

So my project where I saw this issue has 5 endpoints, I just removed the other 3 when I pasted my code here since it wasn't necessary to the description...but forgot to remove them from all places.

In any case, it turns out this happens with servant-client-ghcjs as well -- but NOT servant-client. That is, my POST requests fire fine with GHC but not with GHCJS. So there must be something wrong with my API definition, or servant+GHCJS. Feel free to close this or whatever you want, though I'll try to comment back here if I figure this out.

@3noch
Copy link
Collaborator

3noch commented Oct 22, 2018

It sounds like this is just your browser doing a preflight check on your API endpoint. If you have not added a specific handler for OPTION requests, then of course it will fail with a method-not-allowed and your request will fail. The reason it's doing a preflight in the first place must be because your request is cross-origin. You can add a servant handler for OPTION or you can use https://hackage.haskell.org/package/wai-cors to add CORS policies to your entire API.

@cah6
Copy link
Author

cah6 commented Oct 22, 2018

Yeah, but the thing is I already ran into this and (I thought) fixed it. I.e. on my servant server I have

import Network.Wai.Middleware.Cors (simpleCors)
...
mkApp = simpleCors $ serve hhApi serverDefinition

which should allow access on all methods. Do I need to make some sort of change on the client side as well? Also if it was just that, I wouldn't expect POST to fail but GET to pass?

@3noch
Copy link
Collaborator

3noch commented Oct 22, 2018

Does https://hackage.haskell.org/package/wai-cors-0.2.6/docs/Network-Wai-Middleware-Cors.html#v:simpleCorsResourcePolicy fully meet your needs? Note that POST has extra constraints for content type.

@cah6
Copy link
Author

cah6 commented Oct 22, 2018

Ahh I understand now: it only sends a preflight request if the original request doesn't fall into the "simple" category, and my POST doesn't because it's sending JSON. So I'll have to either customize my CorsResourcePolicy, use something like http://hackage.haskell.org/package/servant-options to provide the OPTIONS response, or some mixture of the two.

Thanks for the help, I can probably figure it out from here!

@3noch
Copy link
Collaborator

3noch commented Oct 22, 2018

No problem. I've spent too long debugging issues exactly like this one myself. You should be able to just add your content type to the resource policy in that case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants