Czy ograniczenia bezpieczeństwa powinny spowodować, że usługa zwróci wartość null lub zgłasza wyjątek? [Zamknięte]


11

Nie zgadzam się z bardziej doświadczonym programistą w tej sprawie i zastanawiam się, co sądzą o tym inni; nasze środowisko to Java, EJB 3, usługi itp.

Kod, który napisałem, wywołuje usługę w celu zdobywania rzeczy i tworzenia rzeczy. Problem, na który natrafiłem, polegał na tym, że otrzymałem wyjątki zerowego wskaźnika, które nie miały sensu. Na przykład, kiedy poproszę usługę o utworzenie obiektu, otrzymuję wartość zerową; gdy próbuję wyszukać obiekt o znanym ważnym identyfikatorze, zwracam wartość null. Spędziłem trochę czasu próbując dowiedzieć się, co było nie tak w moim kodzie; ponieważ jestem mniej doświadczony, zwykle zakładam, że zrobiłem coś złego, ale okazuje się, że przyczyną zerowych zwrotów było bezpieczeństwo. Jeśli użytkownik główny korzystający z mojej usługi nie miał odpowiednich uprawnień do usługi docelowej, wówczas po prostu zwraca wartość null. Większość innych usług tutaj również nie jest dobrze udokumentowana, więc najwyraźniej jest to po prostu coś, co musisz wiedzieć.

Jest to dość mylące, ponieważ programista pisze kod, który wchodzi w interakcję z usługą. Byłoby dla mnie o wiele bardziej sensowne, gdyby usługa stanowiła wyjątek, który powiedziałby mi, że użytkownik nie ma odpowiednich uprawnień do załadowania lub utworzenia tej rzeczy; Od razu wiedziałbym, dlaczego moja usługa nie działa zgodnie z oczekiwaniami.

Bardziej doświadczony programista, który napisał usługę, argumentował, że żądanie danych nie jest warunkiem błędu, a wyjątki powinny być zgłaszane tylko w przypadku błędu, a nie wtedy, gdy użytkownik nie ma dostępu do danych. Dane te są często sprawdzane w interfejsie GUI, a dla tych użytkowników bez odpowiednich uprawnień, te rzeczy po prostu „nie istnieją”. Krótko mówiąc: pytanie nie jest złe, stąd nie ma wyjątku. Metody Get zwracają wartość null, ponieważ dla tych użytkowników rzeczy „nie istnieją”. Metody tworzenia zwracają wartość null, gdy użytkownik nie mógł utworzyć tej rzeczy.

Czy to jest normalna i / lub dobra praktyka? Wolę używać wyjątków, ponieważ znacznie łatwiej jest mi wiedzieć, co się dzieje. Wolałbym więc na przykład zgłosić wyjątek NotFoundException, jeśli poprosiłeś o obiekt z niepoprawnym identyfikatorem, zamiast zwracać wartość null.



@ChrisF: 1) opowiada o ludziach unikających zerowych referencji, czego ja nie. W wielu przypadkach są odpowiednie, po prostu nie sądzę, że są w tym jednym. 2) dotyczy sprawdzania parametrów i czy nie zapewniłby tego rodzaju wyniku, co zgłoszenie wyjątku? 3) wyjątki kontra kody błędów to także inna kwestia, ponieważ oba sposoby „pokazują”, co poszło nie tak. Kody błędów są odpowiednie, jeśli na przykład musisz powiadomić system, który nie obsługuje wyjątków lub jeśli nie chcesz, aby użytkownik końcowy widział coś innego niż kod.
Svish,

Problem, o który tu pytam, dotyczy „udawania, że ​​coś nie istnieje lub się nie wydarzyło”, a „wyjaśniania, dlaczego coś nie istnieje lub się nie wydarzyło”.
Svish

3
Nie sugerowałem, że są duplikatami - po prostu mogą mieć dla ciebie przydatne informacje.
ChrisF

Przepraszam za to! Z jakiegoś powodu uznałem to za komentarz „możliwe duplikaty”… prawdopodobnie z powodu braku snu, hehe.
Svish,

Odpowiedzi:


19

Wyjątki należy zgłaszać tylko wtedy, gdy wystąpi błąd, a prośba o coś nie jest błędem.

