-
Notifications
You must be signed in to change notification settings - Fork 0
/
action_controller_overview.html
1605 lines (1528 loc) · 153 KB
/
action_controller_overview.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!doctype html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Visión General del Controlador de Acción — Ruby on Rails Guides</title>
<link rel="stylesheet" type="text/css" href="stylesheets/style-v2.css" data-turbo-track="reload">
<link rel="stylesheet" type="text/css" href="stylesheets/print-v2.css" media="print">
<link rel="stylesheet" type="text/css" href="stylesheets/highlight-v2.css" data-turbo-track="reload">
<link rel="icon" href="images/favicon.ico" sizes="any">
<link rel="apple-touch-icon" href="images/icon.png">
<script src="javascripts/@hotwired--turbo.js" data-turbo-track="reload"></script>
<script src="javascripts/clipboard.js" data-turbo-track="reload"></script>
<script src="javascripts/guides.js" data-turbo-track="reload"></script>
<meta property="og:title" content="Visión General del Controlador de Acción — Ruby on Rails Guides" />
<meta name="description" content="NO LEA ESTE ARCHIVO EN GITHUB, LAS GUÍAS ESTÁN PUBLICADAS EN https://guides.rubyonrails.org.Visión General del Controlador de AcciónEn esta guía, aprenderás cómo funcionan los controladores y cómo encajan en el ciclo de solicitudes de tu aplicación.Después de leer esta guía, sabrás cómo: Seguir el flujo de una solicitud a través de un controlador. Restringir los parámetros pasados a tu controlador. Almacenar datos en la sesión o cookies, y por qué. Trabajar con callbacks de acción para ejecutar código durante el procesamiento de solicitudes. Usar la autenticación HTTP incorporada de Action Controller. Transmitir datos directamente al navegador del usuario. Filtrar parámetros sensibles para que no aparezcan en el registro de la aplicación. Manejar excepciones que puedan surgir durante el procesamiento de solicitudes. Usar el punto final de verificación de salud incorporado para equilibradores de carga y monitores de tiempo de actividad." />
<meta property="og:description" content="NO LEA ESTE ARCHIVO EN GITHUB, LAS GUÍAS ESTÁN PUBLICADAS EN https://guides.rubyonrails.org.Visión General del Controlador de AcciónEn esta guía, aprenderás cómo funcionan los controladores y cómo encajan en el ciclo de solicitudes de tu aplicación.Después de leer esta guía, sabrás cómo: Seguir el flujo de una solicitud a través de un controlador. Restringir los parámetros pasados a tu controlador. Almacenar datos en la sesión o cookies, y por qué. Trabajar con callbacks de acción para ejecutar código durante el procesamiento de solicitudes. Usar la autenticación HTTP incorporada de Action Controller. Transmitir datos directamente al navegador del usuario. Filtrar parámetros sensibles para que no aparezcan en el registro de la aplicación. Manejar excepciones que puedan surgir durante el procesamiento de solicitudes. Usar el punto final de verificación de salud incorporado para equilibradores de carga y monitores de tiempo de actividad." />
<meta property="og:locale" content="en_US" />
<meta property="og:site_name" content="Ruby on Rails Guides" />
<meta property="og:image" content="https://avatars.githubusercontent.com/u/4223" />
<meta property="og:type" content="website" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Arabic:[email protected]&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Heebo:[email protected]&family=Noto+Sans+Arabic:[email protected]&display=swap" rel="stylesheet">
<meta name="theme-color" content="#C81418">
</head>
<body class="guide">
<nav id="topNav" aria-label="Secondary">
<div class="wrapper">
<strong class="more-info-label">Más en <a href="https://rubyonrails.org/">rubyonrails.org:</a> </strong>
<span class="red-button more-info-button">
Más Ruby on Rails
</span>
<ul class="more-info-links s-hidden">
<li class="more-info"><a href="https://rubyonrails.org/blog">Blog</a></li>
<li class="more-info"><a href="https://guides.rubyonrails.org/">Guías</a></li>
<li class="more-info"><a href="https://api.rubyonrails.org/">API</a></li>
<li class="more-info"><a href="https://discuss.rubyonrails.org/">Foro</a></li>
<li class="more-info"><a href="https://github.com/rails/rails">Contribuir en GitHub</a></li>
</ul>
</div>
</nav>
<header id="page_header">
<div class="wrapper clearfix">
<nav id="feature_nav">
<div class="header-logo">
<a href="index.html" title="Regresar a la página principal de Guías para Edge">Guías</a>
<span id="version_switcher">
Versión:
<select class="guides-version">
<option value="https://edgeguides.rubyonrails.org/" selected>Edge</option>
<option value="https://guides.rubyonrails.org/v7.2/">7.2</option>
<option value="https://guides.rubyonrails.org/v7.1/">7.1</option>
<option value="https://guides.rubyonrails.org/v7.0/">7.0</option>
<option value="https://guides.rubyonrails.org/v6.1/">6.1</option>
<option value="https://guides.rubyonrails.org/v6.0/">6.0</option>
<option value="https://guides.rubyonrails.org/v5.2/">5.2</option>
<option value="https://guides.rubyonrails.org/v5.1/">5.1</option>
<option value="https://guides.rubyonrails.org/v5.0/">5.0</option>
<option value="https://guides.rubyonrails.org/v4.2/">4.2</option>
<option value="https://guides.rubyonrails.org/v4.1/">4.1</option>
<option value="https://guides.rubyonrails.org/v4.0/">4.0</option>
<option value="https://guides.rubyonrails.org/v3.2/">3.2</option>
<option value="https://guides.rubyonrails.org/v3.1/">3.1</option>
<option value="https://guides.rubyonrails.org/v3.0/">3.0</option>
<option value="https://guides.rubyonrails.org/v2.3/">2.3</option>
</select>
</span>
</div>
<ul class="nav">
<li><a class="nav-item" id="home_nav" href="https://rubyonrails.org/">Inicio</a></li>
<li class="guides-index guides-index-large">
<a href="index.html" id="guidesMenu" class="guides-index-item nav-item">Índice de Guías</a>
<div id="guides" class="clearfix" style="display: none;">
<hr />
<dl class="guides-section-container">
<div class="guides-section">
<dt>Comienza Aquí</dt>
<dd><a href="getting_started.html">Primeros Pasos con Rails</a></dd>
</div>
<div class="guides-section">
<dt>Modelos</dt>
<dd><a href="active_record_basics.html">Conceptos Básicos de Active Record</a></dd>
<dd><a href="active_record_migrations.html">Migraciones de Active Record</a></dd>
<dd><a href="active_record_validations.html">Validaciones de Active Record</a></dd>
</div>
<div class="guides-section">
<dt>Vistas</dt>
<dd><a href="action_view_overview.html">Resumen de Action View</a></dd>
<dd><a href="layouts_and_rendering.html">Diseños y Renderizado en Rails</a></dd>
</div>
<div class="guides-section">
<dt>Controladores</dt>
<dd><a href="action_controller_overview.html">Resumen de Action Controller</a></dd>
<dd><a href="routing.html">Enrutamiento en Rails desde el Exterior</a></dd>
</div>
<div class="guides-section">
<dt>Otros Componentes</dt>
<dd><a href="active_support_core_extensions.html">Extensiones Básicas de Active Support</a></dd>
<dd><a href="action_mailer_basics.html">Conceptos Básicos de Action Mailer</a></dd>
<dd><a href="action_mailbox_basics.html">Conceptos Básicos de Action Mailbox</a></dd>
<dd><a href="action_text_overview.html">Resumen de Action Text</a></dd>
<dd><a href="active_job_basics.html">Conceptos Básicos de Active Job</a></dd>
</div>
<div class="guides-section">
<dt>Políticas</dt>
<dd><a href="maintenance_policy.html">Política de Mantenimiento</a></dd>
</div>
<div class="guides-section">
<dt>Notas de Lanzamiento</dt>
<dd><a href="upgrading_ruby_on_rails.html">Actualizando Ruby on Rails</a></dd>
<dd><a href="7_2_release_notes.html">Versión 7.2 - ?</a></dd>
<dd><a href="7_1_release_notes.html">Versión 7.1 - Octubre 2023</a></dd>
<dd><a href="7_0_release_notes.html">Versión 7.0 - Diciembre 2021</a></dd>
<dd><a href="6_1_release_notes.html">Versión 6.1 - Diciembre 2020</a></dd>
</div>
</dl>
</div>
</li>
<li><a class="nav-item" href="contributing_to_ruby_on_rails.html">Contribuir</a></li>
<li class="guides-index guides-index-small">
<select class="guides-index-item nav-item">
<option value="index.html">Índice de Guías</option>
<optgroup label="Comienza Aquí">
<option value="getting_started.html">Primeros Pasos con Rails</option>
</optgroup>
<optgroup label="Modelos">
<option value="active_record_basics.html">Conceptos Básicos de Active Record</option>
<option value="active_record_migrations.html">Migraciones de Active Record</option>
<option value="active_record_validations.html">Validaciones de Active Record</option>
</optgroup>
<optgroup label="Vistas">
<option value="action_view_overview.html">Resumen de Action View</option>
<option value="layouts_and_rendering.html">Diseños y Renderizado en Rails</option>
</optgroup>
<optgroup label="Controladores">
<option value="action_controller_overview.html">Resumen de Action Controller</option>
<option value="routing.html">Enrutamiento en Rails desde el Exterior</option>
</optgroup>
<optgroup label="Otros Componentes">
<option value="active_support_core_extensions.html">Extensiones Básicas de Active Support</option>
<option value="action_mailer_basics.html">Conceptos Básicos de Action Mailer</option>
<option value="action_mailbox_basics.html">Conceptos Básicos de Action Mailbox</option>
<option value="action_text_overview.html">Resumen de Action Text</option>
<option value="active_job_basics.html">Conceptos Básicos de Active Job</option>
</optgroup>
<optgroup label="Políticas">
<option value="maintenance_policy.html">Política de Mantenimiento</option>
</optgroup>
<optgroup label="Notas de Lanzamiento">
<option value="upgrading_ruby_on_rails.html">Actualizando Ruby on Rails</option>
<option value="7_2_release_notes.html">Versión 7.2 - ?</option>
<option value="7_1_release_notes.html">Versión 7.1 - Octubre 2023</option>
<option value="7_0_release_notes.html">Versión 7.0 - Diciembre 2021</option>
<option value="6_1_release_notes.html">Versión 6.1 - Diciembre 2020</option>
</optgroup>
</select>
</li>
</ul>
</nav>
</div>
</header>
<hr class="hide" />
<section id="feature">
<div class="wrapper">
<p><strong>NO LEA ESTE ARCHIVO EN GITHUB, LAS GUÍAS ESTÁN PUBLICADAS EN <a href="https://guides.rubyonrails.org">https://guides.rubyonrails.org</a>.</strong></p><h1>Visión General del Controlador de Acción</h1><p>En esta guía, aprenderás cómo funcionan los controladores y cómo encajan en el ciclo de solicitudes de tu aplicación.</p><p>Después de leer esta guía, sabrás cómo:</p>
<ul>
<li>Seguir el flujo de una solicitud a través de un controlador.</li>
<li>Restringir los parámetros pasados a tu controlador.</li>
<li>Almacenar datos en la sesión o cookies, y por qué.</li>
<li>Trabajar con callbacks de acción para ejecutar código durante el procesamiento de solicitudes.</li>
<li>Usar la autenticación HTTP incorporada de Action Controller.</li>
<li>Transmitir datos directamente al navegador del usuario.</li>
<li>Filtrar parámetros sensibles para que no aparezcan en el registro de la aplicación.</li>
<li>Manejar excepciones que puedan surgir durante el procesamiento de solicitudes.</li>
<li>Usar el punto final de verificación de salud incorporado para equilibradores de carga y monitores de tiempo de actividad.</li>
</ul>
<nav id="subCol">
<h3 class="chapter">
<picture>
<!-- Using the `source` HTML tag to set the dark theme image -->
<source
srcset="images/icon_book-close-bookmark-1-wht.svg"
media="(prefers-color-scheme: dark)"
/>
<img src="images/icon_book-close-bookmark-1.svg" alt="Chapter Icon" />
</picture>
Chapters
</h3>
<ol class="chapters">
<li><a href="#¿qué-hace-un-controlador-questionmark">¿Qué Hace un Controlador?</a></li>
<li><a href="#convención-de-nombres-de-controladores">Convención de Nombres de Controladores</a></li>
<li><a href="#métodos-y-acciones">Métodos y Acciones</a></li>
<li><a href="#parámetros">Parámetros</a>
<ul>
<li><a href="#parámetros-hash-y-de-array">Parámetros Hash y de Array</a></li>
<li><a href="#parámetros-json">Parámetros JSON</a></li>
<li><a href="#parámetros-de-enrutamiento">Parámetros de Enrutamiento</a></li>
<li><a href="#parámetros-de-clave-compuesta">Parámetros de Clave Compuesta</a></li>
<li><a href="#default-url-options"><code>default_url_options</code></a></li>
<li><a href="#parámetros-fuertes">Parámetros Fuertes</a></li>
</ul></li>
<li><a href="#sesión">Sesión</a>
<ul>
<li><a href="#accediendo-a-la-sesión">Accediendo a la Sesión</a></li>
<li><a href="#el-flash">El Flash</a></li>
</ul></li>
<li><a href="#cookies">Cookies</a></li>
<li><a href="#renderizado">Renderizado</a></li>
<li><a href="#callbacks-de-acción">Callbacks de Acción</a>
<ul>
<li><a href="#callbacks-de-acción-después-y-alrededor">Callbacks de Acción Después y Alrededor</a></li>
<li><a href="#otras-formas-de-usar-callbacks-de-acción">Otras Formas de Usar Callbacks de Acción</a></li>
</ul></li>
<li><a href="#protección-contra-falsificación-de-solicitudes">Protección Contra Falsificación de Solicitudes</a></li>
<li><a href="#los-objetos-de-solicitud-y-respuesta">Los Objetos de Solicitud y Respuesta</a>
<ul>
<li><a href="#el-objeto-request">El Objeto <code>request</code></a></li>
<li><a href="#el-objeto-response">El Objeto <code>response</code></a></li>
</ul></li>
<li><a href="#autenticaciones-http">Autenticaciones HTTP</a>
<ul>
<li><a href="#autenticación-básica-http">Autenticación Básica HTTP</a></li>
<li><a href="#autenticación-digest-http">Autenticación Digest HTTP</a></li>
<li><a href="#autenticación-de-token-http">Autenticación de Token HTTP</a></li>
</ul></li>
<li><a href="#transmisión-y-descarga-de-archivos">Transmisión y Descarga de Archivos</a>
<ul>
<li><a href="#enviando-archivos">Enviando Archivos</a></li>
<li><a href="#descargas-restful">Descargas RESTful</a></li>
<li><a href="#transmisión-en-vivo-de-datos-arbitrarios">Transmisión en Vivo de Datos Arbitrarios</a></li>
</ul></li>
<li><a href="#filtrado-de-registro">Filtrado de Registro</a>
<ul>
<li><a href="#filtrado-de-parámetros">Filtrado de Parámetros</a></li>
<li><a href="#filtrado-de-redirecciones">Filtrado de Redirecciones</a></li>
</ul></li>
<li><a href="#rescate">Rescate</a>
<ul>
<li><a href="#las-plantillas-predeterminadas-500-y-404">Las Plantillas Predeterminadas 500 y 404</a></li>
<li><a href="#rescue-from"><code>rescue_from</code></a></li>
</ul></li>
<li><a href="#forzar-el-protocolo-https">Forzar el Protocolo HTTPS</a></li>
<li><a href="#punto-final-de-verificación-de-salud-incorporado">Punto Final de Verificación de Salud Incorporado</a></li>
</ol>
</nav>
<hr>
</div>
</section>
<main id="container">
<div class="wrapper">
<div id="mainCol">
<h2 id="¿qué-hace-un-controlador-questionmark"><a class="anchorlink" href="#¿qué-hace-un-controlador-questionmark"><span>1</span> ¿Qué Hace un Controlador?</a></h2><p>Action Controller es la C en <a href="https://es.wikipedia.org/wiki/Modelo%E2%80%93vista%E2%80%93controlador">MVC</a>. Después de que el enrutador ha determinado qué controlador usar para una solicitud, el controlador es responsable de interpretar la solicitud y producir la salida adecuada. Afortunadamente, Action Controller hace la mayor parte del trabajo preliminar por ti y utiliza convenciones inteligentes para hacer esto lo más sencillo posible.</p><p>Para la mayoría de las aplicaciones convencionales <a href="https://es.wikipedia.org/wiki/Transferencia_de_estado_representacional">RESTful</a>, el controlador recibirá la solicitud (esto es invisible para ti como desarrollador), recuperará o guardará datos de un modelo y usará una vista para crear una salida HTML. Si tu controlador necesita hacer las cosas de manera un poco diferente, eso no es un problema, esta es solo la forma más común de que un controlador funcione.</p><p>Por lo tanto, se puede pensar en un controlador como un intermediario entre modelos y vistas. Hace que los datos del modelo estén disponibles para la vista, para que pueda mostrar esos datos al usuario, y guarda o actualiza los datos del usuario en el modelo.</p><p>NOTA: Para más detalles sobre el proceso de enrutamiento, consulta <a href="routing.html">Enrutamiento de Rails desde el Exterior Hacia Adentro</a>.</p><h2 id="convención-de-nombres-de-controladores"><a class="anchorlink" href="#convención-de-nombres-de-controladores"><span>2</span> Convención de Nombres de Controladores</a></h2><p>La convención de nomenclatura de controladores en Rails favorece la pluralización de la última palabra en el nombre del controlador, aunque no es estrictamente necesario (por ejemplo, <code>ApplicationController</code>). Por ejemplo, <code>ClientsController</code> es preferible a <code>ClientController</code>, <code>SiteAdminsController</code> es preferible a <code>SiteAdminController</code> o <code>SitesAdminsController</code>, y así sucesivamente.</p><p>Seguir esta convención te permitirá usar los generadores de rutas predeterminados (por ejemplo, <code>resources</code>, etc.) sin necesidad de calificar cada <code>:path</code> o <code>:controller</code>, y mantendrá el uso de los ayudantes de rutas nombradas consistente en toda tu aplicación. Consulta <a href="layouts_and_rendering.html">Guía de Layouts y Renderizado</a> para más detalles.</p><p>NOTA: La convención de nombres de controladores difiere de la convención de nombres de modelos, que se espera que estén nombrados en forma singular.</p><h2 id="métodos-y-acciones"><a class="anchorlink" href="#métodos-y-acciones"><span>3</span> Métodos y Acciones</a></h2><p>Un controlador es una clase Ruby que hereda de <code>ApplicationController</code> y tiene métodos como cualquier otra clase. Cuando tu aplicación recibe una solicitud, el enrutamiento determinará qué controlador y acción ejecutar, luego Rails crea una instancia de ese controlador y ejecuta el método con el mismo nombre que la acción.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">ClientsController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
<span class="k">def</span> <span class="nf">new</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class ClientsController < ApplicationController
def new
end
end
">Copy</button>
</div>
<p>Por ejemplo, si un usuario va a <code>/clients/new</code> en tu aplicación para agregar un nuevo cliente, Rails creará una instancia de <code>ClientsController</code> y llamará a su método <code>new</code>. Ten en cuenta que el método vacío del ejemplo anterior funcionaría bien porque Rails, por defecto, renderizará la vista <code>new.html.erb</code> a menos que la acción indique lo contrario. Al crear un nuevo <code>Client</code>, el método <code>new</code> puede hacer que una variable de instancia <code>@client</code> sea accesible en la vista:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">def</span> <span class="nf">new</span>
<span class="vi">@client</span> <span class="o">=</span> <span class="no">Client</span><span class="p">.</span><span class="nf">new</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="def new
@client = Client.new
end
">Copy</button>
</div>
<p>La <a href="layouts_and_rendering.html">Guía de Layouts y Renderizado</a> explica esto con más detalle.</p><p><code>ApplicationController</code> hereda de <a href="https://edgeapi.rubyonrails.org/classes/ActionController/Base.html"><code>ActionController::Base</code></a>, que define una serie de métodos útiles. Esta guía cubrirá algunos de estos, pero si tienes curiosidad por ver qué hay allí, puedes ver todos ellos en la <a href="https://edgeapi.rubyonrails.org/classes/ActionController.html">documentación de la API</a> o en el propio código fuente.</p><p>Solo los métodos públicos son invocables como acciones. Es una buena práctica reducir la visibilidad de los métodos (con <code>private</code> o <code>protected</code>) que no están destinados a ser acciones, como métodos auxiliares o filtros.</p><p>ADVERTENCIA: Algunos nombres de métodos están reservados por Action Controller. Redefinirlos accidentalmente como acciones, o incluso como métodos auxiliares, podría resultar en un <code>SystemStackError</code>. Si limitas tus controladores solo a acciones de <a href="routing.html#resource-routing-the-rails-default">Enrutamiento de Recursos</a> RESTful, no deberías preocuparte por esto.</p><p>NOTA: Si debes usar un método reservado como nombre de acción, una solución es usar una ruta personalizada para mapear el nombre del método reservado a tu método de acción no reservado.</p><h2 id="parámetros"><a class="anchorlink" href="#parámetros"><span>4</span> Parámetros</a></h2><p>Probablemente querrás acceder a los datos enviados por el usuario u otros parámetros en tus acciones de controlador. Hay dos tipos de parámetros posibles en una aplicación web. Los primeros son parámetros que se envían como parte de la URL, llamados parámetros de cadena de consulta. La cadena de consulta es todo lo que está después de "?" en la URL. El segundo tipo de parámetro se refiere generalmente como datos POST. Esta información generalmente proviene de un formulario HTML que ha sido completado por el usuario. Se llama datos POST porque solo se puede enviar como parte de una solicitud HTTP POST. Rails no hace distinción entre los parámetros de cadena de consulta y los parámetros POST, y ambos están disponibles en el hash <a href="https://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html#method-i-params"><code>params</code></a> en tu controlador:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">ClientsController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
<span class="c1"># Esta acción usa parámetros de cadena de consulta porque se ejecuta</span>
<span class="c1"># mediante una solicitud HTTP GET, pero esto no hace ninguna diferencia</span>
<span class="c1"># en cómo se acceden los parámetros. La URL para</span>
<span class="c1"># esta acción se vería así para listar clientes activados:</span>
<span class="c1"># /clients?status=activated</span>
<span class="k">def</span> <span class="nf">index</span>
<span class="k">if</span> <span class="n">params</span><span class="p">[</span><span class="ss">:status</span><span class="p">]</span> <span class="o">==</span> <span class="s2">"activated"</span>
<span class="vi">@clients</span> <span class="o">=</span> <span class="no">Client</span><span class="p">.</span><span class="nf">activated</span>
<span class="k">else</span>
<span class="vi">@clients</span> <span class="o">=</span> <span class="no">Client</span><span class="p">.</span><span class="nf">inactivated</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># Esta acción usa parámetros POST. Lo más probable es que provengan</span>
<span class="c1"># de un formulario HTML que el usuario ha enviado. La URL para</span>
<span class="c1"># esta solicitud RESTful será "/clients", y los datos se enviarán</span>
<span class="c1"># como parte del cuerpo de la solicitud.</span>
<span class="k">def</span> <span class="nf">create</span>
<span class="vi">@client</span> <span class="o">=</span> <span class="no">Client</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:client</span><span class="p">])</span>
<span class="k">if</span> <span class="vi">@client</span><span class="p">.</span><span class="nf">save</span>
<span class="n">redirect_to</span> <span class="vi">@client</span>
<span class="k">else</span>
<span class="c1"># Esta línea anula el comportamiento de renderizado predeterminado, que</span>
<span class="c1"># habría sido renderizar la vista "create".</span>
<span class="n">render</span> <span class="s2">"new"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class ClientsController < ApplicationController
# Esta acción usa parámetros de cadena de consulta porque se ejecuta
# mediante una solicitud HTTP GET, pero esto no hace ninguna diferencia
# en cómo se acceden los parámetros. La URL para
# esta acción se vería así para listar clientes activados:
# /clients?status=activated
def index
if params[:status] == "activated"
@clients = Client.activated
else
@clients = Client.inactivated
end
end
# Esta acción usa parámetros POST. Lo más probable es que provengan
# de un formulario HTML que el usuario ha enviado. La URL para
# esta solicitud RESTful será "/clients", y los datos se enviarán
# como parte del cuerpo de la solicitud.
def create
@client = Client.new(params[:client])
if @client.save
redirect_to @client
else
# Esta línea anula el comportamiento de renderizado predeterminado, que
# habría sido renderizar la vista "create".
render "new"
end
end
end
">Copy</button>
</div>
<h3 id="parámetros-hash-y-de-array"><a class="anchorlink" href="#parámetros-hash-y-de-array"><span>4.1</span> Parámetros Hash y de Array</a></h3><p>El hash <code>params</code> no está limitado a claves y valores unidimensionales. Puede contener arrays y hashes anidados. Para enviar un array de valores, agrega un par de corchetes vacíos "[]" al nombre de la clave:</p><div class="interstitial code">
<pre><code class="highlight plaintext">GET /clients?ids[]=1&ids[]=2&ids[]=3
</code></pre>
<button class="clipboard-button" data-clipboard-text="GET /clients?ids[]=1&ids[]=2&ids[]=3
">Copy</button>
</div>
<p>NOTA: La URL real en este ejemplo estará codificada como "/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5d=3" ya que los caracteres "[" y "]" no están permitidos en las URLs. La mayoría de las veces no tienes que preocuparte por esto porque el navegador lo codificará por ti, y Rails lo decodificará automáticamente, pero si alguna vez te encuentras teniendo que enviar esas solicitudes al servidor manualmente, debes tener esto en mente.</p><p>El valor de <code>params[:ids]</code> ahora será <code>["1", "2", "3"]</code>. Ten en cuenta que los valores de los parámetros siempre son cadenas; Rails no intenta adivinar o convertir el tipo.</p><p>NOTA: Valores como <code>[nil]</code> o <code>[nil, nil, ...]</code> en <code>params</code> se reemplazan
con <code>[]</code> por razones de seguridad por defecto. Consulta la <a href="security.html#unsafe-query-generation">Guía de Seguridad</a>
para más información.</p><p>Para enviar un hash, incluyes el nombre de la clave dentro de los corchetes:</p><div class="interstitial code">
<pre><code class="highlight html"><span class="nt"><form</span> <span class="na">accept-charset=</span><span class="s">"UTF-8"</span> <span class="na">action=</span><span class="s">"/clients"</span> <span class="na">method=</span><span class="s">"post"</span><span class="nt">></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">name=</span><span class="s">"client[name]"</span> <span class="na">value=</span><span class="s">"Acme"</span> <span class="nt">/></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">name=</span><span class="s">"client[phone]"</span> <span class="na">value=</span><span class="s">"12345"</span> <span class="nt">/></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">name=</span><span class="s">"client[address][postcode]"</span> <span class="na">value=</span><span class="s">"12345"</span> <span class="nt">/></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">name=</span><span class="s">"client[address][city]"</span> <span class="na">value=</span><span class="s">"Carrot City"</span> <span class="nt">/></span>
<span class="nt"></form></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<form accept-charset="UTF-8" action="/clients" method="post">
<input type="text" name="client[name]" value="Acme" />
<input type="text" name="client[phone]" value="12345" />
<input type="text" name="client[address][postcode]" value="12345" />
<input type="text" name="client[address][city]" value="Carrot City" />
</form>
">Copy</button>
</div>
<p>Cuando se envía este formulario, el valor de <code>params[:client]</code> será <code>{ "name" => "Acme", "phone" => "12345", "address" => { "postcode" => "12345", "city" => "Carrot City" } }</code>. Nota el hash anidado en <code>params[:client][:address]</code>.</p><p>El objeto <code>params</code> actúa como un Hash, pero te permite usar símbolos y cadenas de manera intercambiable como claves.</p><h3 id="parámetros-json"><a class="anchorlink" href="#parámetros-json"><span>4.2</span> Parámetros JSON</a></h3><p>Si tu aplicación expone una API, es probable que aceptes parámetros en formato JSON. Si el encabezado "Content-Type" de tu solicitud está configurado como "application/json", Rails cargará automáticamente tus parámetros en el hash <code>params</code>, al cual puedes acceder como lo harías normalmente.</p><p>Por ejemplo, si estás enviando este contenido JSON:</p><div class="interstitial code">
<pre><code class="highlight json"><span class="p">{</span><span class="w"> </span><span class="nl">"company"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"acme"</span><span class="p">,</span><span class="w"> </span><span class="nl">"address"</span><span class="p">:</span><span class="w"> </span><span class="s2">"123 Carrot Street"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="{ "company": { "name": "acme", "address": "123 Carrot Street" } }
">Copy</button>
</div>
<p>Tu controlador recibirá <code>params[:company]</code> como <code>{ "name" => "acme", "address" => "123 Carrot Street" }</code>.</p><p>Además, si has activado <code>config.wrap_parameters</code> en tu inicializador o llamado a <a href="https://edgeapi.rubyonrails.org/classes/ActionController/ParamsWrapper/Options/ClassMethods.html#method-i-wrap_parameters"><code>wrap_parameters</code></a> en tu controlador, puedes omitir de manera segura el elemento raíz en el parámetro JSON. En este caso, los parámetros se clonarán y envolverán con una clave elegida basada en el nombre de tu controlador. Así que la solicitud JSON anterior se puede escribir como:</p><div class="interstitial code">
<pre><code class="highlight json"><span class="p">{</span><span class="w"> </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"acme"</span><span class="p">,</span><span class="w"> </span><span class="nl">"address"</span><span class="p">:</span><span class="w"> </span><span class="s2">"123 Carrot Street"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="{ "name": "acme", "address": "123 Carrot Street" }
">Copy</button>
</div>
<p>Y, asumiendo que estás enviando los datos a <code>CompaniesController</code>, entonces se envolvería dentro de la clave <code>:company</code> así:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="p">{</span> <span class="ss">name: </span><span class="s2">"acme"</span><span class="p">,</span> <span class="ss">address: </span><span class="s2">"123 Carrot Street"</span><span class="p">,</span> <span class="ss">company: </span><span class="p">{</span> <span class="ss">name: </span><span class="s2">"acme"</span><span class="p">,</span> <span class="ss">address: </span><span class="s2">"123 Carrot Street"</span> <span class="p">}</span> <span class="p">}</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="{ name: "acme", address: "123 Carrot Street", company: { name: "acme", address: "123 Carrot Street" } }
">Copy</button>
</div>
<p>Puedes personalizar el nombre de la clave o los parámetros específicos que deseas envolver consultando la <a href="https://edgeapi.rubyonrails.org/classes/ActionController/ParamsWrapper.html">documentación de la API</a>.</p><p>NOTA: El soporte para el análisis de parámetros XML ha sido extraído a una gema llamada <code>actionpack-xml_parser</code>.</p><h3 id="parámetros-de-enrutamiento"><a class="anchorlink" href="#parámetros-de-enrutamiento"><span>4.3</span> Parámetros de Enrutamiento</a></h3><p>El hash <code>params</code> siempre contendrá las claves <code>:controller</code> y <code>:action</code>, pero debes usar los métodos <a href="https://edgeapi.rubyonrails.org/classes/ActionController/Metal.html#method-i-controller_name"><code>controller_name</code></a> y <a href="https://edgeapi.rubyonrails.org/classes/AbstractController/Base.html#method-i-action_name"><code>action_name</code></a> en su lugar para acceder a estos valores. Cualquier otro parámetro definido por el enrutamiento, como <code>:id</code>, también estará disponible. Como ejemplo, considera una lista de clientes donde la lista puede mostrar clientes activos o inactivos. Podemos agregar una ruta que capture el parámetro <code>:status</code> en una URL "bonita":</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">get</span> <span class="s1">'/clients/:status'</span><span class="p">,</span> <span class="ss">to: </span><span class="s1">'clients#index'</span><span class="p">,</span> <span class="ss">foo: </span><span class="s1">'bar'</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="get '/clients/:status', to: 'clients#index', foo: 'bar'
">Copy</button>
</div>
<p>En este caso, cuando un usuario abre la URL <code>/clients/active</code>, <code>params[:status]</code> se establecerá en "active". Cuando se usa esta ruta, <code>params[:foo]</code> también se establecerá en "bar", como si hubiera sido pasado en la cadena de consulta. Tu controlador también recibirá <code>params[:action]</code> como "index" y <code>params[:controller]</code> como "clients".</p><h3 id="parámetros-de-clave-compuesta"><a class="anchorlink" href="#parámetros-de-clave-compuesta"><span>4.4</span> Parámetros de Clave Compuesta</a></h3><p>Los parámetros de clave compuesta contienen múltiples valores en un solo parámetro. Por esta razón, necesitamos poder extraer cada valor y pasarlos a Active Record. Podemos aprovechar el método <code>extract_value</code> para este caso de uso.</p><p>Dado el siguiente controlador:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">BooksController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
<span class="k">def</span> <span class="nf">show</span>
<span class="c1"># Extraer el valor de ID compuesto de los parámetros de URL.</span>
<span class="nb">id</span> <span class="o">=</span> <span class="n">params</span><span class="p">.</span><span class="nf">extract_value</span><span class="p">(</span><span class="ss">:id</span><span class="p">)</span>
<span class="c1"># Encontrar el libro usando el ID compuesto.</span>
<span class="vi">@book</span> <span class="o">=</span> <span class="no">Book</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
<span class="c1"># usar el comportamiento de renderizado predeterminado para renderizar la vista show.</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class BooksController < ApplicationController
def show
# Extraer el valor de ID compuesto de los parámetros de URL.
id = params.extract_value(:id)
# Encontrar el libro usando el ID compuesto.
@book = Book.find(id)
# usar el comportamiento de renderizado predeterminado para renderizar la vista show.
end
end
">Copy</button>
</div>
<p>Y la siguiente ruta:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">get</span> <span class="s1">'/books/:id'</span><span class="p">,</span> <span class="ss">to: </span><span class="s1">'books#show'</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="get '/books/:id', to: 'books#show'
">Copy</button>
</div>
<p>Cuando un usuario abre la URL <code>/books/4_2</code>, el controlador extraerá el valor de clave compuesta <code>["4", "2"]</code> y lo pasará a <code>Book.find</code> para renderizar el registro correcto en la vista. El método <code>extract_value</code> puede ser usado para extraer arrays de cualquier parámetro delimitado.</p><h3 id="default-url-options"><a class="anchorlink" href="#default-url-options"><span>4.5</span> <code>default_url_options</code></a></h3><p>Puedes establecer parámetros predeterminados globales para la generación de URLs definiendo un método llamado <code>default_url_options</code> en tu controlador. Dicho método debe devolver un hash con los valores predeterminados deseados, cuyas claves deben ser símbolos:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o"><</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
<span class="k">def</span> <span class="nf">default_url_options</span>
<span class="p">{</span> <span class="ss">locale: </span><span class="no">I18n</span><span class="p">.</span><span class="nf">locale</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class ApplicationController < ActionController::Base
def default_url_options
{ locale: I18n.locale }
end
end
">Copy</button>
</div>
<p>Estas opciones se usarán como punto de partida al generar URLs, por lo que es posible que sean reemplazadas por las opciones pasadas a las llamadas <code>url_for</code>.</p><p>Si defines <code>default_url_options</code> en <code>ApplicationController</code>, como en el ejemplo anterior, estos valores predeterminados se usarán para toda la generación de URLs. El método también se puede definir en un controlador específico, en cuyo caso solo afecta a las URLs generadas allí.</p><p>En una solicitud dada, el método no se llama realmente para cada URL generada. Por razones de rendimiento, el hash devuelto se almacena en caché, y hay como máximo una invocación por solicitud.</p><h3 id="parámetros-fuertes"><a class="anchorlink" href="#parámetros-fuertes"><span>4.6</span> Parámetros Fuertes</a></h3><p>Con los parámetros fuertes, los parámetros del Action Controller están prohibidos para ser usados en asignaciones masivas de Active Model hasta que hayan sido permitidos. Esto significa que tendrás que tomar una decisión consciente sobre qué atributos permitir para la actualización masiva. Esta es una mejor práctica de seguridad para ayudar a prevenir permitir accidentalmente que los usuarios actualicen atributos sensibles del modelo.</p><p>Además, los parámetros pueden ser marcados como requeridos y fluirán a través de un flujo de raise/rescue predefinido que resultará en un 400 Bad Request si no se pasan todos los parámetros requeridos.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">PeopleController</span> <span class="o"><</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
<span class="c1"># Esto lanzará una excepción ActiveModel::ForbiddenAttributesError</span>
<span class="c1"># porque está usando asignación masiva sin un paso de permiso explícito.</span>
<span class="k">def</span> <span class="nf">create</span>
<span class="no">Person</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:person</span><span class="p">])</span>
<span class="k">end</span>
<span class="c1"># Esto pasará sin problemas siempre y cuando haya una clave person</span>
<span class="c1"># en los parámetros, de lo contrario lanzará una excepción</span>
<span class="c1"># ActionController::ParameterMissing, que será capturada por</span>
<span class="c1"># ActionController::Base y convertida en un error 400 Bad Request.</span>
<span class="k">def</span> <span class="nf">update</span>
<span class="n">person</span> <span class="o">=</span> <span class="n">current_account</span><span class="p">.</span><span class="nf">people</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:id</span><span class="p">])</span>
<span class="n">person</span><span class="p">.</span><span class="nf">update!</span><span class="p">(</span><span class="n">person_params</span><span class="p">)</span>
<span class="n">redirect_to</span> <span class="n">person</span>
<span class="k">end</span>
<span class="kp">private</span>
<span class="c1"># Usar un método privado para encapsular los parámetros permisibles</span>
<span class="c1"># es solo un buen patrón ya que podrás reutilizar la misma</span>
<span class="c1"># lista de permisos entre create y update. Además, puedes especializar</span>
<span class="c1"># este método con verificación por usuario de atributos permisibles.</span>
<span class="k">def</span> <span class="nf">person_params</span>
<span class="n">params</span><span class="p">.</span><span class="nf">require</span><span class="p">(</span><span class="ss">:person</span><span class="p">).</span><span class="nf">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:age</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class PeopleController < ActionController::Base
# Esto lanzará una excepción ActiveModel::ForbiddenAttributesError
# porque está usando asignación masiva sin un paso de permiso explícito.
def create
Person.create(params[:person])
end
# Esto pasará sin problemas siempre y cuando haya una clave person
# en los parámetros, de lo contrario lanzará una excepción
# ActionController::ParameterMissing, que será capturada por
# ActionController::Base y convertida en un error 400 Bad Request.
def update
person = current_account.people.find(params[:id])
person.update!(person_params)
redirect_to person
end
private
# Usar un método privado para encapsular los parámetros permisibles
# es solo un buen patrón ya que podrás reutilizar la misma
# lista de permisos entre create y update. Además, puedes especializar
# este método con verificación por usuario de atributos permisibles.
def person_params
params.require(:person).permit(:name, :age)
end
end
">Copy</button>
</div>
<h4 id="valores-escalares-permitidos"><a class="anchorlink" href="#valores-escalares-permitidos"><span>4.6.1</span> Valores Escalares Permitidos</a></h4><p>Llamar a <a href="https://edgeapi.rubyonrails.org/classes/ActionController/Parameters.html#method-i-permit"><code>permit</code></a> como:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">params</span><span class="p">.</span><span class="nf">permit</span><span class="p">(</span><span class="ss">:id</span><span class="p">)</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="params.permit(:id)
">Copy</button>
</div>
<p>permite la inclusión de la clave especificada (<code>:id</code>) si aparece en <code>params</code> y tiene un valor escalar permitido asociado. De lo contrario, la clave será filtrada, por lo que no se pueden inyectar arrays, hashes u otros objetos.</p><p>Los tipos escalares permitidos son <code>String</code>, <code>Symbol</code>, <code>NilClass</code>, <code>Numeric</code>, <code>TrueClass</code>, <code>FalseClass</code>, <code>Date</code>, <code>Time</code>, <code>DateTime</code>, <code>StringIO</code>, <code>IO</code>, <code>ActionDispatch::Http::UploadedFile</code>, y <code>Rack::Test::UploadedFile</code>.</p><p>Para declarar que el valor en <code>params</code> debe ser un array de valores escalares permitidos, mapea la clave a un array vacío:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">params</span><span class="p">.</span><span class="nf">permit</span><span class="p">(</span><span class="ss">id: </span><span class="p">[])</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="params.permit(id: [])
">Copy</button>
</div>
<p>A veces no es posible o conveniente declarar las claves válidas de un parámetro hash o su estructura interna. Simplemente mapea a un hash vacío:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">params</span><span class="p">.</span><span class="nf">permit</span><span class="p">(</span><span class="ss">preferences: </span><span class="p">{})</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="params.permit(preferences: {})
">Copy</button>
</div>
<p>pero ten cuidado porque esto abre la puerta a entradas arbitrarias. En este caso, <code>permit</code> asegura que los valores en la estructura devuelta sean escalares permitidos y filtra cualquier otra cosa.</p><p>Para permitir un hash completo de parámetros, se puede usar el método <a href="https://edgeapi.rubyonrails.org/classes/ActionController/Parameters.html#method-i-permit-21"><code>permit!</code></a>:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">params</span><span class="p">.</span><span class="nf">require</span><span class="p">(</span><span class="ss">:log_entry</span><span class="p">).</span><span class="nf">permit!</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="params.require(:log_entry).permit!
">Copy</button>
</div>
<p>Esto marca el hash de parámetros <code>:log_entry</code> y cualquier sub-hash de él como permitido y no verifica los escalares permitidos, se acepta cualquier cosa. Se debe tener extremo cuidado al usar <code>permit!</code>, ya que permitirá que todos los atributos del modelo presentes y futuros sean asignados masivamente.</p><h4 id="parámetros-anidados"><a class="anchorlink" href="#parámetros-anidados"><span>4.6.2</span> Parámetros Anidados</a></h4><p>También puedes usar <code>permit</code> en parámetros anidados, como:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">params</span><span class="p">.</span><span class="nf">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="p">{</span> <span class="ss">emails: </span><span class="p">[]</span> <span class="p">},</span>
<span class="ss">friends: </span><span class="p">[</span> <span class="ss">:name</span><span class="p">,</span>
<span class="p">{</span> <span class="ss">family: </span><span class="p">[</span> <span class="ss">:name</span> <span class="p">],</span> <span class="ss">hobbies: </span><span class="p">[]</span> <span class="p">}])</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="params.permit(:name, { emails: [] },
friends: [ :name,
{ family: [ :name ], hobbies: [] }])
">Copy</button>
</div>
<p>Esta declaración permite los atributos <code>name</code>, <code>emails</code>, y <code>friends</code>. Se espera que <code>emails</code> sea un array de valores escalares permitidos, y que <code>friends</code> sea un array de recursos con atributos específicos: deben tener un atributo <code>name</code> (se permiten cualquier valor escalar permitido), un atributo <code>hobbies</code> como un array de valores escalares permitidos, y un atributo <code>family</code> que está restringido a tener un <code>name</code> (también se permiten aquí cualquier valor escalar permitido).</p><h4 id="más-ejemplos"><a class="anchorlink" href="#más-ejemplos"><span>4.6.3</span> Más Ejemplos</a></h4><p>Es posible que también desees usar los atributos permitidos en tu acción <code>new</code>. Esto plantea el problema de que no puedes usar <a href="https://edgeapi.rubyonrails.org/classes/ActionController/Parameters.html#method-i-require"><code>require</code></a> en la clave raíz porque, normalmente, no existe cuando llamas a <code>new</code>:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="c1"># usando `fetch` puedes proporcionar un valor predeterminado y usar</span>
<span class="c1"># la API de Parámetros Fuertes desde allí.</span>
<span class="n">params</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:blog</span><span class="p">,</span> <span class="p">{}).</span><span class="nf">permit</span><span class="p">(</span><span class="ss">:title</span><span class="p">,</span> <span class="ss">:author</span><span class="p">)</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="# usando `fetch` puedes proporcionar un valor predeterminado y usar
# la API de Parámetros Fuertes desde allí.
params.fetch(:blog, {}).permit(:title, :author)
">Copy</button>
</div>
<p>El método de clase del modelo <code>accepts_nested_attributes_for</code> te permite actualizar y destruir registros asociados. Esto se basa en los parámetros <code>id</code> y <code>_destroy</code>:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="c1"># permite :id y :_destroy</span>
<span class="n">params</span><span class="p">.</span><span class="nf">require</span><span class="p">(</span><span class="ss">:author</span><span class="p">).</span><span class="nf">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">books_attributes: </span><span class="p">[</span><span class="ss">:title</span><span class="p">,</span> <span class="ss">:id</span><span class="p">,</span> <span class="ss">:_destroy</span><span class="p">])</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="# permite :id y :_destroy
params.require(:author).permit(:name, books_attributes: [:title, :id, :_destroy])
">Copy</button>
</div>
<p>Los hashes con claves enteras se tratan de manera diferente, y puedes declarar
los atributos como si fueran hijos directos. Obtienes estos tipos de
parámetros cuando usas <code>accepts_nested_attributes_for</code> en combinación
con una asociación <code>has_many</code>:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="c1"># Para permitir los siguientes datos:</span>
<span class="c1"># {"book" => {"title" => "Some Book",</span>
<span class="c1"># "chapters_attributes" => { "1" => {"title" => "First Chapter"},</span>
<span class="c1"># "2" => {"title" => "Second Chapter"}}}}</span>
<span class="n">params</span><span class="p">.</span><span class="nf">require</span><span class="p">(</span><span class="ss">:book</span><span class="p">).</span><span class="nf">permit</span><span class="p">(</span><span class="ss">:title</span><span class="p">,</span> <span class="ss">chapters_attributes: </span><span class="p">[</span><span class="ss">:title</span><span class="p">])</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="# Para permitir los siguientes datos:
# {"book" => {"title" => "Some Book",
# "chapters_attributes" => { "1" => {"title" => "First Chapter"},
# "2" => {"title" => "Second Chapter"}}}}
params.require(:book).permit(:title, chapters_attributes: [:title])
">Copy</button>
</div>
<p>Imagina un escenario donde tienes parámetros que representan un
nombre de producto, y un hash de datos arbitrarios asociados con ese producto, y
quieres permitir el atributo de nombre del producto y también todo el
hash de datos:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">def</span> <span class="nf">product_params</span>
<span class="n">params</span><span class="p">.</span><span class="nf">require</span><span class="p">(</span><span class="ss">:product</span><span class="p">).</span><span class="nf">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">data: </span><span class="p">{})</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="def product_params
params.require(:product).permit(:name, data: {})
end
">Copy</button>
</div>
<h4 id="fuera-del-alcance-de-los-parámetros-fuertes"><a class="anchorlink" href="#fuera-del-alcance-de-los-parámetros-fuertes"><span>4.6.4</span> Fuera del Alcance de los Parámetros Fuertes</a></h4><p>La API de parámetros fuertes fue diseñada con los casos de uso más comunes
en mente. No está destinada a ser una solución mágica para manejar todos tus
problemas de filtrado de parámetros. Sin embargo, puedes mezclar fácilmente la API con tu
propio código para adaptarte a tu situación.</p><h2 id="sesión"><a class="anchorlink" href="#sesión"><span>5</span> Sesión</a></h2><p>Tu aplicación tiene una sesión para cada usuario en la que puedes almacenar pequeñas cantidades de datos que se mantendrán entre solicitudes. La sesión solo está disponible en el controlador y la vista y puede usar uno de varios mecanismos de almacenamiento diferentes:</p>
<ul>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActionDispatch/Session/CookieStore.html"><code>ActionDispatch::Session::CookieStore</code></a> - Almacena todo en el cliente.</li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActionDispatch/Session/CacheStore.html"><code>ActionDispatch::Session::CacheStore</code></a> - Almacena los datos en la caché de Rails.</li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActionDispatch/Session/MemCacheStore.html"><code>ActionDispatch::Session::MemCacheStore</code></a> - Almacena los datos en un clúster de memcached (esta es una implementación heredada; considera usar <code>CacheStore</code> en su lugar).</li>
<li><a href="https://github.com/rails/activerecord-session_store"><code>ActionDispatch::Session::ActiveRecordStore</code></a> -
Almacena los datos en una base de datos usando Active Record (requiere la
gema <a href="https://github.com/rails/activerecord-session_store"><code>activerecord-session_store</code></a>)</li>
<li>Un almacén personalizado o un almacén proporcionado por una gema de terceros</li>
</ul>
<p>Todos los almacenes de sesión usan una cookie para almacenar un ID único para cada sesión (debes usar una cookie, Rails no te permitirá pasar el ID de sesión en la URL ya que esto es menos seguro).</p><p>Para la mayoría de los almacenes, este ID se usa para buscar los datos de la sesión en el servidor, por ejemplo, en una tabla de base de datos. Hay una excepción, y esa es el almacén de sesiones predeterminado y recomendado: el CookieStore, que almacena todos los datos de la sesión en la propia cookie (el ID sigue estando disponible para ti si lo necesitas). Esto tiene la ventaja de ser muy ligero, y no requiere ninguna configuración en una nueva aplicación para usar la sesión. Los datos de la cookie están firmados criptográficamente para hacerlos a prueba de manipulaciones. Y también están encriptados para que cualquiera con acceso a ellos no pueda leer su contenido. (Rails no lo aceptará si ha sido editado).</p><p>El CookieStore puede almacenar alrededor de 4 kB de datos, mucho menos que los otros, pero esto suele ser suficiente. Se desaconseja almacenar grandes cantidades de datos en la sesión sin importar qué almacén de sesión use tu aplicación. Debes evitar especialmente almacenar objetos complejos (como instancias de modelo) en la sesión, ya que el servidor podría no ser capaz de reensamblarlos entre solicitudes, lo que resultará en un error.</p><p>Si las sesiones de tu usuario no almacenan datos críticos o no necesitan estar disponibles durante largos períodos (por ejemplo, si solo usas el flash para mensajes), puedes considerar usar <code>ActionDispatch::Session::CacheStore</code>. Esto almacenará las sesiones usando la implementación de caché que hayas configurado para tu aplicación. La ventaja de esto es que puedes usar tu infraestructura de caché existente para almacenar sesiones sin requerir ninguna configuración o administración adicional. La desventaja, por supuesto, es que las sesiones serán efímeras y podrían desaparecer en cualquier momento.</p><p>Lee más sobre el almacenamiento de sesiones en la <a href="security.html">Guía de Seguridad</a>.</p><p>Si necesitas un mecanismo de almacenamiento de sesión diferente, puedes cambiarlo en un inicializador:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">config</span><span class="p">.</span><span class="nf">session_store</span> <span class="ss">:cache_store</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="Rails.application.config.session_store :cache_store
">Copy</button>
</div>
<p>Consulta <a href="configuring.html#config-session-store"><code>config.session_store</code></a> en la
guía de configuración para más información.</p><p>Rails configura una clave de sesión (el nombre de la cookie) al firmar los datos de la sesión. Estos también se pueden cambiar en un inicializador:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="c1"># Asegúrate de reiniciar tu servidor cuando modifiques este archivo.</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">config</span><span class="p">.</span><span class="nf">session_store</span> <span class="ss">:cookie_store</span><span class="p">,</span> <span class="ss">key: </span><span class="s1">'_your_app_session'</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="# Asegúrate de reiniciar tu servidor cuando modifiques este archivo.
Rails.application.config.session_store :cookie_store, key: '_your_app_session'
">Copy</button>
</div>
<p>También puedes pasar una clave <code>:domain</code> y especificar el nombre de dominio para la cookie:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="c1"># Asegúrate de reiniciar tu servidor cuando modifiques este archivo.</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">config</span><span class="p">.</span><span class="nf">session_store</span> <span class="ss">:cookie_store</span><span class="p">,</span> <span class="ss">key: </span><span class="s1">'_your_app_session'</span><span class="p">,</span> <span class="ss">domain: </span><span class="s2">".example.com"</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="# Asegúrate de reiniciar tu servidor cuando modifiques este archivo.
Rails.application.config.session_store :cookie_store, key: '_your_app_session', domain: ".example.com"
">Copy</button>
</div>
<p>Rails configura (para el CookieStore) una clave secreta utilizada para firmar los datos de la sesión en <code>config/credentials.yml.enc</code>. Esto se puede cambiar con <code>bin/rails credentials:edit</code>.</p><div class="interstitial code">
<pre><code class="highlight yaml"><span class="c1"># aws:</span>
<span class="c1"># access_key_id: 123</span>
<span class="c1"># secret_access_key: 345</span>
<span class="c1"># Usado como la clave secreta base para todos los MessageVerifiers en Rails, incluyendo el que protege las cookies.</span>
<span class="na">secret_key_base</span><span class="pi">:</span> <span class="s">492f...</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="# aws:
# access_key_id: 123
# secret_access_key: 345
# Usado como la clave secreta base para todos los MessageVerifiers en Rails, incluyendo el que protege las cookies.
secret_key_base: 492f...
">Copy</button>
</div>
<p>NOTA: Cambiar el secret_key_base cuando se usa el <code>CookieStore</code> invalidará todas las sesiones existentes.</p><h3 id="accediendo-a-la-sesión"><a class="anchorlink" href="#accediendo-a-la-sesión"><span>5.1</span> Accediendo a la Sesión</a></h3><p>En tu controlador, puedes acceder a la sesión a través del método de instancia <code>session</code>.</p><p>NOTA: Las sesiones se cargan de manera perezosa. Si no accedes a las sesiones en el código de tu acción, no se cargarán. Por lo tanto, nunca necesitarás deshabilitar las sesiones, simplemente no acceder a ellas hará el trabajo.</p><p>Los valores de la sesión se almacenan usando pares clave/valor como un hash:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o"><</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
<span class="kp">private</span>
<span class="c1"># Encuentra al Usuario con el ID almacenado en la sesión con la clave</span>
<span class="c1"># :current_user_id Esta es una forma común de manejar el inicio de sesión del usuario en</span>
<span class="c1"># una aplicación Rails; iniciar sesión establece el valor de la sesión y</span>
<span class="c1"># cerrar sesión lo elimina.</span>
<span class="k">def</span> <span class="nf">current_user</span>
<span class="vi">@_current_user</span> <span class="o">||=</span> <span class="n">session</span><span class="p">[</span><span class="ss">:current_user_id</span><span class="p">]</span> <span class="o">&&</span>
<span class="no">User</span><span class="p">.</span><span class="nf">find_by</span><span class="p">(</span><span class="ss">id: </span><span class="n">session</span><span class="p">[</span><span class="ss">:current_user_id</span><span class="p">])</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class ApplicationController < ActionController::Base
private
# Encuentra al Usuario con el ID almacenado en la sesión con la clave
# :current_user_id Esta es una forma común de manejar el inicio de sesión del usuario en
# una aplicación Rails; iniciar sesión establece el valor de la sesión y
# cerrar sesión lo elimina.
def current_user
@_current_user ||= session[:current_user_id] &&
User.find_by(id: session[:current_user_id])
end
end
">Copy</button>
</div>
<p>Para almacenar algo en la sesión, simplemente asígnalo a la clave como un hash:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">LoginsController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
<span class="c1"># "Crear" un inicio de sesión, también conocido como "iniciar sesión al usuario"</span>
<span class="k">def</span> <span class="nf">create</span>
<span class="k">if</span> <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">authenticate</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:username</span><span class="p">],</span> <span class="n">params</span><span class="p">[</span><span class="ss">:password</span><span class="p">])</span>
<span class="c1"># Guarda el ID de usuario en la sesión para que pueda usarse en</span>
<span class="c1"># solicitudes posteriores</span>
<span class="n">session</span><span class="p">[</span><span class="ss">:current_user_id</span><span class="p">]</span> <span class="o">=</span> <span class="n">user</span><span class="p">.</span><span class="nf">id</span>
<span class="n">redirect_to</span> <span class="n">root_url</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class LoginsController < ApplicationController
# "Crear" un inicio de sesión, también conocido como "iniciar sesión al usuario"
def create
if user = User.authenticate(params[:username], params[:password])
# Guarda el ID de usuario en la sesión para que pueda usarse en
# solicitudes posteriores
session[:current_user_id] = user.id
redirect_to root_url
end
end
end
">Copy</button>
</div>
<p>Para eliminar algo de la sesión, elimina el par clave/valor:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">LoginsController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
<span class="c1"># "Eliminar" un inicio de sesión, también conocido como "cerrar sesión al usuario"</span>
<span class="k">def</span> <span class="nf">destroy</span>
<span class="c1"># Elimina el id de usuario de la sesión</span>
<span class="n">session</span><span class="p">.</span><span class="nf">delete</span><span class="p">(</span><span class="ss">:current_user_id</span><span class="p">)</span>
<span class="c1"># Limpia el usuario actual memoizado</span>
<span class="vi">@_current_user</span> <span class="o">=</span> <span class="kp">nil</span>
<span class="n">redirect_to</span> <span class="n">root_url</span><span class="p">,</span> <span class="ss">status: :see_other</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class LoginsController < ApplicationController
# "Eliminar" un inicio de sesión, también conocido como "cerrar sesión al usuario"
def destroy
# Elimina el id de usuario de la sesión
session.delete(:current_user_id)
# Limpia el usuario actual memoizado
@_current_user = nil
redirect_to root_url, status: :see_other
end
end
">Copy</button>
</div>
<p>Para restablecer toda la sesión, usa <a href="https://edgeapi.rubyonrails.org/classes/ActionController/Metal.html#method-i-reset_session"><code>reset_session</code></a>.</p><h3 id="el-flash"><a class="anchorlink" href="#el-flash"><span>5.2</span> El Flash</a></h3><p>El flash es una parte especial de la sesión que se borra con cada solicitud. Esto significa que los valores almacenados allí solo estarán disponibles en la siguiente solicitud, lo cual es útil para pasar mensajes de error, etc.</p><p>El flash se accede a través del método <a href="https://edgeapi.rubyonrails.org/classes/ActionDispatch/Flash/RequestMethods.html#method-i-flash"><code>flash</code></a>. Al igual que la sesión, el flash se representa como un hash.</p><p>Usemos el acto de cerrar sesión como ejemplo. El controlador puede enviar un mensaje que se mostrará al usuario en la siguiente solicitud:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">LoginsController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
<span class="k">def</span> <span class="nf">destroy</span>
<span class="n">session</span><span class="p">.</span><span class="nf">delete</span><span class="p">(</span><span class="ss">:current_user_id</span><span class="p">)</span>
<span class="n">flash</span><span class="p">[</span><span class="ss">:notice</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"Has cerrado sesión exitosamente."</span>
<span class="n">redirect_to</span> <span class="n">root_url</span><span class="p">,</span> <span class="ss">status: :see_other</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class LoginsController < ApplicationController
def destroy
session.delete(:current_user_id)
flash[:notice] = "Has cerrado sesión exitosamente."
redirect_to root_url, status: :see_other
end
end
">Copy</button>
</div>
<p>Nota que también es posible asignar un mensaje flash como parte de la redirección. Puedes asignar <code>:notice</code>, <code>:alert</code> o el <code>:flash</code> de propósito general:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">redirect_to</span> <span class="n">root_url</span><span class="p">,</span> <span class="ss">notice: </span><span class="s2">"Has cerrado sesión exitosamente."</span>
<span class="n">redirect_to</span> <span class="n">root_url</span><span class="p">,</span> <span class="ss">alert: </span><span class="s2">"¡Estás atrapado aquí!"</span>
<span class="n">redirect_to</span> <span class="n">root_url</span><span class="p">,</span> <span class="ss">flash: </span><span class="p">{</span> <span class="ss">referral_code: </span><span class="mi">1234</span> <span class="p">}</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="redirect_to root_url, notice: "Has cerrado sesión exitosamente."
redirect_to root_url, alert: "¡Estás atrapado aquí!"
redirect_to root_url, flash: { referral_code: 1234 }
">Copy</button>
</div>
<p>La acción <code>destroy</code> redirige a la <code>root_url</code> de la aplicación, donde se mostrará el mensaje. Nota que depende completamente de la siguiente acción decidir qué, si acaso, hará con lo que la acción anterior puso en el flash. Es convencional mostrar cualquier alerta de error o aviso del flash en el layout de la aplicación:</p><div class="interstitial code">
<pre><code class="highlight erb"><span class="nt"><html></span>
<span class="c"><!-- <head/> --></span>
<span class="nt"><body></span>
<span class="cp"><%</span> <span class="n">flash</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="nb">name</span><span class="p">,</span> <span class="n">msg</span><span class="o">|</span> <span class="cp">-%></span>
<span class="cp"><%=</span> <span class="n">content_tag</span> <span class="ss">:div</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="ss">class: </span><span class="nb">name</span> <span class="cp">%></span>
<span class="cp"><%</span> <span class="k">end</span> <span class="cp">-%></span>
<span class="c"><!-- más contenido --></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<html>
<!-- <head/> -->
<body>
<% flash.each do |name, msg| -%>
<%= content_tag :div, msg, class: name %>
<% end -%>
<!-- más contenido -->
</body>
</html>
">Copy</button>
</div>
<p>De esta manera, si una acción establece un aviso o un mensaje de alerta, el layout lo mostrará automáticamente.</p><p>Puedes pasar cualquier cosa que la sesión pueda almacenar; no estás limitado a avisos y alertas:</p><div class="interstitial code">
<pre><code class="highlight erb"><span class="cp"><%</span> <span class="k">if</span> <span class="n">flash</span><span class="p">[</span><span class="ss">:just_signed_up</span><span class="p">]</span> <span class="cp">%></span>
<span class="nt"><p</span> <span class="na">class=</span><span class="s">"welcome"</span><span class="nt">></span>¡Bienvenido a nuestro sitio!<span class="nt"></p></span>
<span class="cp"><%</span> <span class="k">end</span> <span class="cp">%></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<% if flash[:just_signed_up] %>
<p class="welcome">¡Bienvenido a nuestro sitio!</p>
<% end %>
">Copy</button>
</div>
<p>Si deseas que un valor flash se mantenga para otra solicitud, usa <a href="https://edgeapi.rubyonrails.org/classes/ActionDispatch/Flash/FlashHash.html#method-i-keep"><code>flash.keep</code></a>:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">MainController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
<span class="c1"># Digamos que esta acción corresponde a root_url, pero deseas</span>
<span class="c1"># que todas las solicitudes aquí sean redirigidas a UsersController#index.</span>
<span class="c1"># Si una acción establece el flash y redirige aquí, los valores</span>
<span class="c1"># normalmente se perderían cuando ocurra otra redirección, pero puedes</span>
<span class="c1"># usar 'keep' para hacer que persista para otra solicitud.</span>
<span class="k">def</span> <span class="nf">index</span>
<span class="c1"># Persistirá todos los valores flash.</span>
<span class="n">flash</span><span class="p">.</span><span class="nf">keep</span>
<span class="c1"># También puedes usar una clave para mantener solo algún tipo de valor.</span>
<span class="c1"># flash.keep(:notice)</span>
<span class="n">redirect_to</span> <span class="n">users_url</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class MainController < ApplicationController
# Digamos que esta acción corresponde a root_url, pero deseas
# que todas las solicitudes aquí sean redirigidas a UsersController#index.
# Si una acción establece el flash y redirige aquí, los valores
# normalmente se perderían cuando ocurra otra redirección, pero puedes
# usar 'keep' para hacer que persista para otra solicitud.
def index
# Persistirá todos los valores flash.
flash.keep
# También puedes usar una clave para mantener solo algún tipo de valor.
# flash.keep(:notice)
redirect_to users_url
end
end
">Copy</button>
</div>
<h4 id="flash-now"><a class="anchorlink" href="#flash-now"><span>5.2.1</span> <code>flash.now</code></a></h4><p>Por defecto, agregar valores al flash los hará disponibles para la próxima solicitud, pero a veces puedes querer acceder a esos valores en la misma solicitud. Por ejemplo, si la acción <code>create</code> no logra guardar un recurso, y renderizas la plantilla <code>new</code> directamente, eso no resultará en una nueva solicitud, pero es posible que aún desees mostrar un mensaje usando el flash. Para hacer esto, puedes usar <a href="https://edgeapi.rubyonrails.org/classes/ActionDispatch/Flash/FlashHash.html#method-i-now"><code>flash.now</code></a> de la misma manera que usas el flash normal:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">ClientsController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
<span class="k">def</span> <span class="nf">create</span>
<span class="vi">@client</span> <span class="o">=</span> <span class="no">Client</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">client_params</span><span class="p">)</span>
<span class="k">if</span> <span class="vi">@client</span><span class="p">.</span><span class="nf">save</span>
<span class="c1"># ...</span>
<span class="k">else</span>
<span class="n">flash</span><span class="p">.</span><span class="nf">now</span><span class="p">[</span><span class="ss">:error</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"No se pudo guardar el cliente"</span>
<span class="n">render</span> <span class="ss">action: </span><span class="s2">"new"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class ClientsController < ApplicationController
def create
@client = Client.new(client_params)
if @client.save
# ...
else
flash.now[:error] = "No se pudo guardar el cliente"
render action: "new"
end
end
end
">Copy</button>
</div>
<h2 id="cookies"><a class="anchorlink" href="#cookies"><span>6</span> Cookies</a></h2><p>Tu aplicación puede almacenar pequeñas cantidades de datos en el cliente, llamadas cookies, que se mantendrán entre solicitudes e incluso sesiones. Rails proporciona un acceso fácil a las cookies a través del método <a href="https://edgeapi.rubyonrails.org/classes/ActionController/Cookies.html#method-i-cookies"><code>cookies</code></a>, que, al igual que la <code>session</code>, funciona como un hash:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">CommentsController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
<span class="k">def</span> <span class="nf">new</span>
<span class="c1"># Auto-completar el nombre del comentarista si se ha almacenado en una cookie</span>
<span class="vi">@comment</span> <span class="o">=</span> <span class="no">Comment</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">author: </span><span class="n">cookies</span><span class="p">[</span><span class="ss">:commenter_name</span><span class="p">])</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">create</span>
<span class="vi">@comment</span> <span class="o">=</span> <span class="no">Comment</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">comment_params</span><span class="p">)</span>
<span class="k">if</span> <span class="vi">@comment</span><span class="p">.</span><span class="nf">save</span>
<span class="n">flash</span><span class="p">[</span><span class="ss">:notice</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"¡Gracias por tu comentario!"</span>
<span class="k">if</span> <span class="n">params</span><span class="p">[</span><span class="ss">:remember_name</span><span class="p">]</span>
<span class="c1"># Recordar el nombre del comentarista.</span>
<span class="n">cookies</span><span class="p">[</span><span class="ss">:commenter_name</span><span class="p">]</span> <span class="o">=</span> <span class="vi">@comment</span><span class="p">.</span><span class="nf">author</span>
<span class="k">else</span>
<span class="c1"># Eliminar la cookie del nombre del comentarista, si la hay.</span>
<span class="n">cookies</span><span class="p">.</span><span class="nf">delete</span><span class="p">(</span><span class="ss">:commenter_name</span><span class="p">)</span>
<span class="k">end</span>
<span class="n">redirect_to</span> <span class="vi">@comment</span><span class="p">.</span><span class="nf">article</span>
<span class="k">else</span>
<span class="n">render</span> <span class="ss">action: </span><span class="s2">"new"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class CommentsController < ApplicationController
def new
# Auto-completar el nombre del comentarista si se ha almacenado en una cookie
@comment = Comment.new(author: cookies[:commenter_name])
end
def create
@comment = Comment.new(comment_params)
if @comment.save
flash[:notice] = "¡Gracias por tu comentario!"
if params[:remember_name]
# Recordar el nombre del comentarista.
cookies[:commenter_name] = @comment.author
else
# Eliminar la cookie del nombre del comentarista, si la hay.
cookies.delete(:commenter_name)
end
redirect_to @comment.article
else
render action: "new"
end
end
end
">Copy</button>
</div>
<p>Ten en cuenta que, mientras que para los valores de sesión puedes establecer la clave en <code>nil</code>, para eliminar un valor de cookie debes usar <code>cookies.delete(:key)</code>.</p><p>Rails también proporciona un tarro de cookies firmado y un tarro de cookies encriptado para almacenar
datos sensibles. El tarro de cookies firmado añade una firma criptográfica a los
valores de las cookies para proteger su integridad. El tarro de cookies encriptado encripta los
valores además de firmarlos, para que no puedan ser leídos por el usuario final.
Consulta la <a href="https://edgeapi.rubyonrails.org/classes/ActionDispatch/Cookies.html">documentación de la API</a>
para más detalles.</p><p>Estos tarros de cookies especiales usan un serializador para serializar los valores asignados en
cadenas y deserializarlos en objetos Ruby al leerlos. Puedes especificar qué
serializador usar a través de <a href="configuring.html#config-action-dispatch-cookies-serializer"><code>config.action_dispatch.cookies_serializer</code></a>.</p><p>El serializador predeterminado para nuevas aplicaciones es <code>:json</code>. Ten en cuenta que JSON tiene
soporte limitado para redondear objetos Ruby. Por ejemplo, los objetos <code>Date</code>, <code>Time</code> y
<code>Symbol</code> (incluidas las claves de <code>Hash</code>) se serializarán y deserializarán
en <code>String</code>s:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">CookiesController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
<span class="k">def</span> <span class="nf">set_cookie</span>
<span class="n">cookies</span><span class="p">.</span><span class="nf">encrypted</span><span class="p">[</span><span class="ss">:expiration_date</span><span class="p">]</span> <span class="o">=</span> <span class="no">Date</span><span class="p">.</span><span class="nf">tomorrow</span> <span class="c1"># => Thu, 20 Mar 2014</span>
<span class="n">redirect_to</span> <span class="ss">action: </span><span class="s1">'read_cookie'</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">read_cookie</span>
<span class="n">cookies</span><span class="p">.</span><span class="nf">encrypted</span><span class="p">[</span><span class="ss">:expiration_date</span><span class="p">]</span> <span class="c1"># => "2014-03-20"</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class CookiesController < ApplicationController
def set_cookie
cookies.encrypted[:expiration_date] = Date.tomorrow # => Thu, 20 Mar 2014
redirect_to action: 'read_cookie'
end
def read_cookie
cookies.encrypted[:expiration_date] # => "2014-03-20"
end
end
">Copy</button>
</div>
<p>Si necesitas almacenar estos o más objetos complejos, es posible que necesites
convertir manualmente sus valores al leerlos en solicitudes posteriores.</p><p>Si usas el almacén de sesión de cookies, lo anterior también se aplica al hash <code>session</code> y
<code>flash</code>.</p><h2 id="renderizado"><a class="anchorlink" href="#renderizado"><span>7</span> Renderizado</a></h2><p>ActionController hace que el renderizado de datos HTML, XML o JSON sea sencillo. Si has generado un controlador usando scaffolding, se vería algo así:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
<span class="k">def</span> <span class="nf">index</span>
<span class="vi">@users</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">all</span>
<span class="n">respond_to</span> <span class="k">do</span> <span class="o">|</span><span class="nb">format</span><span class="o">|</span>
<span class="nb">format</span><span class="p">.</span><span class="nf">html</span> <span class="c1"># index.html.erb</span>
<span class="nb">format</span><span class="p">.</span><span class="nf">xml</span> <span class="p">{</span> <span class="n">render</span> <span class="ss">xml: </span><span class="vi">@users</span> <span class="p">}</span>
<span class="nb">format</span><span class="p">.</span><span class="nf">json</span> <span class="p">{</span> <span class="n">render</span> <span class="ss">json: </span><span class="vi">@users</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class UsersController < ApplicationController
def index
@users = User.all
respond_to do |format|
format.html # index.html.erb
format.xml { render xml: @users }
format.json { render json: @users }
end
end
end
">Copy</button>
</div>
<p>Puedes notar en el código anterior que estamos usando <code>render xml: @users</code>, no <code>render xml: @users.to_xml</code>. Si el objeto no es una cadena, Rails invocará automáticamente <code>to_xml</code> por nosotros.</p><p>Puedes aprender más sobre el renderizado en la <a href="layouts_and_rendering.html">Guía de Layouts y Renderizado</a>.</p><h2 id="callbacks-de-acción"><a class="anchorlink" href="#callbacks-de-acción"><span>8</span> Callbacks de Acción</a></h2><p>Los callbacks de acción son métodos que se ejecutan "antes", "después" o "alrededor" de una acción del controlador.</p><p>Los callbacks de acción se heredan, por lo que si configuras uno en un <code>ApplicationController</code>, se ejecutará en cada controlador de tu aplicación.</p><p>Los callbacks de acción "antes" se registran a través de <a href="https://edgeapi.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-before_action"><code>before_action</code></a>. Pueden detener el ciclo de solicitud. Un callback de acción "antes" común es uno que requiere que un usuario esté registrado para que una acción se ejecute. Puedes definir el método de esta manera:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o"><</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
<span class="n">before_action</span> <span class="ss">:require_login</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">require_login</span>
<span class="k">unless</span> <span class="n">logged_in?</span>
<span class="n">flash</span><span class="p">[</span><span class="ss">:error</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"Debes estar registrado para acceder a esta sección"</span>
<span class="n">redirect_to</span> <span class="n">new_login_url</span> <span class="c1"># detiene el ciclo de solicitud</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class ApplicationController < ActionController::Base
before_action :require_login
private
def require_login
unless logged_in?
flash[:error] = "Debes estar registrado para acceder a esta sección"
redirect_to new_login_url # detiene el ciclo de solicitud
end
end
end
">Copy</button>
</div>
<p>El método simplemente almacena un mensaje de error en el flash y redirige al formulario de inicio de sesión si el usuario no está registrado. Si un callback de acción "antes" renderiza o redirige, la acción del controlador no se ejecutará. Si hay callbacks de acción adicionales programados para ejecutarse después de ese, también se cancelan.</p><p>En este ejemplo, el callback de acción se agrega a <code>ApplicationController</code> y, por lo tanto, todos los controladores en la aplicación lo heredan. Esto hará que todo en la aplicación requiera que el usuario esté registrado para usarlo. Por razones obvias (¡el usuario no podría iniciar sesión en primer lugar!), no todos los controladores o acciones deberían requerir esto. Puedes evitar que este callback de acción se ejecute antes de acciones particulares con <a href="https://edgeapi.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-skip_before_action"><code>skip_before_action</code></a>:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">LoginsController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
<span class="n">skip_before_action</span> <span class="ss">:require_login</span><span class="p">,</span> <span class="ss">only: </span><span class="p">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:create</span><span class="p">]</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class LoginsController < ApplicationController
skip_before_action :require_login, only: [:new, :create]
end
">Copy</button>
</div>
<p>Ahora, las acciones <code>new</code> y <code>create</code> de <code>LoginsController</code> funcionarán como antes sin requerir que el usuario esté registrado. La opción <code>:only</code> se usa para omitir este callback de acción solo para estas acciones, y también hay una opción <code>:except</code> que funciona al revés. Estas opciones también se pueden usar al agregar callbacks de acción, por lo que puedes agregar un callback que solo se ejecute para acciones seleccionadas en primer lugar.</p><p>NOTA: Llamar al mismo callback de acción varias veces con diferentes opciones no funcionará,
ya que la última definición del callback de acción sobrescribirá las anteriores.</p><h3 id="callbacks-de-acción-después-y-alrededor"><a class="anchorlink" href="#callbacks-de-acción-después-y-alrededor"><span>8.1</span> Callbacks de Acción Después y Alrededor</a></h3><p>Además del callback de acción "antes", también puedes ejecutar callbacks de acción después de que se haya ejecutado una acción del controlador, o tanto antes como después.</p><p>Los callbacks de acción "después" se registran a través de <a href="https://edgeapi.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-after_action"><code>after_action</code></a>. Son similares a los callbacks de acción "antes", pero debido a que la acción del controlador ya se ha ejecutado, tienen acceso a los datos de respuesta que están a punto de ser enviados al cliente. Obviamente, los callbacks de acción "después" no pueden detener la ejecución de la acción. Ten en cuenta que los callbacks de acción "después" se ejecutan solo después de una acción de controlador exitosa, pero no cuando se produce una excepción en el ciclo de solicitud.</p><p>Los callbacks de acción "alrededor" se registran a través de <a href="https://edgeapi.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-around_action"><code>around_action</code></a>. Son responsables de ejecutar sus acciones asociadas al ceder, similar a cómo funcionan los middlewares de Rack.</p><p>Por ejemplo, en un sitio web donde los cambios tienen un flujo de trabajo de aprobación, un administrador podría previsualizarlos fácilmente aplicándolos dentro de una transacción:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">ChangesController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
<span class="n">around_action</span> <span class="ss">:wrap_in_transaction</span><span class="p">,</span> <span class="ss">only: :show</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">wrap_in_transaction</span>
<span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">transaction</span> <span class="k">do</span>
<span class="k">begin</span>
<span class="k">yield</span>
<span class="k">ensure</span>
<span class="k">raise</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Rollback</span>
<span class="k">end</span>
<span class="k">end</span>