Lokalizowanie komunikatów błedów w ASP.NET MVC
Gdy w 2008 czy tam 2009 roku Microsoft wynalazł wzorzec MVC 1, firma ta jako jedną z większych zalet nowego frameworka podawała odróżniającą go od WebFormsów elastyczność i możliwość łatwej konfiguracji. (Pojechanie po innym swoim produkcie to swoją drogą jest świetny chwyt marketingowy.) Zdawałoby się zatem, że lokalizowanie standardowych komunikatów błędów powinno być banalne. No i w sumie jest, gdy już się potrafi to zrobić.
Sposób pierwszy
Najczęściej proponowanym na StackOverflow rozwiązaniem tego problemu jest umieszczenie pliku zasobów z odpowiednimi kluczami w katalogu App_GlobalResources
oraz ustawienie DefaultModelBinder.ResourceClassKey = "Messages";
. To rozwiązanie ma dwie wady:
- Korzysta z
App_GlobalResources
, co jest dość oldskulowe. - Nie działa.
Działało kiedyś, ostatnio chyba w wersji 2 lub 3 ASP.NET MVC, zeznania nie są spójne. W każdym razie odpada.
Sposób drugi
Kolejnym sposobem jest utworzenie własnego atrybutu dziedziczącego ze standardowego atrybutu i nadpisanie w nim komunikatu. Coś w tym stylu:
To pewno działa - nie sprawdzałem, bo nie widzę za bardzo sensu w zmuszaniu użytkowników Pizzy do nauki nowego zestawu atrybutów. Do podstawowych zastosowań te z System.ComponentModel.DataAnnotations
są całkiem niezłe. Jeśli kiedykolwiek stwierdzę, że nie wystarczają, to raczej przejdę na Fluent Validation niż napiszę coś swojego.
Sposób trzeci
Na szczęście w ASP.NET 4.0 wprowadzono nowy twór: RequiredAttributeAdapter
.
Jego podstawowe i polecane użycie wygląda np. tak:
Istnieje cały zestaw takich adapterów dla poszczególnych standardowych atrybutów walidacji:
- MaxLengthAttributeAdapter
- MinLengthAttributeAdapter
- RangeAttributeAdapter
- RegularExpressionAttributeAdapter
- RequiredAttributeAdapter
- StringLengthAttributeAdapter
Tak utworzony adapter trzeba oczywiście gdzieś zarejestrować, np. w Global.asax
albo w metodzie wołanej przez WebActivatora po starcie aplikacji. Kod rejestrujący wygląda tak:
Proste prawda? Aż za bardzo, dlatego nie działa.
Sposób trzeci edycja druga
Problem powstanie, gdy spróbujemy nadpisać ten nasz domyślny, pobrany z zasobów komunikat błędu, podając wartość ErrorMessage
w kodzie modelu:
Po uruchomieniu aplikacja rzuci pięknym wyjątkiem: >Either ErrorMessageString or ErrorMessageResourceName must be set, but not both.
Pochodzi on z klasy System.ComponentModel.DataAnnotations.ValidationAttribute
. Musiałem aż zdebugować ten kod, aby dojść o co chodzi i czemu tak się dzieje. Tak wygląda ten fragment, warto zwrócić uwagę zwłaszcza na komentarze. Wynika z tego, że w dwóch zupełnie róznych sytuacjach zostanie rzucony ten sam wyjątek. Dobre rozwiązanie, w Microsofcie zaoszczędzlii w ten sposób jakieś 50 bajtów na zasobach tekstowych, ktoś pewno dostał solidną premię.
Gdybym to ja projektował tę klasę, wyszedłbym z założenia, że ręcznie wpisany tekst ma nadpisać tekst domyślny, a nie rzucać wyjątkiem. No, ale rozumiem też, że rzucenie wyjątkiem w jakiś tam sposób zabezpiecza programistę przed pomyłką, więc niech już będzie. I tak nie mam na to wpływu, więc nie pozostaje nic innego jak poprawić własny kod. Oto finalna wersja:
Dodatkowym plusem tego rozwiązania jest brak magic stringów.
1 Oczywiście to nie Microsoft wynalazł MVC, ale mało kto o tym wie, a poza tym ja chcę wygrać Xboxa.