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.
- Hanami::Action::Request is aimed to keep compatibility to the
Rack::Request
- 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
@jodosha WDYT?
@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