Come determinare cosa dovrebbe ottenere il proprio controller rispettivo?

10

Sto usando il pattern MVC nella mia applicazione web costruita con PHP.

Sto sempre cercando di stabilire se ho bisogno di un nuovo controller dedicato per una serie di azioni o se dovrei inserirle in un controller già esistente.

Ci sono buone regole da seguire durante la creazione dei controller?

Ad esempio posso avere:

AuthenticationController con azioni:

  • index() per visualizzare il modulo di accesso.
  • submit() per gestire l'invio del modulo.
  • logout() , autoesplicativo.

o

LoginController con azioni:

  • index() per visualizzare il modulo di accesso.
  • submit() per gestire l'invio del modulo.

LogoutController con azione:

  • index() per gestire la disconnessione.

o

AccountController con azioni:

  • loginGet() per visualizzare il modulo di accesso.
  • loginPost() per gestire l'invio del modulo di accesso.
  • logoutGet() per gestire la disconnessione.
  • registerGet() per visualizzare il modulo di registrazione.
  • registerPost() per gestire l'invio del modulo.

    E tutte le altre azioni che sono coinvolte in un account.

posta Kid Diamond 07.09.2014 - 19:12
fonte

6 risposte

3

Per trovare il giusto raggruppamento per i controllori, pensa al test .

(Anche se in realtà non fai alcun test, pensando a come andresti a testare i tuoi controller ti fornirò alcune informazioni molto utili su come strutturarli.)

Un AuthenticationController non è testabile da solo, perché contiene solo funzionalità per l'accesso e la disconnessione, ma il codice di test dovrà in qualche modo creare account falsi a scopo di test prima di poter verificare un login riuscito. Potresti scavalcare il sottosistema sotto test e andare direttamente al tuo modello per la creazione degli account di test, ma poi avrai un test fragile nelle tue mani: se il modello cambia, dovrai modificare non solo il codice che prova il modello, ma anche il codice che testa il controller, anche se l'interfaccia e il comportamento del controller sono rimasti invariati. È irragionevole.

Un LoginController non è adatto per gli stessi motivi: non puoi testarlo senza creare prima gli account, e ci sono ancora più cose che non puoi testare, come ad esempio prevenire accessi duplicati ma permettere a un utente di accedere dopo aver effettuato l'accesso su. (Poiché questo controller non ha funzionalità di disconnessione.)

Un AccountController ti darà tutto ciò che ti serve per fare il tuo test: puoi creare un account di prova e poi provare ad accedere, puoi eliminare l'account e quindi assicurarti di non poter più accedere, puoi cambiare password e assicurarsi che la password corretta debba essere utilizzata per accedere, ecc.

Per concludere: per scrivere anche la più piccola suite di test, è necessario rendere disponibili tutte le funzionalità di AccountController . La suddivisione in controllori più piccoli sembra offrire controller per disabili con funzionalità insufficienti per un corretto test. Questa è un'ottima indicazione che la funzionalità di AccountController è la suddivisione più piccola che abbia senso.

E in generale, l'approccio "pensa al test" funzionerà non solo in questo particolare scenario, ma in qualsiasi scenario simile ti imbatti in futuro.

    
risposta data 29.04.2015 - 14:53
fonte
1

La risposta non è ovvia

Per favore lasciatemi chiarire alcune cose prima di fare qualsiasi dichiarazione di risposta. Prima di tutto:

Che cos'è il controller?

Controller è una parte del sistema che controlla richiesta - dopo la spedizione. Quindi, possiamo definirlo come un insieme di azioni relative a ... cosa?

Qual è lo scopo del controller?

E questa è più o meno parte quando avremo una risposta. Cosa pensi? È un controller di cose (ad esempio un account) o un controller di azioni? Ovviamente è un controller di qualche modello o qualcosa di più astratto che fornisce azioni su di esso.

La risposta è ...

AuthenticationController with actions:

  • index() to display login form.
  • submit() to handle form submission.
  • logout(), self-explanatory.

No, l'autenticazione è un processo. Non andare in quel modo.

LoginController with actions:

  • index() to display login form.
  • submit() to handle form submission.

Lo stesso qui. Login - azione. Meglio non creare un action controller (non hai un modello correlato ad esso).

