params.expect, wo früher require und permit erzeugt wurden. Die neue Form ist kürzer. Außerdem behebt sie ein Reihenfolgeproblem. Dieses Problem löst in Rails-Anwendungen schon so lange vermeidbare Exceptions aus, wie es Strong Parameters gibt.
Was dieses Tutorial behandelt
- Die Exception, die
requiregefolgt vonpermitbei manipulierten Eingaben auslöst. - Warum das Umkehren der Reihenfolge das Problem behebt und warum
expectbesser ist, als das selbst zu tun. - Wie Sie einen Parameter als Array deklarieren, damit er nicht als Hash ankommen kann.
Voraussetzungen
Das Problem mit require gefolgt von permit
Die vertraute Form lautet:require(:post) gibt zurück, was auch immer unter diesem Schlüssel steht, und stellt nur sicher, dass er vorhanden ist. Anschließend wird permit auf das Ergebnis angewendet.
Das ist unproblematisch, solange post ein Hash ist. Es wird zum Problem, wenn jemand ?post=hello sendet. require gibt bereitwillig den String "hello" zurück, permit wird auf einen String angewendet, und die Anfrage schlägt fehl mit:
NoMethodError ist das falsche Ergebnis. Die Parameter waren fehlerhaft, was ein Client-Fehler ist, und Strong Parameters existieren genau dafür, fehlerhafte Eingaben abzulehnen. Stattdessen ist der Code, der die Eingabe bereinigen soll, derjenige, der abstürzt. Bots, die eine Anwendung abtasten, finden das schnell, weshalb solche Fehler im Error-Tracking in großer Zahl auftauchen.
Warum das Umkehren der Reihenfolge das Problem behebt
Rufen Sie zuerstpermit auf, dann wird die Form geprüft, bevor überhaupt etwas extrahiert wird:
permit post gegen eine deklarierte Form: „ein Hash, der title und body enthalten darf“. Ein String passt nicht dazu, wird also verworfen statt weitergegeben. require löst dann ActionController::ParameterMissing aus, was Rails bereits als 400 behandelt.
Was expect macht
params.expect hat diese Reihenfolge fest eingebaut:
ParameterMissing statt NoMethodError aus, ohne dass Sie sich merken müssen, in welcher Reihenfolge Sie zwei Methoden schreiben.
Arrays deklarieren
Das zweite Problem, dasexpect löst, lässt sich mit permit überhaupt nicht ausdrücken.
Nehmen wir verschachtelte Kategorien. Mit permit erzeugt das Senden von post[categories][name]=foo einen einzelnen Hash, wo Ihr Code ein Array davon erwartet. Die Parameter werden erlaubt, die Typen sind falsch, und der Fehler zeigt sich erst später, an anderer Stelle.
expect lässt Sie ein Array verlangen, indem Sie ein zweites Klammernpaar verwenden:
Migrieren
expect ist Opt-in, und require und permit gelten nicht als veraltet. Fast jede bestehende Rails-Anwendung verwendet sie, weshalb sie noch lange bestehen bleiben werden.
- Bei Rails 8: Stellen Sie Ihre Controller auf
expectum. Rails hat seine eigenen internen Controller, einschließlich Action Mailbox, in derselben Änderung umgestellt. Die Anpassung ist mechanisch. Ersetzen Sierequireundpermitdurchexpect, und fügen Sie ein zweites Klammernpaar um alles hinzu, was ein Array sein soll. - Noch nicht auf Rails 8: Schreiben Sie
permitvorrequire. So erhalten Sie den Vorteil der Reihenfolge ohne das Upgrade.
Was sich dadurch in Ihrem Error-Tracking ändert
Dies ist eines der wenigen Refactorings mit einer direkt sichtbaren Auswirkung auf Ihre Issue-Liste.NoMethodError: undefined method 'permit' for an instance of String ist Rauschen. Es stammt von wem auch immer Ihre Anwendung abtastet, nicht von Ihren Nutzern. Nach dem ersten Auftreten liefert es keine zusätzliche Information mehr und konkurriert um Aufmerksamkeit mit echten Regressionen. Der Wechsel zu expect verwandelt diese Fehler in ParameterMissing, worauf Rails mit einem 400 antwortet, ohne durch Ihre Anwendung zu propagieren.
Zwei Dinge, die sich zusätzlich zur Umstellung lohnen:
- Beobachten Sie das Issue im Error-Tracking, um zu bestätigen, dass es nach dem Deploy aufhört. Fügen Sie einen Deploy-Marker hinzu, damit der Vorher-Nachher-Vergleich im Graphen sichtbar ist. Die Request-Parameter bei jedem Issue zeigen, was übermittelt wurde. Sie können damit eine Sonde von einem Formular unterscheiden, das Ihre eigene Anwendung sendet.
- Bleibt ein Rest an Volumen übrig, ignorieren Sie den Fehler, statt ihn in Ihrer Liste liegen zu lassen. Ignorieren Sie ihn, weil Sie festgestellt haben, dass es sich um clientseitiges Rauschen handelt, nicht weil er häufig auftritt.
Transkript
Aus dem Video transkribiert und leicht bearbeitet. Die automatischen Untertitel haben mehrere Produkt- und API-Namen falsch verstanden, und diese wurden korrigiert.Transkript lesen
Transkript lesen
0:02 Hey Leute, in dieser Episode geht es um das brandneue
params.expect in Rails 8, auf das Sie vielleicht schon oder noch nicht aktualisiert haben. Sie sehen das in Ihren Scaffolds, wo Sie params.expect für die ID bekommen, und auch params.expect für Ihren Modellnamen wie post mit title und body. Und das behebt gleich mehrere Probleme der alten Vorgehensweise. Schauen wir uns also einige dieser Probleme an. Die alte Art, das zu tun, wäre: require post, permit, title- und body-Attribute, richtig?0:35 Wenn wir also in unsere Rails-Anwendung gehen, hier habe ich die neue Action genommen und gebe einfach die Parameter aus, die dort durchgelassen werden. Gerade sehen wir, dass params.expect einen Fehler auslöst, weil es keinen post gibt. Jetzt verwenden wir params.require. Das wird ebenfalls sagen, dass das ungültig ist, weil es keinen post in den Parametern gibt. Fügen wir also post hinzu, und normalerweise machen wir das so, dass ein Attribut wie title in eckigen Klammern neben post steht, damit es weiß, das innerhalb von post zu platzieren und daraus einen Hash zu machen.1:10 Allerdings haben wir oft böswillige Nutzer oder Bots oder was auch immer, die herumprobieren und Dinge verändern. Und wenn wir sagen post gleich hello, wobei das ein String statt eines Hashes ist, gehen unsere Parameter mit diesem No-Method-Error in die Luft, anstatt diese Parameter tatsächlich zu bereinigen, wie es eigentlich vorgesehen ist. Das ist also ein Problem, bei dem wir params.require post haben. Das gibt uns, lassen Sie uns das mal etwas aufblasen. Das gibt uns den String hello zurück, und offensichtlich ist permit keine Methode auf diesem String.1:42 Es muss sich auf einem Parameter-Objekt befinden. Die Lösung besteht also darin, es tatsächlich umzudrehen. Das können wir tun, indem wir params.permit post mit title und body sagen. Und dann sagen wir ihm auch, dieses require von post durchzuführen. Um das auszuprobieren, sehen wir, ob dieses require mit der richtigen Fehlermeldung fehlschlägt, und dem Parameter-Missing-Fehler.2:13 Das liegt daran, dass params.permit, wenn es sich das ansieht, post auswertet und feststellt, oh, das muss ein Hash sein, wobei title optional darin enthalten ist. Und das ist ein String und kein Hash. Also sollten wir diesen Parameter nicht erlauben. Da haben wir es, wir bekommen hier ein leeres Element, was es uns dann erlaubt, post zu verlangen. Aber alle unsere Scaffolds und Controller haben es umgekehrt gemacht, seit es Strong Parameters gibt: Sie haben zuerst require gemacht.2:47 Und das ist eines der Probleme, die params.expect für uns löst. Wenn wir das ausführen, bekommen wir automatisch diese Missing-Param-Methode, oder Missing Param post mit dem Parameter-Missing-Fehler, genau wie wir es hier von Haus aus sehen. Aber wir müssen dieses require nicht doppelt angeben. Es übernimmt einfach diesen gesamten Teil für uns, was großartig ist. Das vereinfacht das also ein wenig und macht es ein bisschen sicherer, sodass Leute, die mit Parametern herumbasteln, die richtigen Fehler als ungültige Anfragen zurückbekommen.3:29 Das wird die Dinge also ein bisschen glatter bereinigen. Aber was passiert, wenn wir so etwas wie ein verschachteltes Element haben, zum Beispiel categories, und categories könnte einen name darauf haben? Wenn wir das also machen und post mit, sagen wir, title und post categories als foo angeben, wird das feststellen, dass categories kein Hash war, sondern nur ein String. Es ist also in diesen Parametern nicht erlaubt.4:01 Aber wenn wir categories name foo sagen würden, wird categories auf diese untergeordneten erlaubten Parameter gesetzt. Aber das ist falsch, weil wir stattdessen ein Array wollen, kein einzelnes Element. Und deshalb sind unsere Parameter immer noch etwas, mit dem sich hier ein wenig herumspielen lässt, denn wir brauchen diese leeren eckigen Klammern innen, um Rack und Action Dispatch zu sagen, dass das als Array statt als Objekt wie ein Hash geparst werden muss. Das ist also etwas, das in params.permit nicht wirklich möglich ist, aber hier können wir das tun. Und zwar sagen wir categories und verwenden doppelte eckige Klammern, sodass die erste eckige Klammer sagt, hey, das wird ein Array, und dann folgen die Attribute, die innerhalb dieses Arrays für jedes der Objekte erlaubt sind.4:59 Wenn wir das jetzt neu laden, bekommen wir das Richtige, nämlich eckige Klammern für die Kategorie. Wir bekommen also ein Array. Wenn wir versucht hätten, damit herumzuspielen und categories stattdessen einen Hash zuzuweisen, würde es sagen, dass das nicht erlaubt ist, und sich vollständig darum kümmern. Und Sie werden sehen: Wenn wir die alte Vorgehensweise wieder aktivieren, wird categories immer noch gesetzt, aber auf ein einzelnes Element statt auf ein Array, was nicht das ist, was wir wollen. Das wird hier nicht richtig vertraut und bereinigt. params.expect ist also eine großartige Verbesserung, um das einfach konsistenter und weniger manipulierbar zu machen.5:41 Wir müssen es also richtig machen, um diese Typen zu erhalten. Es muss entweder ein Hash, ein Array oder ein einzelner Wert sein, und wir können ihm das sagen, und es wird das durchsetzen, wie es soll. Ein Punkt, den ich noch ansprechen möchte, bevor wir Schluss machen: params.expect ist Opt-in, Ihre bestehenden Rails-Anwendungscontroller müssen das nicht verwenden. permit und require werden wahrscheinlich noch lange bestehen bleiben, weil derzeit fast alle Rails-Anwendungen sie verwenden, und es wäre eine sehr große Veränderung, alle zu zwingen, auf expect umzusteigen. Und während permit und require weiterhin funktionieren, ist params.expect wirklich die empfohlene Vorgehensweise, um Ihre Rails-Apps ein bisschen sicherer zu machen. Wenn Sie also auf Rails 8 aktualisieren können, aktualisieren Sie Ihre Controller unbedingt auf die neue Syntax, und wenn Sie noch nicht auf Rails 8 aktualisieren können, verwenden Sie permit und dann require in dieser Reihenfolge, damit Sie ein bisschen mehr von diesem Vorteil der Ausführungsreihenfolge beim Erlauben dieser Parameter erhalten.6:46 Wenn also Leute diese böswilligen post gleich name oder foo oder was auch immer machen und einen String statt eines Hashes oder Arrays übermitteln, sind Sie dadurch geschützt, und Sie werden weniger Fehler in Ihrem Error-Monitoring haben, und das wird für diese Situationen automatisch erledigt, was großartig ist. Ich empfehle also sehr, zumindest das zu tun, aber wenn Sie auf Rails 8 kommen können, dann kümmern Sie sich um alle diese Params-Methoden und stellen Sie sicher, dass Sie params.expect überall verwenden, wo Sie können. Wenn Sie einige der internen Details davon sehen möchten: Martin Emde, der an RubyGems und Bundler arbeitet, hat diesen PR im April an Rails gestellt, der schließlich im September gemerged wurde, und es gibt eine Menge großartiger Diskussionen darüber, wie es funktionieren sollte, und das Hin und Her zu den verschiedenen Punkten, denn das ist an dieser Stelle ein ziemlich zentrales Feature von Rails, das geändert wird. Es ist also ein ziemlich großer PR hier, und er aktualisiert die gesamte Action Mailbox und andere interne Controller innerhalb von Rails, damit sie ebenfalls params.expect verwenden, und all das ist eigentlich recht einfach.7:59 Es geht praktisch darum, expect hinzuzufügen, require und permit zu entfernen und dann jedes dieser Arrays mit einem zusätzlichen eckigen Klammernpaar zu umschließen, um das zu handhaben. Aber schauen Sie sich diesen PR an, das ist wirklich eine großartige Diskussion und so, wenn Sie sehen möchten, was bei einem Kernfeature wie diesem vom Rails-Core-Team passiert. Das war’s also, params.expect wird meiner Meinung nach eine großartige Verbesserung sein, die unseren Code einfach ein bisschen besser schützt, und wir werden davon profitieren, weniger dieser Undefined-Method-permit-Probleme zu haben, wann immer jemand dort herumstochert, wo er nicht sollte. Das war’s also für diese Episode, ich hoffe, sie hat Ihnen gefallen, und ich spreche Sie in der nächsten. Peace!Verwandte Tutorials
Über dieses Tutorial
Dieses Tutorial fasst einen GoRails-Screencast von Chris Oliver zuparams.expect in Rails 8 zusammen. Der Screencast ist das Originalwerk. GoRails veröffentlicht ihn, und den Rest der Serie, auf gorails.com.