Modello è un posto migliore per impostare il codice di stato HTTP?

5

In MVC, di solito, il controller imposta qualsiasi richiesta da rinviare al client / vista, incluso il codice di stato HTTP, ad esempio:

class Controller {

    public function get(Request req, Response resp) {

        if (this.model.get(req.getId()) {
            resp.setStatusCode(Response::HTTP_OK)

        } else {
            resp.setStatusCode(Response::HTTP_NOT_FOUND)
        }
    }
}

Ci sono alcuni problemi nel farlo in questo modo:

  • tutti i metodi del controller vengono divisi con if / else per l'impostazione di un diverso codice di stato HTTP
  • Ho riscontrato un problema in cui qualcuno ha un Model :: createOrGet (), che restituisce semplicemente Model. Il controller non sa se è stato creato o recuperato dal negozio.
  • l'impostazione del codice del codice di stato HTTP viene duplicata tra i controller.

Sarebbe meglio far passare il codice di stato HTTP al controller, ad esempio:

class Controller {

    public function get(Request req, Response resp) {
        cart := this.model.getOrCreate(req.getCustomerId()
        resp.setStatusCode(this.model.getStatusCode())

        if (cart) {
            return cart
        }
    }

    public function create(Request req, Response resp) {
        cart := this.model.getOrCreate(req.getCustomerId()
        resp.setStatusCode(this.model.getStatusCode())

        if (cart) {
            return cart
        }
    }

    public function put(Customer c, array data) {
        // this can result different success/error status codes
        this.model.processPutDataForCustomer(c, data)
        resp.setStatusCode(this.model.getStatusCode())
    }
}

Fondamentalmente this.model imposta l'ultimo stato sul codice di stato HTTP appropriato che può essere uno dei codici di errore di successo, errore client o server e il controller può ottenere solo il codice di stato.

È buono o esiste un modo migliore per questo problema comune? Non è logico che l'applicazione web conosca il codice di stato HTTP più profondo del controller?

Mi piacerebbe molto vedere l'esempio di più codice di stato HTTP che può essere restituito dal controller, qualcosa come una richiesta PUT che può essere una di "Richiesta non valida", "Conflitto", "Non trovato", "Creato "e" Nessun contenuto ".

    
posta imel96 14.03.2016 - 12:57
fonte

3 risposte

11

Il Controller è responsabile per la gestione della richiesta HTTP e sapendo cosa fare. L'impostazione del codice di stato HTTP nel Controller è il posto giusto per farlo.

There are a few problems in doing it this way:

  • all controller methods become littered with if/else for setting different HTTP status code

Questo è il posto dove questo codice era destinato.

  • I've encountered a problem where someone has a Model::createOrGet(), which just return Model. The controller doesn't know if one has been created or fetch from store.

Se il controller deve sapere se è stato creato qualcosa, il controller non deve chiamare Model::getOrCreate(...) .

  • setting HTTP status code code becomes duplicated between controllers.

Se il modello imposta il codice di stato HTTP, allora è duplicato tra i modelli, quindi questo non ti compra davvero nulla.

Questo prossimo suggerimento dipenderà in gran parte dal framework che stai utilizzando, ma la maggior parte dei framework ha come impostazione predefinita una risposta 200 OK , lasciando solo i casi negativi ( 404 NOT FOUND , ecc.).

Ma ancora una volta, questo è precisamente ciò che il Controller deve fare.

Sì, sembra che tu stia duplicando il codice, ma sei non che duplica la logica aziendale. Questo è il tipo di duplicazione che vuoi evitare. Duplicare i codici di stato HTTP non è proprio ciò che chiamerei "duplicazione". Fa parte del lavoro del Controller.

imel96 ha commentato:

I know setting some status code is Controller's job. But a lot of HTTP status codes are part of business logic and business logic should go in Model, right?

I codici di stato HTTP sono solo una parte di Business Logic se l'applicazione che stai scrivendo è un server web. In caso contrario, un codice di stato HTTP è un artefatto dell'applicazione che viene implementata come applicazione web. Immagina se avessi bisogno di prendere la tua logica aziendale e riutilizzarla nel contesto di un'applicazione GUI installata. Quindi i codici di stato HTTP hanno senso? Non lo fanno. Un codice di stato HTTP non è una logica aziendale. È il controllo del flusso delle applicazioni. Il controller è responsabile del controllo del flusso.

La logica aziendale è Perché e il tuo controller dovrebbe sapere Cosa fare quando succede qualcosa. I controllori non sanno Perché succede qualcosa, perché è responsabilità di Business Logic.

I also find some Model throwing exceptions with HTTP status code in it and then Controller copy that to Response object. That also indicates to me that Model really wants to return HTTP status code, and without exception (like when using Go), it'd be just like returning status code.

Se il modello genera eccezioni contenenti codici di stato HTTP, il modello è stato creato in modo errato. Un'eccezione è una condizione eccezionale che non può essere gestita dal codice chiamato, e quindi deve essere gestita dal chiamante. Validazioni, come entità separata dai Modelli (sebbene i validatori debbano chiamare i metodi sui Modelli durante l'esecuzione della convalida) e i Controllori dovrebbero occuparsi di controllare i dati prima che le eccezioni possano essere lanciate dal modello o dal livello di accesso ai dati

When using many status codes, isn't it too much of business logic to be put in Controller?

No. Ancora una volta, i codici di stato HTTP non hanno nulla a che fare con Business Logic. Il fatto stesso che tu abbia posto questa domanda dovrebbe mostrarti quanto diventa confuso quando confondi due aspetti completamente diversi: Business Logic (Modelli) e Flow Control (Controllers).

    
risposta data 14.03.2016 - 14:26
fonte
1

Il lavoro del controllore è mediare tra il modello e la vista. Indica al modello cosa, in senso astratto, l'utente vuole che faccia e dice alla vista come dovrebbe apparire in base allo stato del modello. È importante sottolineare che non è compito del controllore decidere quale dovrebbe essere lo stato del modello.

La chiamata al server e i dati che restituisce sono parti del modello e devono quindi essere gestiti dal modello. Il controllore non dovrebbe nemmeno dire al modello di fare una richiesta server, tanto meno osservare direttamente il risultato di quella richiesta.

In un sistema ben progettato, il controllore non dovrebbe sapere da dove provengono i dati o cosa deve fare il modello per ottenerlo. Il modello dovrebbe essere libero di cambiare la sua fonte di verità, ad esempio dal server alla cache, senza modifiche nel controller.

In sostanza, sto dicendo che function get(Request req, Response rest) non dovrebbe essere nel controller in primo luogo. Dovrebbe esserci invece una sorta di funzione "userWantsX" nel modello e il modo in cui il modello risponde a quella richiesta per X è una sua attività.

Quindi il lavoro del controllore è di convertire i gesti dell'interfaccia utente (pulsante X utente toccato) in richieste di modello , (l'utente vuole X). Quindi, dopo che il modello ha cambiato lo stato, per convertire le informazioni sullo stato in UI, visualizzare le modifiche dello stato. Questo è tutto che il controller dovrebbe fare.

    
risposta data 14.03.2016 - 14:58
fonte
0

Ti consiglio di utilizzare un sistema di gestione degli errori e / o di validazione adeguato per mitigare la duplicazione del codice e la violazione del principio di responsabilità singola.

Ogni framework MVC ha alcuni hook di gestione degli errori centrali come questi su ASP.NET 6 . Tutto ciò che devi fare è lanciare l'eccezione appropriata, riprendere il meccanismo di gestione degli errori centrale, infine impostare il codice di stato appropriato per la risposta.

Il tuo controllore non dovrebbe eseguire il lavoro di convalida e gestione degli errori.

    
risposta data 14.03.2016 - 16:51
fonte

Leggi altre domande sui tag