http-rs / tide

Fast and friendly HTTP server framework for async Rust
https://docs.rs/tide
Apache License 2.0
5.04k stars 322 forks source link

Ergonomic content negotiation #27

Open aturon opened 5 years ago

aturon commented 5 years ago

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.

humb1t commented 3 years ago

@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).

prabirshrestha commented 3 years ago

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");
};
yoshuawuyts commented 3 years ago

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 commented 3 years ago

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

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

shulcsm commented 3 years ago

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)]`
yoshuawuyts commented 3 years ago

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 commented 3 years ago

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