As restrições de segurança devem fazer com que um serviço retorne nulo ou gere uma exceção? [fechadas]

11

Estou um pouco em desacordo com um desenvolvedor mais experiente sobre esse assunto e me perguntando o que os outros pensam sobre isso; nosso ambiente é Java, EJB 3, serviços etc.

O código que escrevi chama um serviço para obter e criar coisas. O problema que encontrei foi que recebi exceções de ponteiro nulo que não faziam sentido. Por exemplo, quando solicito ao serviço para criar um objeto, recebo nulo de volta; quando tento procurar um objeto com um ID válido conhecido, recebo nulo de volta. Passei algum tempo tentando descobrir o que havia de errado no meu código; como sou menos experiente, geralmente assumo que fiz algo errado, mas o motivo dos retornos nulos era a segurança. Se o principal de usuário que usa meu serviço não tiver as permissões corretas para o serviço de destino, ele simplesmente retornará nulo. A maioria dos outros serviços aqui também não está muito bem documentada, portanto, aparentemente, isso é apenas algo que você precisa saber.

Isso é um pouco confuso, pois um desenvolvedor escreve um código que interage com o serviço. Faria muito mais sentido para mim se o serviço apresentasse uma exceção que me dissesse que o usuário não tinha as permissões adequadas para carregar ou criar essa coisa; Eu saberia imediatamente por que meu serviço não estava funcionando conforme o esperado.

O desenvolvedor mais experiente que criou o serviço argumentou que solicitar os dados não é uma condição de erro e que exceções devem ser lançadas apenas em uma condição de erro, não quando o usuário não tiver acesso aos dados. Esses dados são frequentemente pesquisados ​​em uma GUI e, para aqueles usuários sem as permissões corretas, essas coisas simplesmente "não existem". Então, resumindo: perguntar não é errado, portanto não há exceção. Os métodos Get retornam nulos porque para esses usuários essas coisas "não existem". Os métodos Create retornam nulo quando o usuário não tem permissão para criar essa coisa.

Isso é normal e / ou boa prática? Eu prefiro usar exceções porque acho muito mais fácil saber o que está acontecendo. Por isso, preferiria, por exemplo, também lançar uma NotFoundException se você solicitasse um objeto com um ID inválido, em vez de retornar nulo.

Svish
fonte
@ ChrisF: 1) é sobre pessoas que evitam referências nulas, o que eu não faço. Em muitos casos, eles são apropriados, simplesmente não acho que estejam nesse. 2) trata-se de verificar parâmetros, e um resultado afirmativo não seria o mesmo que lançar uma exceção? 3) exceções versus códigos de erro também é uma questão diferente, pois são as duas maneiras de "mostrar" o que deu errado. Os códigos de erro são adequados se, por exemplo, você precisar notificar um sistema que não oferece suporte a exceções ou se não desejar que o usuário final veja algo além de um código.
Svish
A questão que pergunto aqui é sobre "fingir que algo não existe ou não aconteceu" vs "dizer por que algo não existe ou não aconteceu".
Svish
3
Não sugeri que fossem duplicatas - apenas para que pudessem ter informações úteis para você.
ChrisF
Verdade, desculpe por isso! Tomou isso como um comentário de "possíveis duplicatas" por algum motivo ... provavelmente por falta de sono, hehe.
Svish

Respostas:

20

Exceções só devem ser lançadas quando houver um erro e pedir algo não é um erro.

Pedir uma coisa pode não ser um erro, mas não ter permissões para algo que você pediu é certamente algum tipo de erro. As exceções são uma maneira alternativa de relatar condições excepcionais, a serem usadas em vez de valores de retorno especiais (como null, como você escreveu, não ajuda em nada, se houver> 1 razões possíveis para que as coisas dêem errado (mesmo que exista) exatamente 1 motivo possível agora, pode haver 2 motivos possíveis na próxima versão; portanto, usar nullcomo valor de retorno uma indicação de falha seria um problema). Se o serviço não relatar de forma alguma por que não fará o que você pediu, é definitivamente um design ruim.

A utilização de um valor de retorno especial (por exemplo, números inteiros negativos são úteis para funções que normalmente retornam um número inteiro não negativo), uma exceção, um manipulador de erros global ou criador de logs etc., é um detalhe de implementação no final. A escolha depende do idioma, da situação, das convenções e também é uma questão de gosto. O ponto principal é que deve haver alguma maneira direta de descobrir por que algo não funciona. Caso contrário, sua única opção é vasculhar opções diferentes e descobrir o que se correlaciona com o comportamento da caixa preta, e isso é perda de tempo.