Pytanie o coś może nie być błędem, ale brak uprawnień do czegoś, o co prosiłeś, to z pewnością jakiś błąd. Wyjątki to alternatywny sposób zgłaszania wyjątkowych warunków, który można zastosować zamiast specjalnych wartości zwracanych (takich jak null, jak napisałeś, absolutnie nie pomoże, jeśli istnieje> 1 możliwy powód, dla którego wszystko może pójść nie tak (nawet jeśli dokładnie 1 możliwy powód, w następnej wersji mogą istnieć 2 możliwe powody, więc użycie nulljako wartości zwracanej wskazującej na niepowodzenie oznaczałoby malowanie się w rogu). Jeśli usługa nie zgłosi w żaden sposób, dlaczego nie zrobi tego, o co prosiłeś, to zdecydowanie jest to zły projekt.

Niezależnie od tego, czy użyć specjalnej wartości zwracanej (np. Ujemne liczby całkowite są przydatne dla funkcji, które zwykle zwracają nieujemną liczbę całkowitą), wyjątek, globalny moduł obsługi błędów lub program rejestrujący itp. Jest szczegółem implementacji na końcu. Wybór zależy od języka, sytuacji, konwencji i jest również kwestią gustu. Najważniejsze jest to, że powinien istnieć jakiś bezpośredni sposób ustalenia, dlaczego coś nie działa. W przeciwnym razie twoją jedyną opcją jest wyrzucanie śmieci z różnymi opcjami i cokolwiek, aby dowiedzieć się, co koreluje z zachowaniem czarnej skrzynki, i to strata czasu.

String.substring()zgłasza i, IndexOutOfBoundsExceptionjeśli indeks jest poza zakresem. Nie mogę wymyślić żadnych korzyści z powrotu null, chociaż - filozoficznie - można argumentować, że a Stringnie zawiera niczego poza swoimi granicami, więc nullbyłaby to logiczna wartość zwracana. Bycie logiczno-filozoficznym i praktycznym to oczywiście dwa różne zwierzęta.


Jeśli substring () zwróci wartość dla indeksów spoza zakresu, powinien zwrócić część ciągu między indeksami, być może pustą. To może zapisać test w jakimś kodzie wywołującym.
kevin cline

2
Tak, istnieje ta niekończąca się „pusta wartość” vs. null -dyskusja: czy podłańcuch dla indeksów spoza zakresu powinien być zerowy, czy pusty ciąg? Nie wiem, oba są w rzeczywistości „niczym” (no może null to „więcej nic” niż pusty ciąg?), Ale żadne z nich nie podaje ilości informacji, które robi wyjątek.
Joonas Pulakka

@kevincline: O ile nie używa się frameworka, w którym odwołanie zerowe jest idiomatycznym sposobem wskazania pustego ciągu, nie widzę podstaw substringdo zwrócenia odwołania zerowego. IMHO powinny być dwie oddzielne funkcje - jedna z nich obiecuje albo zwrócić żądaną liczbę znaków, albo zgłosić wyjątek, a jedna zwraca tyle, ile może. Każda metoda byłaby znacznie lepsza od drugiej w niektórych kontekstach.
supercat

@ supercat: Nie mówiłem nic o zwrocie wartości null. Powiedziałem „wróć… część… prawdopodobnie pusta”. Pusty ciąg znaków nie jest pusty, z wyjątkiem Oracle.
kevin cline

@JoonasPulakka: Powinienem był cię pingować do mojego poprzedniego komentarza.
supercat

5

Wszystko sprowadza się do umowy. Jeśli umowa mówi, że możesz zażądać czegoś, co nie istnieje, powinno zawierać informację o tym, co się stanie (obiekt zerowy lub zerowy).

Z drugiej strony, jeśli umowa mówi, że powinieneś najpierw wywołać inną metodę ( DoesSomethingExist()), a następnie wywołać tę Getmetodę, wówczas umowa może powiedzieć, że możesz uzyskać tylko rzeczy, które istnieją, i jeśli nie, wyrzucić wyjątek. Komunikat wyjątku może brzmieć „ DoesSomethingExist()Najpierw zadzwoń ”, co jest przydatnym komunikatem o błędzie.


W tym przypadku powiedziałbym, że nie jest normalne proszenie o coś, co nie istnieje, ponieważ prawdopodobnie nie poprosiłbyś o identyfikator, który nie istnieje. Zwykle najpierw dostajesz listę rzeczy, a następnie szukasz konkretnej z nich, aby uzyskać więcej szczegółów. Więc jeśli istniała findAllThingsmetoda, powiedziałbym, że właściwe jest zwrócenie pustej listy lub podzbioru, jeśli są pewne lub wszystkie rzeczy, których nie wolno ci widzieć. W ten sam sposób mogę na blogu wyświetlać tylko posty do edycji należące do bieżącego użytkownika. Ale jeśli ten użytkownik następnie spróbuje zmodyfikować adres URL i edytować inny post ...
Svish

4

Nie ma jednej, uniwersalnej odpowiedzi na wszystkie pytania. To, czy użyć wyjątków, czy zwrócić null, zależy od sytuacji.

Zamiast patrzeć na to wyłącznie z dogmatycznego punktu widzenia, spójrz na interfejs jako interfejs użytkownika . Interfejsy użytkownika powinny być użyteczne . Niezależnie od własnych opinii na temat „właściwego” sposobu zrobienia czegoś, wybierz metodę, która jest najbardziej użyteczna z perspektywy osoby korzystającej z Twojego interfejsu.

Jeśli osoba dzwoniąca musi wiedzieć, dlaczego obiekt nie jest zwracany, odpowiedni jest wyjątek. Jeśli jednak ogólna koncepcja brzmi „jeśli nie masz pozwolenia, nie istnieje”, wartość null jest akceptowalna.

Szczególnie w twoim przypadku powiedziałbym, że wartości zerowe są całkowicie akceptowalne do wyszukiwania elementu, jeśli osoba dzwoniąca musi najpierw zalogować się lub połączyć z serwerem. Wtedy powiedziano ci, że masz lub nie masz pozwolenia. Gdy miniesz bramę, uzasadnione jest, że kiedy szukasz czegoś, do czego nie masz pozwolenia (a nawet wiesz, że istnieje), powinieneś otrzymać zero.

Z drugiej strony, jeśli nie masz początkowego połączenia lub kroku logowania, wyjątek ma sens. Jeśli dzwoniący wie, że element istnieje i nie odzyskuje go, interfejs API nie jest pomocny w zwróceniu wartości null.

Jednak tworzenie przedmiotu to inna historia. Prawdopodobnie może być wiele przyczyn niepowodzenia - brak uprawnień, złe parametry, brak pamięci w serwerze itp. Jeśli programista otrzyma wartość null dla wszystkich tych warunków, nie będzie w stanie określić właściwego sposobu działania .

Aby odpowiedzieć na pytanie, zadaj sobie pytanie, jakie jest najbardziej przydatne rozwiązanie z perspektywy osoby korzystającej z usługi. Może się okazać, że droga ty używasz go jest nietypowy, a najczęstszym przypadku zerowy jest prawidłowa odpowiedź.

Więc nie wdawaj się w wojnę religijną, zdecyduj, co jest właściwe dla tego konkretnego problemu.


Nie planuję w tym celu wkładać sprzętu bojowego, hehe. Byłem ciekawy, czy całkowicie się myliłem, myśląc o tym, co myślę, czy nie. Chcę się uczyć, ale nie lubię skakać do czegoś tylko dlatego, że tak mówi jedna osoba. A zwłaszcza nie, jeśli jest to sprzeczne z moim własnym doświadczeniem i logiką (bez względu na to, jak małe). W każdym razie całkowicie się zgadzam, że jeśli byłby to interfejs użytkownika końcowego, to rzeczywiście miałoby to sens. Jednak, ponieważ jest to fasola, który, o ile wiem, można uzyskać tylko za pomocą kodu, chciałbym powiedzieć, że ja, jako deweloper, jest użytkownik.
Svish

1
Jako programista dbam o to, dlaczego coś jest nie tak, niezależnie od tego, co użytkownik końcowy powinien o tym wiedzieć.
Svish

Całkowicie zgadzam się z Bryanem. Nawet deweloperzy są użytkownikami, tzn. Używają kodu innego programisty. Opis rzucenia lub null jest rodzajem interfejsu / gui, które powinny być pomocne w określonym zadaniu. Nie tylko przez ogólny termin logiki lub filozofii.
Niezależny

3

Zdecydowanie uważam, że to zły projekt . Wskaźnik zerowy nie jest dla mnie prawidłową odpowiedzią i może być trudny w zarządzaniu.

Jeśli użytkownik spróbuje połączyć usługę bez odpowiedniego poświadczenia, powinienem odpowiedzieć jasną i zwięzłą odpowiedzią. Odpowiedzią może być wyjątek lub obiekt specjalny, ale nie zero.

Powinieneś pozostawić zerową odpowiedź, gdy połączenie sieciowe zostanie zerwane lub inna nieoczekiwana krytyczna usterka.

Co więcej, czas spędzony na zrozumieniu, dlaczego masz wartość zerową, jest silnym argumentem. Każdy fragment kodu powinien być łatwy do zrozumienia i użycia. Posiadanie wartości zerowej nie jest prawdą.


Czy w ostatnim zdaniu masz na myśli „ Powinieneś zerować tylko wtedy, gdy połączenie sieciowe jest zerwane itp.” lub „ Powinieneś przestać zwracać wartość NULL, gdy połączenie sieciowe zostanie zerwane itp.” ?
Péter Török

@ Péter Török: Nie polecam jawnego użycia „return null;”. Jednak gdy nastąpi poważna awaria, dopuszczalne jest, aby niektóre usługi zwracały wartość null.
Amine,

Czy wyjątek nie byłby bardziej odpowiedni, gdyby nastąpiła nieoczekiwana krytyczna awaria?
Svish

2
@ Amine: Powiedziałbym, że jest to najmniej odpowiedni przypadek, dla którego należy zwrócić wartość null.
Michael Borgwardt

@Svish: jeśli możesz wykryć poważną awarię, oczywiście wyjątek będzie świetny.
Amine,

3

Mając na uwadze dobre projektowanie oprogramowania, powinieneś pomyśleć o życiu swojego projektu.
Zwracając null, jak powiedziano, nie podajesz żadnych informacji klientowi. Spójrz, co ci się przydarzyło, na początku nie zdawałeś sobie sprawy, gdzie jest problem. Co więcej, jeśli nie podano żadnej dokumentacji, jest to bałagan.

Zgłaszając wyjątek, możesz stwierdzić, co poszło nie tak. Możesz nawet dostosować wyświetlany tekst, aby był bardziej szczegółowy, jeśli chcesz.

Z drugiej strony, zwracając null, sprawisz , że klient zbada, co się dzieje.

Ponadto zdałeś sobie sprawę, że był problem, ponieważ dostałeś się gdzie indziej NullPointerException. Teraz wyobraź sobie, że nie przechowujesz zwrotu tej funkcji ... Będziesz zmuszony otoczyć każde wywołanie tej funkcji if elseblokiem ...

Z mojego punktu widzenia powrót nullto zła praktyka.


2

Bardziej doświadczony programista, który napisał usługę, argumentował, że żądanie danych nie jest warunkiem błędu, a wyjątki powinny być zgłaszane tylko w przypadku błędu, a nie wtedy, gdy użytkownik nie ma dostępu do danych.

Z mojego punktu widzenia nie ma to sensu.

Zapytanie o dane bez pozwolenia powinno spowodować wyjątek, ponieważ jest to nieoczekiwany wynik - brak rezultatu - bez odpowiedniego dostępu.

Analogia : kod odpowiedzi dla GETautoryzacji 200 okbez danych nie jest bez danych, tak naprawdę jest 401 Unauthorized.


1

Zwracanie wartości null nie jest świetne. Nic ci nie mówi. Na przykład, jeśli aplikacja próbuje coś wyszukać, a odpowiedź na oba problemy bezpieczeństwa jest zerowa, a jeśli element nie istnieje, to jak odróżnić te dwa elementy?

Znacząca odpowiedź może pozwolić aplikacji na dokonywanie logicznych wyborów dotyczących tego, co robić dalej lub jak rozwiązywać problemy.


Pytasz, jak odróżnić przedmiot, którego nie ma, a brakuje ci pozwolenia na jego zobaczenie. Być może konstrukcja systemu wymaga, abyś nie wiedział. Nie sądzę, abyśmy mieli wystarczającą ilość informacji, aby odpowiedzieć w tym konkretnym przypadku, i nie ma odpowiedzi, która zadziała we wszystkich przypadkach. Istnieją doskonale uzasadnione przypadki użycia, w których, jeśli nie widać, nie ma ich skutecznie. Są też przypadki użycia, w których jeśli go nie widzisz, powinieneś otrzymać wyjątek, aby powiedzieć, dlaczego nie możesz.
Bryan Oakley,

Co rozumiesz przez „projekt systemu, którego nie znasz”? Jaki to może być projekt?
Svish

2
Jeśli polityka bezpieczeństwa mówi, że nie możesz jej zobaczyć, w większości przypadków nie powinieneś wiedzieć, że ona istnieje. To sprawia, że ​​powrót „nie znaleziono” jest całkowicie akceptowalny.
Blrfl

1

Jeśli główną przyczyną jest nieuwierzytelniony użytkownik, wolę twardą odpowiedź HTTP 401, być może z przekierowaniem na ładną stronę z wiadomością (i powiadomieniem dla kogoś, kogo to obchodzi). Przyczyny związane z autoryzacją są bardziej indywidualne; istnieją argumenty za zwracaniem błędów HTTP (powiedzmy 403) i argumenty za zwracaniem specjalnych poprawnie sformułowanych kodów / komunikatów w odpowiedzi usługi.

Wyjątki należy stosować tylko w wyjątkowych okolicznościach i oznaczać, że coś poszło nie tak. Nie zgadzam się, że powinny być przeciążone, co oznacza „niedozwolone”. (To samo dotyczy zerowej wartości zwracanej: Nie zgadzam się, że należy ją stosować w znaczeniu „niedozwolony”).


Cóż, w GUI możesz to zrobić, ale jeśli chodzi o implementację usługi bean, która robi rzeczy za kulisami, masz do dyspozycji tylko wyjątki i specjalne wartości zwrotne. Oczywiście nie mam na myśli tego, że użytkownik powinien otrzymać wyjątek, ale wyjątek może na przykład zostać złapany w serwisie WWW / serwletu / czymkolwiek, a następnie zrobić to, co jest właściwe. Przekieruj gdzieś, twardą stronę http 4xx lub coś innego.
Svish,

Dobra uwaga, ale możesz uchwycić takie paradygmaty, jak AOP, aby uchwycić te przypadki. Aspekt może przetestować uprawnienia do wywołania danej operacji i przekierowania / zezwolenia na kontynuowanie przetwarzania w razie potrzeby.
Mark

0

Aby zagrać w adwokata diabłów, postaram się powrócić do zera.

Moim zdaniem jest tylko jedna sytuacja, w której tego rodzaju reakcja jest prawie do zaakceptowania, i tak jak w tym przypadku, gdy chodzi o bezpieczeństwo.

Bardzo łatwo jest przypadkowo podać szczegóły systemu, o których osoby nieupoważnione nie powinny wiedzieć. Tego należy unikać.

Weźmy na przykład sprawdzanie nazwy użytkownika i hasła. Zwrócenie błędu „Nie znaleziono nazwy użytkownika” i błędu „Nieprawidłowe hasło” jako oddzielnych kodów błędów oczywiście otworzyłoby cię na poważne problemy bezpieczeństwa, ponieważ ktoś mógłby najpierw poszukać prawidłowej nazwy użytkownika, a następnie hasła. Lepszym rozwiązaniem byłoby zwrócenie ogólnego „Nieprawidłowa nazwa użytkownika / hasło”.

Tak więc w tym konkretnym przypadku powiedziałbym, że powrót jest prawie w porządku, nullale zgadzam się, że bardzo nieprzyjemnie byłoby z nim pracować.


Tak, nie zgodziłbym się. Właściwa odpowiedź byłaby moim zdaniem wyjątkiem, ale taka sama zarówno dla nazwy użytkownika, jak i dla niepoprawnego hasła. FailedToLogin, InvalidCredentials, cokolwiek.
Svish

@Svish - Ale jeśli próbujesz w ogóle nie dać żadnych wskazówek co do przyczyny niepowodzenia, to zwrot nulljest dokładnie najmniej pomocną odpowiedzią. Prawie wszystko inne mogłoby potencjalnie zostać wykorzystane do odkrycia czegoś o systemie. Powód narzekał OP, ponieważ nullzwrot nie tylko nie wyjaśnił, że był nawet nieprzydatny. Taki jest celowy cel ... powiedzenie absolutnie nic i bycie niepomocnym. Misja zrealizowana moim zdaniem.
OldCurmudgeon

Cóż, może tak być, ale nadal powiedziałbym, że robienie tego jest złe. Przynajmniej w systemie. Na krawędziach, na których użytkownik końcowy widzi rzeczy, mogę się z tym zgodzić. Ale wewnątrz systemu my, programiści, jesteśmy użytkownikami i dlaczego, na świecie, chcielibyśmy, aby sprawy stały się bardziej mylące dla nas samych? Jakby tworzenie oprogramowania nie było już wystarczająco trudne ...
Svish,

1
I na przykładzie logowania powiedziałbym, że źle jest dla użytkownika nie mówić przynajmniej czegoś o tym, dlaczego wydawało się, że nic się nie stało, gdy (myśleli, że) wprowadzili swoje dane uwierzytelniające. Jeśli strona właśnie się odświeża i wygląda na to, że po prostu zignorowała moją próbę zalogowania, założę, że coś jest nie tak z systemem, a nie to, że wpisałem, że było źle.
Svish

-2

Myślę, że return null jest nieprzyjazny, kiedy klient wywołuje tę metodę i zwraca null, klient nie wie, co się stało i dlaczego zwrócił null. Powinniśmy więc wykonać wykonanie sensowne i dostarczyć dokumentację opisującą wykonanie.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.