tide icon indicating copy to clipboard operation
tide copied to clipboard

Ergonomic content negotiation

Open aturon opened this issue 6 years ago • 7 comments

Content negotiation is not (currently!) part of routing in Tide, but we should decide whether we want that to change, and if not, how to make it easy for an endpoint to internally perform content negotiation.

aturon avatar Nov 08 '18 05:11 aturon

@aturon did you mean an ability to route based on requested content-type? I was trying to return JSON or HTML based on it from one endpoint and looks like there is no ability to do that via tide:

      app.at("/courses/:id")
         .get(|request: Request<State>| async move { 
              match request.content_type() {
                  Some(mime::JSON) => Ok(request.state().courses().read(request.param("id").unwrap())),
                 Some(mime::HTML) => ResourceTemplate{}.into(), 
                  _ => Err("Unsupported Content-Type.")
              }
          })

Would be great to have more control over what I can return from one endpoint or for endpoints to be fine grained (not only path-based configuration).

humb1t avatar Oct 23 '20 20:10 humb1t

nancyfx has an interesting way to do content negotiation. https://github.com/NancyFx/Nancy/wiki/Content-Negotiation

Get["/"] = parameters => {
    return Negotiate
        .WithModel(new RatPack {FirstName = "Nancy "})
        .WithMediaRangeModel("text/html", new RatPack {FirstName = "Nancy fancy pants"})
        .WithView("negotiatedview")
        .WithHeader("X-Custom", "SomeValue");
};

prabirshrestha avatar Oct 24 '20 01:10 prabirshrestha

We're recently merged a foundation for content negotation into http-types: https://docs.rs/http-types/2.6.0/http_types/content/index.html -- it's not complete yet, but we should be able to use that to drive API experiments.

yoshuawuyts avatar Oct 26 '20 10:10 yoshuawuyts

Note from triage: there are several different things content-negotation might mean, and we probably want to support all of them.

  • content-negotation for responses: serve up the right media type based on the accept headers passed by the client
  • routing based on content-type / accept
  • best-effort deserialization in an endpoint based on the incoming content-type; e.g. always succeed if json or xml passed

We should survey the field, and come up with implementations. We already have all typed headers for these, so we can try implementations.

yoshuawuyts avatar Nov 12 '20 16:11 yoshuawuyts

What is the current state of this? You can't even do

        match request.content_type() {
            Some(mime::JSON) => {

error: to use a constant of type `tide::http_types::Mime` in a pattern, `tide::http_types::Mime` must be annotated with `#[derive(PartialEq, Eq)]`

shulcsm avatar Dec 22 '20 13:12 shulcsm

It seems the root issue here is that we're not implementing StructuralEq for Mime. This can only be implemented through a derive, not a manual implementation like we have. This is a relatively easy fix, albeit noisy.

yoshuawuyts avatar Dec 24 '20 16:12 yoshuawuyts

Ah, it appears we actually can't implement StructuralEq for Mime even if we forward the inner types. It seems this is wholly incompatible with custom PartialEq implementations like we have in Mime. And we need those in order to even be able to define constants. This means we're unfortunately limited by what the language is able to express right now.

Tracking issue on the compiler for this: https://github.com/rust-lang/rust/issues/31434

yoshuawuyts avatar Dec 24 '20 16:12 yoshuawuyts