String.substring()lança um IndexOutOfBoundsExceptionse o índice estiver fora dos limites. Não consigo pensar em nenhuma vantagem em retornar null, embora - filosoficamente - alguém possa argumentar que a Stringnão contém nada fora de seus limites, então nullseria um valor de retorno lógico na época. Ser lógico-filosófico e ser prático são obviamente dois animais diferentes.

Joonas Pulakka
fonte
Se substring () retornar um valor para índices fora dos limites, ele deverá retornar a parte da sequência entre os índices, possivelmente vazia. Isso poderia salvar um teste em algum código de chamada.
kevin Cline
2
Sim, existe esse "valor vazio" interminável versus discussão nula: a substring para índices fora de limite deve ser nula ou uma sequência vazia? Não sei, ambos são efetivamente "nada" (bem, talvez nulo seja "mais nada" do que uma string vazia?), Mas nenhum deles fornece a quantidade de informações que uma exceção faz.
Joonas Pulakka
@kevincline: A menos que alguém esteja usando uma estrutura em que uma referência nula seja um meio idiomático de indicar uma string vazia, não vejo base para substringretornar uma referência nula. IMHO, deve haver duas funções separadas - uma das quais promete retornar o número solicitado de caracteres ou lançar uma exceção, e uma das quais retorna o máximo possível. Cada método seria substancialmente superior ao outro em alguns contextos.
Supercat 8/13
@ supercat: Eu não disse nada sobre retornar nulo. Eu disse 'devolva ... a parte ... possivelmente vazia'. Uma cadeia vazia não é nula, exceto para Oracle.
Kevin cline
@JoonasPulakka: Eu deveria ter feito ping no meu comentário anterior.
Supercat
5

Tudo se resume ao que é o contrato. Se o seu contrato diz que você pode solicitar algo que não existe, ele deve dizer o que acontece (objeto nulo ou nulo).

Por outro lado, se o seu contrato diz que você deve chamar um método diferente primeiro ( DoesSomethingExist()) e depois chamar o Getmétodo, então o contrato pode dizer que você só pode obter as coisas que existem e lançar uma exceção se não o fizerem. A mensagem de exceção pode dizer algo como "Certifique-se de ligar DoesSomethingExist()primeiro", que é uma mensagem de erro útil.

Scott Whitlock
fonte
Nesse caso, eu diria que não é normal pedir algo que não existe, pois você provavelmente não pediria um ID que não existe. Normalmente, você primeiro obteria uma lista de coisas e depois procuraria uma específica para obter mais detalhes. Portanto, se houvesse um findAllThingsmétodo, eu diria que é apropriado retornar uma lista ou um subconjunto vazio se houver algumas ou todas as coisas que você não tem permissão para ver. Da mesma forma, em um blog, posso listar apenas as postagens para edição pertencentes ao usuário atual. Mas se esse usuário, em seguida, tentou ajustar o url e editar um post diferente ...
Svish
4

Não existe uma resposta única para a pergunta. A utilização de exceções ou o retorno nulo depende da situação.

Em vez de olhar isso puramente do ponto de vista dogmático, observe a interface como uma interface de usuário . As interfaces do usuário devem ser utilizáveis . Portanto, independentemente de suas próprias opiniões sobre a maneira "certa" de fazer algo, escolha o método que é mais utilizável da perspectiva de alguém que usa sua interface.

Se o chamador precisar saber por que um objeto não está sendo retornado, uma exceção é apropriada. Se, no entanto, o conceito geral for "se você não tem permissão, ela não existe", o valor nulo é aceitável.

Especificamente no seu caso, eu diria que os nulos são perfeitamente aceitáveis ​​para procurar um item se o chamador for primeiro solicitado a fazer login ou conectar-se ao servidor. É quando você é informado de que tem ou não permissão. Depois de passar pelo portão, é razoável que, ao procurar algo que você não tem permissão para ver (ou mesmo saiba que existe), você deve obter um nulo.

Se, por outro lado, você não tiver uma etapa inicial de conexão ou login, uma exceção fará sentido. Se o chamador sabe que um item existe e não o recupera, a API não está sendo útil para retornar apenas um nulo.

Para criar um item, no entanto, é uma história diferente. Presumivelmente, pode haver várias razões para isso falhar - sem permissões, parâmetros incorretos, servidor sem memória, etc. Se o desenvolvedor receber um nulo para todas essas condições, ele não terá como determinar um curso de ação adequado. .

Portanto, para responder à pergunta, pergunte-se qual é a solução mais utilizável do ponto de vista de alguém que usa o serviço. Pode ser que o modo como você o esteja usando seja atípico e, no caso mais comum, um nulo seja a resposta certa.