AccountController with actions:

  • loginGet() to display login form.
  • loginPost() to handle login form submission.
  • logoutGet() to handle logging out.
  • registerGet() to display registration form.
  • registerPost() to handle form submission.

Abbastanza buono, ma non sono convinto che la costruzione di quel controller di basso livello (il controller è l'astrazione stessa) valga la pena di essere portato. In ogni caso, la creazione di metodi con * Get o * Post non è chiara.

Qualche suggerimento?

Sì, consideralo:

AccountController:

  • login (AccountModel)
  • logout (AccountModel)
  • registro (AccountModel)
  • index ()

E relativo modello ad esso, ofc Account class. Ti darà l'opportunità di spostare la coppia del tuo controller di modello da qualche altra parte (se necessario) e di fare un codice chiaro (è ovvio che cosa significhi il metodo login() ). Stincking to model è davvero famoso soprattutto con le applicazioni CRUD e forse è un modo per te.

    
risposta data 27.04.2015 - 16:28
fonte
1

I controllori vengono in genere creati per una determinata risorsa (una classe di entità, una tabella nel database), ma possono anche essere creati per raggruppare insieme le azioni responsabili di una determinata parte dell'applicazione. Nei tuoi esempi, sarebbe un controller che gestisce la sicurezza dell'applicazione:

class SecurityController
{
    // can handle both the login page display and
    // the login page submission
    login(); 

    logout();

    register();

    // optional: confirm account after registration
    confirm();

    // displays the forgot password page
    forgotPassword();

    // displays the reset password page
    // and handle the form submission
    resetPassword();
}

Nota : non inserire le azioni relative alla sicurezza e le azioni del profilo utente nello stesso controller; potrebbe avere senso perché sono correlati all'utente, ma uno dovrebbe gestire l'autenticazione e l'altro dovrebbe gestire gli aggiornamenti di email, nome, ecc.

Con i controller creati per le risorse (diciamo Task ), avresti il solito CRUD azioni:

class TasksController
{
    // usually displays a paginated list of tasks
    index();

    // displays a certain task, based on an identifier
    show(id);

    // displays page with form and
    // handles form submission for creating
    // new tasks
    create();

    // same as create(), but for changing records
    update(id);     

    // displays confirmation message
    // and handles submissions in case of confirmation
    delete()
}

Ovviamente, hai la possibilità di aggiungere risorse correlate allo stesso controller. Supponiamo ad esempio che tu abbia l'entità Business , e ognuna ha diverse% di% di esclusioni. Un controller per questo potrebbe apparire come questo:

class BusinessController
{
    index();

    show(id);

    create();

    update(id);

    delete();

    // display the business services for a certain business
    listBusinessServices(businessId);

    // displays a certain business service
    showBusinessService(id);

    // create a new business service for a certain business
    createBusinessService(businessId);

    // updates a certain business service
    updateBusinessService(id);

    // deletes a certain business service
    deleteBusinessService(id);
}

Questo approccio ha senso quando le entità figli correlate non possono esistere senza l'entità genitore.

Questi sono i miei consigli:

  • crea i controller in base a un gruppo di operazioni correlate (gestendo determinate responsabilità come sicurezza o operazioni CRUD sulle risorse ecc.);
  • per i controller basati su risorse, non aggiungere azioni non necessarie (se non si desidera aggiornare la risorsa, non aggiungere l'azione di aggiornamento);
  • puoi aggiungere azioni "personalizzate" per semplificare le cose (es. hai un'entità BusinessService che ha una disponibilità basata su un numero limitato di voci, puoi aggiungere una nuova azione al controller chiamato Subscription che ha il scopo singolo di sottrarre una voce da use() )
  • mantieni le cose semplici - non ingombrare il controller con un numero enorme di azioni e una logica complessa, prova a semplificare le cose riducendo il numero di azioni o creando due controller;
  • se utilizzi un framework focalizzato su MVC, segui le linee guida sulle best practice (se ce l'hanno).

Alcune risorse per ulteriori letture qui .

    
risposta data 26.04.2015 - 08:45
fonte
0

Vedo due "forze" di design antagonistico (che non sono esclusive per i controller):

  • coesione - i controllori dovrebbero raggruppare le azioni correlate
  • semplicità - i controllori dovrebbero essere il più piccoli possibile per gestire la loro complessità

Dal punto di vista della coesione, tutte e tre le azioni (login, logout, registrazione) sono correlate, ma login & disconnettersi molto più della registrazione. Sono correlati semanticamente (uno è un'inversione dell'altro) e molto probabilmente useranno anche gli stessi oggetti di servizio (le loro implementazioni sono anche coesive).

Il mio primo istinto sarebbe raggruppare login e logout in un controller. Ma se accedi e amp; le implementazioni dei controller di logout non sono così semplici (ad esempio login ha captcha, più metodi di autenticazione ecc.), non avrei problemi a dividerle in LoginController e LogoutController per mantenere la semplicità. Dove questa soglia di complessità (quando dovresti iniziare a dividere il controller), le bugie sono un po 'personali.

Ricorda inoltre che qualsiasi cosa disegni inizialmente il tuo codice, puoi (e dovresti) rifattorizzare man mano che cambia. In questo caso è abbastanza tipico iniziare con un design semplice (avere un AuthenticationController) e con il tempo riceverai più requisiti che complicheranno il codice. Una volta superata la soglia di complessità, dovresti rifattarla su due controller.

A proposito, il tuo codice suggerisce che stai disconnettendo l'utente con la richiesta GET. Questa è una cattiva idea dato che HTTP GET dovrebbe essere nullipotent (non dovrebbe modificare lo stato dell'applicazione).

    
risposta data 26.04.2015 - 09:16
fonte
0

Ecco alcune regole pratiche:

  • Organizza per argomento o argomento, con il nome del controller come nome dell'argomento.

  • Ricorda che il nome del controller apparirà nell'URL, visibile agli utenti, quindi preferibilmente dovrebbe avere senso per loro.

Nella situazione menzionata (autenticazione) il team MVC ha già scritto il controller per te. Apri Visual Studio 2013, quindi fai clic su

File / New / Project... 
Search installed templates for "ASP.NET MVC4 Web Application"
Choose "Internet Application" / OK.

AccountController.cs contiene tutti i metodi per la gestione degli account utente:

Login()
Logoff()
Register()
Disassociate()
Manage()
ExternalLogin()

Quindi hanno organizzato per argomento "Account utente e autenticazione", con nome argomento visibile "Account".

    
risposta data 27.04.2015 - 15:38
fonte
0

Terminologia

Credo che sia un grosso malinteso chiamare una classe contenente alcuni metodi relativi a HTTP come "controller".

Il controller è un metodo che gestisce la richiesta, ma non una classe che contiene tali metodi . Quindi, index() , submit() , logout() sono controllori.

La classe che contiene quel tipo di metodi è denominata "controller" solo perché costituisce un gruppo di controller e svolge un ruolo di spazio dei nomi "di livello inferiore". Nel linguaggio FP (come Haskell) sarebbe solo un modulo. È buona norma mantenere le classi di "controller" il più apolide possibile nei linguaggi OOP, eccetto i riferimenti a servizi e altre cose a livello di programma.

La risposta

Con una terminologia risolta, la domanda è "come dovremmo separare i controllori in namespace / moduli?" penso che la risposta sia: i controller all'interno di un singolo namespace / modulo dovrebbero occuparsi stesso tipo di dati . Ad esempio, UserController occupa principalmente con istanze di User class, ma occasionalmente tocca altre cose correlate, se necessario.

Poiché login , logout e altre azioni simili si riferiscono principalmente alla sessione, probabilmente è meglio metterle all'interno di SessionController , e index controller, che stampa solo un modulo, deve essere inserito in LoginPageController , poiché ovviamente si occupa della pagina di accesso. Ha poco senso inserire il rendering HTML e la gestione delle sessioni in una singola classe, e ciò violerebbe SRP e probabilmente un mucchio di altre buone pratiche.

Principio generale

Quando hai difficoltà a decidere dove inserire una parte di codice, inizia con i dati (e i tipi) con cui hai a che fare.

    
risposta data 27.04.2015 - 17:22
fonte

Leggi altre domande sui tag