Manchmal machen Kleinigkeiten den Unterschied, in diesem Fall ganze 9 Zeichen: as: :json.

Bei der Arbeit an einer API, basierend auf Ruby on Rails, zeigte sich dass ein RSpec request spec irgendwo eine falsche Abzweigung nahm. Der allgemeine Test des index Endpunkts funktionierte wie erwartet mit dem HTTP request ähnlich wie

get articles_url, headers: valid_headers, as: :json

Das hatte der Generator beim Erzeugen des scaffold so formuliert. Sobald wir allerdings versuchten, einen zusätzlichen query parameter einzufügen, begannen die Probleme:

get articles_url, params: {filter: 'published'}, headers: valid_headers, as: :json

Dieser request führte zu einem 400 Bad Request. Eine nähere Untersuchung zeigte, dass der Controller den Request ablehnte, weil die erwarteten Parameter für den neuen article nicht übergeben wurden. Moment, Parameter für einen neuen article? Wir reden von index, nicht von create.

Aber ein Blick auf das request-Objekt zeigte: Aus einem GET auf index war irgendwie ein POST auf create geworden. Das bestätigte auch eine zusätzliche Prüfung von controller_name und action_name.

Das Problem schien irgendwo im Routing zu legen. Der dazugehörige Eintrag in config/routing.rb war komplett unauffällig, nämlich einfach etwas wie resources :articles. Was war hier also los?

Wie sich am Ende herausstellte: Der vom Generator erzeugte request war nicht ganz korrekt, was dort aber noch keinen sichtbaren Effekt hatte. Erst die Erweiterung um den filter-Parameter brachte das Problem zum Vorschein. Und zwar gehört der Parameter as: :json dort nicht hin.

Dieser Parameter legt (unter anderem) fest, dass die Daten des requests als application/json übergeben werden sollen, nicht als der Standard application/x-www-form-urlencoded. Das ist bei “schreibenden” requests wie post, put oder patch auch genau das was wir haben wollen. Bei “lesenden” requests, insbesondere bei get, kann es jedoch zu den oben beschriebenen Effekten führen.

Die beiden angesprochenen requests mussten also richtig lauten:

get articles_url, headers: valid_headers
get articles_url, params: {filter: 'published'}, headers: valid_headers

Das war mal wieder ein Fall der Kategorie “wenn man’s hinterher weiß ist es logisch”…