hanami / controller

Complete, fast and testable actions for Rack and Hanami

Home Page:http://hanamirb.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

REQ/RES Content-Type header clash

Strech opened this issue · comments

Hi guys,
First of all wanna say that project you've built is awesome, lightweight and clear.

Issue

I've found that inside action I can't get easy access to the @_env['CONTENT_TYPE'] like I can for accept. I start to dig and I've found 2 things.

  1. Hanami::Action::Request is aimed to keep compatibility to the Rack::Request
  2. At the same time Hanami::Action::Mime#content_type is presenting computed value of the response content type

n.1 is kind-of abused because of Rack::Request#content_type presenting request content type.

n.2 is confusing because you expect it to be a request content type.

Missing feature

I found it because I was looking for accept-like feature, but for incoming payload. As you know Content-Type header for requests like POST, PUT, PATCH and so on are defining a content type of request payload and it would be nice to have 2-way contracts client-server, like

class UserController::Update
  include Hanami::Action

  accept :json
  content_type :json

  def call(params); end
end

In case of accept we are checking that client accepts defined content-type of the response and with content_type we are checking that client will send to the server a payload of a specific content type.

Contribution

I would like to help the project, but I not sure what we can do in this case. We have Hanami::Action::Mime#format to overwrite the response content type and we have Hanami::Action::Mime#content_type which is more like helper for the response. And if I will change it to request content type it will be a breaking change.

So if someone from the core-team can highlight an approach you will accept, I will try to open PR

@Strech Hi and thanks for writing to us. It's not clear to me if this is a bug report (from the title looks so) or a feature proposal (from the body of this ticket).

Can you please provide a couple of examples of what you expect to see? Thanks.

@jodosha Sorry for a mess, I think it's kind-of design issue + missing feature. Will post example a bit later.

Issue

Rack application

require 'rack'

app = ->(env) do
  request = Rack::Request.new(env)
  [
    200,
    {'Content-Type' => 'text/plain'},
    ["CONTENT_TYPE = #{request.content_type}, ACCEPT = #{request.get_header('HTTP_ACCEPT')}"]
  ]
end

run app

# curl -XPOST -H 'Content-Type: application/json' -H 'Accept: text/plain' 0:9292/
# CONTENT_TYPE = application/json, ACCEPT = text/plain

Hanami application

require 'rack'
require 'hanami-controller'

class App
  include Hanami::Action

  def call(params)
    status 200, "CONTENT_TYPE = #{content_type}, ACCEPT = #{accept}"
  end
end

run App.new

# curl -XPOST -H 'Content-Type: application/json' -H 'Accept: text/plain' 0:9292/
# CONTENT_TYPE = text/plain, ACCEPT = text/plain

As you can see in case of Rack content_type is representing request content type, but in case of Hanami - response content_type.

This means, that Hanami::Action::Request differs here, but it should guarantee compatibility

An HTTP request based on top of Rack::Request. This guarantees backwards compatibility with Rack.

And if you will try to use content_type you can be surprised

Missing feature (plz read issue ⬆️)

class App
  include Hanami::Action

  accept :html       # <--- Guard for a client request `Accept` header
  content_type :json # <--- Guard for a client request `Content-Type` header [MISSING]

  def call(params)
    # ...
  end
end

So Hanami provides a way to have guard about Accept header, but don't for Content-Type header. I think it's crucial in case of APIs (this issue I face with project I'm working now)

And because of the issue above it's not so easy for me to came up with any-kind of PR

@Strech Sorry for the late reply. I agree that the MIME types handling is mixed up both in the DSL and in the instance level methods. Would you like to implement that extra .content_type feature? TY.

@jodosha Yes, would love to implement content_type feature 👍

But I need a bit of your guidance with Hanami::Action::Request#content_type what are we gonna do with it? So far it makes feature complicated because I will have to store content_type guard value somewhere else ... should we break compatibility or make it somehow and deprecate/change the behaviour of existing code later?

@Strech Action#content_type is the computed value, where Request#content_type is the requested Content-Type. I think we can safely restore it (instead of raising an exception).

Implemented by #247