Portanto, não entre em uma guerra religiosa por causa disso, decida o que é certo para esse problema em particular.

Bryan Oakley
fonte
Não planejando colocar equipamento de batalha sobre isso, hehe. Só estava curioso para saber se eu estava completamente errado em pensar no que estava pensando ou não. Eu quero aprender, mas não gosto de pular para algo apenas porque uma pessoa diz isso. E especialmente não se for contra minha própria experiência e lógica (não importa quão pequena). De qualquer forma, concordo totalmente que, se essa fosse uma interface de usuário final, isso poderia realmente fazer sentido. No entanto, como esse é um bean que, até onde eu sei, só pode ser acessado por código, eu diria que eu, como desenvolvedor, sou o usuário.
Svish
1
E como desenvolvedor, eu me preocupo com o motivo de algo estar errado, independentemente do que o usuário final deva saber sobre ele.
Svish
Concordo plenamente com Bryan. Até os desenvolvedores são usuários, ou seja, de consumir outros códigos de desenvolvedores. Decisões de lançamento ou nulo são um tipo de interface / GUI que deve ser útil para a tarefa específica. Não apenas pelo termo geral de lógica ou filosofia.
Independente
3

Eu definitivamente acho que é um design ruim . Ponteiro nulo não é uma resposta válida para mim e pode ser complicado de gerenciar.

Se o usuário tentar conectar um serviço sem a credencial adequada, devo responder com uma resposta clara e concisa. A resposta pode ser uma exceção ou um objeto especial, mas não nulo.

Você deve deixar a resposta nula quando o link de rede estiver quebrado ou outro mau funcionamento crítico inesperado.

Além disso, o tempo que você gasta para entender por que você obteve um nulo é um argumento forte. Qualquer parte do código deve ser fácil de entender e usar. Ter um valor nulo não é o caso.

Amina
fonte
Na sua última frase, você quer dizer "Você deve retornar nulo somente quando o link de rede estiver quebrado etc." ou "Você deve parar de retornar nulo quando o link de rede estiver quebrado etc." ?
Péter Török
@ Péter Török: Eu não recomendo o uso explícito de "return null;". No entanto, quando ocorre uma falha grave, é aceitável que algum serviço retorne nulo.
Amine
Uma exceção não seria mais apropriada se ocorresse um mau funcionamento crítico inesperado?
Svish
2
@Amine: Eu diria que esse é o caso menos apropriado para o qual retornar nulo.
precisa
@Ishish: se você pode detectar a grande avaria, é claro que uma exceção será ótima.
Amine
3

Tendo em mente um bom design de software, pense na vida do seu projeto.
Ao retornar null, como já foi dito, você não fornece nenhuma informação ao cliente. Veja o que aconteceu com você, a princípio você não percebeu onde estava o problema. Além disso, se nenhuma documentação for fornecida, isso é uma bagunça.

Ao lançar uma exceção, você pode dizer o que deu errado. Você pode até personalizar o texto exibido para ser mais específico, se desejar.

Por outro lado, ao retornar, nullvocê faz o cliente investigar o que está acontecendo.

Além disso, você percebeu que havia um problema porque havia outro lugar a NullPointerException. Agora imagine que você não armazena o retorno dessa função ... Você será forçado a cercar cada chamada dessa função com um if elsebloco ...

Do meu ponto de vista, retornar nullé uma prática ruim.

eversor
fonte
2

O desenvolvedor mais experiente que criou o serviço argumentou que solicitar os dados não é uma condição de erro e que exceções devem ser lançadas apenas em uma condição de erro, não quando o usuário não tiver acesso aos dados.

Do meu ponto de vista, isso não faz sentido.

A consulta de dados sem permissão deve resultar em uma exceção, porque é um resultado inesperado - sem resultado - sem acesso adequado.

Analogia : o código de resposta para um GETsem autorização não está 200 oksem dados, na verdade é 401 Unauthorized.

Thomas Junk
fonte
1

Retornar nulo não é bom. Isso não diz nada. Para dar o seu exemplo, se um aplicativo está tentando procurar algo e a resposta é nula para os dois problemas de segurança e se o item não existe, como você sabe a diferença entre os dois?

Uma resposta significativa pode permitir que o aplicativo faça escolhas lógicas sobre o que fazer em seguida ou como resolver problemas.

