Verificare se la risorsa esiste prima di inserirlo nel database o attendere fino a quando il DAO non lancia l'Eccezione?

2

Ho due percorsi:

  • /students
  • /students/{id}/addresses

... con il seguente comportamento:

POST in /students - 201 Created (se lo studente è stato creato con successo) POST in /students/{id}/addresses - 201 Created (se è stato creato correttamente l'indirizzo sotto l'utente richiesto) Ora immagina una situazione in cui il cliente POST a /students/12/addresses , ma non c'è Studente con id = 12.

Come devo gestire questo scenario?

Devo inserire senza alcun controllo e attendere che DAO lanci le eccezioni o, prima di inserirmi, dovrei controllare l'esistenza dello studente?

Prima opzione :

StudentController.java

@Autowired
private AddressService addressService;

@PostMapping("/students/{id}/addresses")
public ResponseEntity<?> handleRequestOfCreateAddressToStudent(@PathVariable("id") Long studentId, @RequestBody Address address) {
    address.setStudent(new Student(studentId));
    Address createdAddress = addressService.saveAddress(address);

    URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").build(createdAddress.getId());
    return ResponseEntity.created(location).build();
}

AddressService.java

@Autowired
private AddressRepository addressRepository;

public Address saveAddress(Address address) {
    // omitted for brevity...
    return addressRepository.save(address);
}

In questo modo, dal momento che sto usando il framework Spring, la riga return addressRepository.save(address); genererebbe un DataIntegrityViolationException , perché il vincolo è violato (non c'è Studente con tale ID da legarsi con quell'indirizzo). E, poiché ho bisogno di inviare la risposta al client, dovrei tradurre l'eccezione che viene lanciata.

2nd opzione:

StudentController.java

@Autowired
private StudentService studentService;

@Autowired
private AddressService addressService;

@PostMapping("/students/{id}/addresses")
public ResponseEntity<?> handleRequestOfCreateAddressToStudent(@PathVariable("id") Long studentId, @RequestBody Address address) throws StudentNotFoundException {
    Student student = studentService.findById(studentId);

    if(student == null)
        throw new StudentNotFoundException("message");

    address.setStudent(new Student(studentId));
    Address createdAddress = addressService.saveAddress(address);

    URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").build(createdAddress.getId());
    return ResponseEntity.created(location).build();
}

In questo modo, non ho bisogno di tradurre l'Eccezione, perché so già che lo Studente non esiste così da poter generare un'eccezione personalizzata.

I miei pensieri in entrambi gli approcci:

  1. Senza controllo, riduco il codice e il numero di volte attiva il mio database;
  2. Controllo, so esattamente perché non riesco ad aggiungere l'indirizzo (il Lo studente non esiste) e può lanciare un'eccezione personalizzata senza dovendo tradurlo;

Qual è l'approccio migliore per questo scenario?

Devo aspettare che il DAO lanci l'Eccezione, o dovrei impedirgli di lanciare controllando se lo Studente esiste?

    
posta Matheus Cirillo 17.12.2018 - 17:01
fonte

4 risposte

0

Non dovresti mai usare le eccezioni come dispositivo di controllo del flusso. Verifica innanzitutto l'esistenza dello Studente, quindi aggiungi l'indirizzo. Come hai notato, questo ti dà più controllo sulla messaggistica che invii, inoltre mantieni tutta la logica nello stesso flusso (invece che restituire i messaggi sia dal blocco principale che dal gestore di eccezioni).

    
risposta data 17.12.2018 - 18:00
fonte
4

Può certamente essere una preoccupazione valida ridurre la latenza e la domanda sul back-end per motivi di prestazioni. Ma questo è in contrasto con la correttezza:

TOCTOU, completamente scritto come Time-Of-Check-Time-Of-Use, è un errore molto noto derivante dal pensare in un modello rigorosamente lineare (come il multi-threading cooperativo in Windows 3.11), mentre in realtà è indipendente , simultanee, azioni non sincronizzate.

Bene, a meno che tu non supporti anche l'eliminazione degli studenti, almeno se hai trovato lo studente, sarà ancora lì dopo. Puoi garantirlo? Inoltre, che non cambierà mai? Basta andare per l'approccio più performante e sempre corretto.

Alcuni dicono che le eccezioni sono un maiale da prestazione e, a seconda della lingua e dell'implementazione, potrebbero non essere sbagliati. Misuralo , e se questo è il caso, i codici di errore sono anche un'opzione. C'è un motivo per cui molte librerie presentano varie funzioni TryXYZ() oltre a ParseXYZ() , in quanto l'errore potrebbe essere comune.

    
risposta data 17.12.2018 - 17:24
fonte
0

Questa eccezione aziendale dovrebbe essere generata dal tuo livello di servizio. Il tuo livello di persistenza dovrebbe generare un'eccezione di dati. Quindi, nel tuo controller, traduci solo quell'eccezione aziendale in una risposta HTTP.

Questo è un caso eccezionale (chi sta richiedendo all'API REST di uno studente non esistente?) quindi la gestione dovrebbe essere tale.

    
risposta data 17.12.2018 - 22:47
fonte
0

Se vogliamo essere severi nei confronti di HTTP, restituirò un HTTP 404 Not found.

Poiché l'URL non punta a una posizione esistente, non è possibile trovarlo, quindi 404.

Per il tuo livello di servizio se vuoi una gestione dettagliata delle eccezioni, dovresti avere una StudentNotFoundException (o eventualmente una EntityNotFoundException più generica) che tradurrai in 404. Per l'implementazione, se provi a cercare uno studente prima di inserire cercare di inserire direttamente nel tavolo non importa molto. A meno che tu non abbia intenzione di avere centinaia di migliaia di tentativi di inserimento. Se hai bisogno di un classico "dato di inizializzazione dell'importazione", probabilmente avrai bisogno di un codice appropriato solo per gestire quel caso specifico, senza che il tuo servizio principale debba preoccuparsi di quel caso.

Notare che qui l'uso di Exception non è il controllo di flusso, è davvero un'eccezione che porta a rispondere a un errore standard.

    
risposta data 18.12.2018 - 11:32
fonte

Leggi altre domande sui tag