Skip to content

Commit fd50b1f

Browse files
committed
feat(router/builder): string bodies should support "text/plain" mime
any route who can accept a string as the body should support "text/plain" mime so the request doesn't need to be JSON encoded
1 parent 2661952 commit fd50b1f

5 files changed

Lines changed: 37 additions & 3 deletions

File tree

shard.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: action-controller
2-
version: 7.5.3
2+
version: 7.6.0
33
crystal: ">= 1.9.0"
44

55
dependencies:

spec/open_api_spec.cr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ describe ActionController::OpenAPI do
99
it "generates openapi docs" do
1010
result = ActionController::OpenAPI.generate_open_api_docs("title", "version", description: "desc")
1111
result[:openapi].should eq "3.0.3"
12-
result[:paths].size.should eq 25
12+
result[:paths].size.should eq 26
1313
result[:info][:description].should eq "desc"
1414
end
1515
end

spec/route_builder_spec.cr

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,17 @@ describe AC::Route::Builder do
114114
result.body.should eq %(300.4)
115115
end
116116

117+
it "String bodies should work with text/plain mime" do
118+
result = client.post("/filtering/string_entry", headers: HTTP::Headers{
119+
"Content-Type" => "text/plain",
120+
}, body: "some text")
121+
result.body.should eq %("some text")
122+
123+
expect_raises(JSON::ParseException) do
124+
client.post("/filtering/string_entry", body: "some text")
125+
end
126+
end
127+
117128
it "should work with other charsets" do
118129
body_text = "34.8".encode("UTF-16")
119130
body_text.bytesize.should eq 10

spec/spec_helper.cr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,11 @@ class Filtering < FilterOrdering
165165
float
166166
end
167167

168+
@[AC::Route::POST("/string_entry/", status_code: HTTP::Status::ACCEPTED, body: :string)]
169+
def create_string_entry(string : String) : String
170+
string
171+
end
172+
168173
@[AC::Route::POST("/some_other_entry/", status_code: HTTP::Status::ACCEPTED)]
169174
def create_form_encoded_entry(float : Float64) : Float64
170175
float

src/action-controller/router/builder.cr

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,10 +366,24 @@ module ActionController::Route::Builder
366366
{% end %}
367367
{% else %}
368368
# check we can parse the body if a content type is provided
369+
{% string_body_possible = false %}
369370
{% if body_argument != "%" %}
370371
body_type = request_content_type || {{ DEFAULT_PARSER[0] }}
371372
unless {{@type.name.id}}.can_parse?(body_type)
372-
raise AC::Route::UnsupportedMediaType.new("no parser available for #{body_type}", {{@type.name.id}}.parsable)
373+
# special case for mime type text/plain and String body types
374+
{% for arg, arg_index in method.args %}
375+
{% if body_argument == arg.name.id.stringify %}
376+
{% union_types = arg.restriction.resolve.union_types.reject(&.nilable?) %}
377+
{% if union_types.includes?(String) %}
378+
{% string_body_possible = true %}
379+
if body_type != "text/plain"
380+
raise AC::Route::UnsupportedMediaType.new("no parser available for #{body_type}", {{@type.name.id}}.parsable)
381+
end
382+
{% else %}
383+
raise AC::Route::UnsupportedMediaType.new("no parser available for #{body_type}", {{@type.name.id}}.parsable)
384+
{% end %}
385+
{% end %}
386+
{% end %}
373387
end
374388
{% end %}
375389

@@ -466,6 +480,10 @@ module ActionController::Route::Builder
466480
when {{type}}
467481
{{@type.name.id}}.parse_{{type.gsub(/\W/, "_").id}}({{ arg.restriction }}, body_io, request: @__context__.request)
468482
{% end %}
483+
{% if string_body_possible == true %}
484+
when "text/plain"
485+
body_io.gets_to_end
486+
{% end %}
469487
end
470488
{% if arg.default_value.stringify != "" %}
471489
else

0 commit comments

Comments
 (0)