Qwerky
fonte
Você pergunta como você diz a diferença entre um objeto que não existe e lhe falta permissão para vê-lo. Talvez o design do sistema exija que você não saiba. Acho que não temos informações suficientes para responder nesse caso específico e não há resposta que funcione em todos os casos. Existem casos de uso perfeitamente válidos em que, se você não pode vê-lo, ele efetivamente não existe. E há casos de uso em que, se você não pode vê-lo, deve receber uma exceção para dizer por que não pode.
Bryan Oakley #
O que você quer dizer com "o design do sistema exige que você não saiba"? Que tipo de design seria esse?
Svish
2
Se a política de segurança disser que você não tem permissão para vê-la, na maioria dos casos, também não deverá saber que ela existe. Isso torna o retorno "não encontrado" perfeitamente aceitável.
Blrfl
1

Se a causa raiz for um usuário não autenticado, prefiro uma resposta HTTP 401 rígida, talvez com um redirecionamento para uma página de mensagem bonita (e uma notificação para alguém que se importa). As causas relacionadas à autorização são mais caso a caso; existem argumentos para retornar erros HTTP (403, por exemplo) e argumentos para retornar códigos / mensagens especiais bem formados na resposta do serviço.

Exceções devem ser usadas apenas em circunstâncias excepcionais e significam que algo deu errado. Eu discordo que eles devem estar sobrecarregados para significar "não permitido". (O mesmo vale para o valor de retorno nulo: discordo que ele deva ser usado para significar "não permitido".)

Marca
fonte
Bem, na GUI, você poderia fazer isso, mas quanto ao bean implementando o serviço que faz as coisas nos bastidores, você só tem exceções e valores de retorno especiais à sua disposição. É claro que não quero dizer que o usuário receba a exceção, mas a exceção pode, por exemplo, ser capturada no serviço da web / servlet / o que for e depois fazer o que for apropriado. Redirecione para algum lugar, página http 4xx rígida ou outra coisa.
Svish
Bom ponto, mas você pode empregar um paradigma como AOP para capturar esses casos. O Aspect pode testar o privilégio de chamar uma determinada operação e redirecionar / permitir que o processamento continue conforme apropriado.
Mark
0

Para fazer o papel de advogado do diabo, tentarei tomar o lado de retornar nulo.

Na minha opinião, há apenas uma situação em que esse tipo de resposta é quase aceitável e é, como neste caso, quando a segurança está envolvida.

É muito fácil divulgar acidentalmente detalhes do seu sistema que pessoas não autorizadas não devem conhecer. Isso deve ser evitado.

Pegue, por exemplo, um verificador de nome de usuário e senha. Retornar um erro "Nome de usuário não encontrado" e um erro "Senha incorreta" como códigos de erro separados obviamente abriria você para sérios problemas de segurança, já que alguém poderia primeiro procurar um nome de usuário válido e depois procurar uma senha. Retornar um "nome de usuário / senha inválido" genérico seria uma opção melhor.

Assim, nesse caso em particular, eu diria que está quase certo retornar, nullmas concordo que seria muito desagradável trabalhar com isso.

OldCurmudgeon
fonte
Sim, eu discordo. A resposta adequada seria, na minha opinião, uma exceção, mas a mesma para o nome de usuário não encontrado e a senha incorreta. FailedToLogin, InvalidCredentials, qualquer que seja.
Svish
@Svish - Mas se você está tentando não dar nenhuma dica sobre o motivo da falha, o retorno nullé exatamente a resposta menos útil. Quase qualquer outra coisa poderia ser usada para descobrir algo sobre o sistema. A razão pela qual a OP reclamou foi porque o nullretorno não apenas explicou nada, mas também foi inútil. Esse é o objetivo deliberado ... não dizer absolutamente nada e ser inútil. Missão cumprida em minha opinião.
OldCurmudgeon
Bem, pode ser que sim, mas eu ainda diria que fazer isso é ruim. Pelo menos dentro de um sistema. Nas bordas em que o usuário final vê as coisas, então eu posso concordar. Mas, dentro do sistema, nós desenvolvedores somos os usuários, e por que no mundo queremos tornar as coisas mais confusas para nós mesmos? Como se o desenvolvimento de um software já não fosse difícil o suficiente ...
Svish
1
E com o exemplo de login, eu diria que é ruim para a experiência do usuário não dizer pelo menos algo sobre por que nada pareceu acontecer quando eles (pensaram que) inseriram suas credenciais. Se a página for atualizada e parecer que ignorou minha tentativa de logon, assumirei que algo está errado com o sistema, não que o que eu inseri esteja errado.
Svish
-2

Eu acho que o retorno nulo é hostil, quando o cliente invoca esse método e retorna nulo, o cliente não sabe o que aconteceu e por que retornou nulo. Portanto, devemos executar a execução, o que faz sentido e fornecer uma documentação para descrever essa execução.

Mark xie
fonte