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