none
Primi passi e debug - MVC VB.Net 2017 RRS feed

  • Domanda

  • Buongiorno a tutti,

    dopo anni di applicazioni desktop con VB prima e VB.Net poi, sto muovendo i primissimi passi in Asp e più precisamente in MVC tramite Visual Studio 2017 e VB.Net. Sto provando a fare qualcosa seguendo tutorial, libri ecc, ma mi mancano proprio le basi.

    In una applicazione ho definito un Controller di nome Login, nel suo metodo Index faccio restituire una view.

    In questa view, posizionata correttamente nella cartella Views - Login, e dal nome Index, richiamo il metodo (o meglio l'azione) Check definita sempre nel controller Login che a sua volta restituisce una view, naturalmente anche questa view è presente nella cartella Views - Login e si chiama Check. Nella cartella App_Start ho editato il modulo RouteConfig per far si che di default partisse l'azione Index del controller Login.

    Lanciando il debug mi sono già reso conto di una cosa a cui non ero abituato nei miei progetti desktop. Il debug "parte" dall'elemento selezionato in esplora risorse. Se infatti evidenzio la mia view "Index", parte questa, e sul browser che si apre vedo "http://localhost:59715/Login/Index", mentre se evidenzio la view "Check", visualizzo quest'ultima ed il browser mostra "http://localhost:59715/Login/Check". Se invece evidenzio un controller, parte (presumo) l'azione di default e il browser mostra, nella barra dell'indirizzo, semplicemente "localhost:59715", e da parte mia visualizzo ciò che ho definito nella view Index, passando comunque per il controller Login, visto che mettendo un breakpoint prima del Return View(), vedo che l'applicazione si ferma li.

    La prima domanda quindi è di facile intuizione: quale è il metodo migliore per fare il debug dell'applicazione?

    Altra cosa che non capisco, allego un piccolo codice:

    Imports System.Web.Mvc
    
    Namespace Controllers
        Public Class LoginController
            Inherits Controller
    
            ' GET: Login
            Function Index() As ActionResult
                Return View()
            End Function
    
            <HttpPost> Function Check() As ActionResult
                Return View()
    
            End Function
        End Class
    End Namespace


    Questo è, per ora, tutto il controller Login. La view Index è:

    @Code
        ViewData("Title") = "Index"
        Layout = "~/Views/Shared/_Layout.vbhtml"
    End Code
    <form action="Check" method="post">
        <h2>Index</h2>
        <input id="Submit1" type="submit" value="submit" />
    </form>


    Se faccio partire il debug dalla view Index, come ho detto prima evidenziandola in esplora risorse, premendo il bottone "submit", viene chiamata la azione Check nel controller Login (ho messo un breakpoint e viene raggiunto) e quindi al browser viene restituita la view corrispondente.

    Se invece faccio partire il debug da qualunque altro punto, anche proprio dalla view Check, esce fuori un 

    Errore server nell'applicazione '/'. 

    Impossibile trovare la risorsa.
    Descrizione: HTTP 404. La risorsa che si sta cercando (o una delle sue dipendenze) potrebbe essere stata rimossa, rinominata o non essere temporaneamente disponibile. Verificare che l'URL riportato di seguito sia stato digitato correttamente.

    URL richiesto: /Login/Check

    Informazioni di versione: Versione di Microsoft .NET Framework:4.0.30319; Versione di ASP.NET:4.8.4075.0

    L'URL richiesto, in realtà cambia in base a dove parto con il debug, se parto dalla view Check, il messaggio è quello mostrato sopra, se parto da un controller, allora mi viene mostrato "URL richiesto: /Check"

    Chi mi può dare una mano a capire cosa sbaglio?

    Grazie


    • Modificato vbMizio mercoledì 25 marzo 2020 09:03
    martedì 24 marzo 2020 17:00

Risposte

  • Ciao Mizio e benvenuto nel mondo asp.net ;-)
    Il comportamento che ottieni (ho letto in realtà solo la tua domanda, non anche la tua risposta) con vs è del tutto normale.
    Praticamente se ti trovi in una view e avvii il debug, il browser parte con la relativa pagina della view, mentre se ti trovi in un controller o anche in un model e avvi il debug il browser parte direttamente visualizzanbdo la home del sito che per home intendo la regola Route di default, di norma è questa:

    routes.MapRoute(
                    name: "Default",
                    url: "{controller}/{action}/{id}",
                    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                );

    Tu però hai modificato il controller da quanto ho letto e quindi parte la index del controller Login.
    Se fai partire il browser dalla view check è normale che ti dia l'errore perché non esiste l'action "get", nel tuo controller esiste solo l'action post (nota che l'action Check() ha la firma "<HttpPost>").

    Quando digitiamo un url nel browser effettuiamo in realtà una richiesta GET, come spesso accade anche quando navighiamo all'interno di un sito web. Quando invece (ma non solo in questo caso) utilizziamo un form e clicchiamo su di un bottone, spesso la pagina di destinazione la richiamiamo col metodo POST.

    In MVC possiamo gestire tramite due diversi actionresult le rispettive chiamate GET e POST.

    Infatti, se aggiungi nel tuo controller l'actionresult Check() con la firma GET vedrai correttamente la view Check:

    Function Check() As ActionResult
          Return View()
    End Function

    Nota anche che quando la firma non è presente (non l'ho inserita appositamente), di default si tratta di un metodo GET.

    Spero di averti chiarito un po di concetti, noi siamo sempre qui,
    Ciao



    Paolo Pranzo

    giovedì 26 marzo 2020 15:11
  • Praticamente la proprietà action di una form è come la proprietà href di un collegamento. Se mi trovo in miosito.it/login/ (in questo caso sto visualizzando la index del controller login) e uso "login/check2", il browser effettua la richiesta sul server all'indirizzo miosito.it/login/login/check2.
    Per risolvere il problema ti basta scrivere il giusto indirizzo della richiesta aggiungendo innanzitutto "/" (per indicare la root del sito) e poi scrivendo controller e action, quindi "/login/check2".

    Usando le form con MVC dovresti scrivere una cosa del genere:

    @using (Html.BeginForm("Check2", "Login", FormMethod.Post))
    {
    ...
    }

    Questo codice creerà un html del tipo:

    <form action="/Login/Check2" method="post">
    ...
    </form>
    Nota la proprietà action che aggiunge il carattere "/" per stabilire il corretto url partendo dalla root del sito.

    In questo modo è MVC a stabilire il corretto url.
    Ciao


    Paolo Pranzo

    giovedì 26 marzo 2020 16:09

Tutte le risposte

  • Salve Mizio, 

    Normalmente mi fido di te, quando si tratta di tale domande piu' specifici. Vediamo se posso essere utile io, con alcune idee. 

    Prova a impostare in web.config

    <trace enabled="true" />

    Inoltre, puoi provare a distribuire l'applicazione in modalità debug.

    Vedi se questo LINK in MSDN ti aiuta

    Forums ASP.NET in inglese. Hanno alcune idee buone sulla problematica, nel thread linkato. 


    • Microsoft offre questo servizio gratuitamente, per aiutare gli utenti e aumentare il database dei prodotti e delle tecnologie. Il contenuto fornito “as is“ non comporta alcuna responsabilità da parte dell’azienda.

    mercoledì 25 marzo 2020 16:23
    Moderatore
  • Ciao Yordan, e grazie per la risposta.

    Mi scuso se non sono stato troppo preciso nel formulare la domanda, ma sono davvero agli inizi e anche per fare delle domande mirate, bisogna sapere quel che si chiede.

    Non mi pare che l'abilitare il trace abbia avuto qualche effetto immediato. Vediamo se riesco a spiegarmi meglio riguardo la prima problematica.

    Prendiamo ad esempio la cartella View - Login, dove ci sono due viste, le corrispondenti alle due action definite nel controller. Queste due view si chiamano Index e Check, dato che nel controller Login ci sono le due action Index e Check.

    Se in esplora soluzione evidenzio, cliccandoci una volta con il tasto sinistro del mouse, la vista Check (cioè il file Check.vbhtml) e premo F5 per il lanciare l'esecuzione o faccio click sulla freccetta verde, viene aperto il browser definito e che apre la pagina /Login/Check benché nel file RouteConfig presente nella cartella App_Start ho definito come default l'action Index del controller Login. La domanda quindi era... come faccio ad essere sicuro che l'applicazione che sto lanciando, si comporti esattamente come se, una volta distribuita, stessi puntando all'indirizzo principale del mio "sito"? Come mai facendo partire l'applicazione, non si è aperto il browser sulla pagina di cdefault? E' il comportamento standard?

    In seconda battuta, guardando il codice che ho riportato, se facevo partire l'applicazione dalla pagina Index, il metodo post che richiamava l'action Check funzionava, se invece facevo partire l'applicazione dalla pagina di default (che comunque puntava alla action Index) ricevevo l'errore di risorsa non trovata.

    mercoledì 25 marzo 2020 16:50
  • Ciao Mizio e benvenuto nel mondo asp.net ;-)
    Il comportamento che ottieni (ho letto in realtà solo la tua domanda, non anche la tua risposta) con vs è del tutto normale.
    Praticamente se ti trovi in una view e avvii il debug, il browser parte con la relativa pagina della view, mentre se ti trovi in un controller o anche in un model e avvi il debug il browser parte direttamente visualizzanbdo la home del sito che per home intendo la regola Route di default, di norma è questa:

    routes.MapRoute(
                    name: "Default",
                    url: "{controller}/{action}/{id}",
                    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                );

    Tu però hai modificato il controller da quanto ho letto e quindi parte la index del controller Login.
    Se fai partire il browser dalla view check è normale che ti dia l'errore perché non esiste l'action "get", nel tuo controller esiste solo l'action post (nota che l'action Check() ha la firma "<HttpPost>").

    Quando digitiamo un url nel browser effettuiamo in realtà una richiesta GET, come spesso accade anche quando navighiamo all'interno di un sito web. Quando invece (ma non solo in questo caso) utilizziamo un form e clicchiamo su di un bottone, spesso la pagina di destinazione la richiamiamo col metodo POST.

    In MVC possiamo gestire tramite due diversi actionresult le rispettive chiamate GET e POST.

    Infatti, se aggiungi nel tuo controller l'actionresult Check() con la firma GET vedrai correttamente la view Check:

    Function Check() As ActionResult
          Return View()
    End Function

    Nota anche che quando la firma non è presente (non l'ho inserita appositamente), di default si tratta di un metodo GET.

    Spero di averti chiarito un po di concetti, noi siamo sempre qui,
    Ciao



    Paolo Pranzo

    giovedì 26 marzo 2020 15:11
  • Paolo,

    grazie anche a te per la spiegazione. Ho *quasi* capito.

    Premetto che nel codice che segue, non ho più la action (e la relativa view) Check, sostituita con Check2; ecco il motivo della differenza nel codice che scriverò

    C'è una cosa ancora che non mi torna riguardo l'errore della risorsa non trovata. Nei miei tentativi, avevo risolto (in maniera empirica) aggiungendo il nome del controller prima della azione, e cioè, nella view:

    form action="Login/Check2" method="post"

    In questo modo, funziona, mentre se lascio solo il nome della action, e cioè form action="Check2" method="post" esce fuori l'errore di risorsa non trovata.

    Naturalmente non sto cambiando la firma alla action, che resta sempre <HttpPost>  

    Come mai devo anteporre il nome del controller? La view fa riferimento ad un metodo di *quel* controller.

    Alla fine ho quattro casi diversi. Se "parto" dalla view Index del controller Login, evidenziandola in esplora risorse, funziona solo non includendo il nome del controller, quindi action="Check2" e non action="Login/Check2", mentre se parto dalla pagina di default, è esattamente il contrario, quindi funziona includendo il nome del controller e non lasciando solo il nome della action. 

    Come ulteriore prova, ho anche eliminato la firma HttpPost alla action Check2, e tutto continua a funzionare esattamente come prima, come se fosse stato inutile specificare la firma.


    giovedì 26 marzo 2020 15:41
  • Praticamente la proprietà action di una form è come la proprietà href di un collegamento. Se mi trovo in miosito.it/login/ (in questo caso sto visualizzando la index del controller login) e uso "login/check2", il browser effettua la richiesta sul server all'indirizzo miosito.it/login/login/check2.
    Per risolvere il problema ti basta scrivere il giusto indirizzo della richiesta aggiungendo innanzitutto "/" (per indicare la root del sito) e poi scrivendo controller e action, quindi "/login/check2".

    Usando le form con MVC dovresti scrivere una cosa del genere:

    @using (Html.BeginForm("Check2", "Login", FormMethod.Post))
    {
    ...
    }

    Questo codice creerà un html del tipo:

    <form action="/Login/Check2" method="post">
    ...
    </form>
    Nota la proprietà action che aggiunge il carattere "/" per stabilire il corretto url partendo dalla root del sito.

    In questo modo è MVC a stabilire il corretto url.
    Ciao


    Paolo Pranzo

    giovedì 26 marzo 2020 16:09
  • Grazie ancora.

    In effetti ho visto che aggiungendo la radice, funziona sempre. Credo che mi regolerò in questa maniera, per essere sicuro di vedere poi l'effetto finale. Appena posso, provo anche con il

    @using (HTML.BeginForm.... ma ancora no ho capito bene come andare poi ad inserire gli elementi, cioè non ho capito come "tradurre" quello che avevo scritto io inserendo la textbox, la password e il submit e reset.

    Un'ultima cosa, per terminare il discorso già iniziato.

    Scrivevi che è necessario aggiungere la firma <HttpPost> per indicare al controller che quella action verrà richiamata tramite un metodo post, mentre la firma di default, quella implicita, è <HttpGet>, che NON risponde a metodi Post.

    Come mai la mia action Check2 viene richiamata comunque dalla view Index, che fa un post, anche se non è specificata la firma <HttpPost>?

    giovedì 26 marzo 2020 17:05

  • Come mai la mia action Check2 viene richiamata comunque dalla view Index, che fa un post, anche se non è specificata la firma <HttpPost>?

    All'inizio al codice che hai postato la firma c'è. Magari usa un altro action di un controller differente. Nel dubbio metti sempre un breakpoint.

    Per "leggere" i dati inseiriti dall'utente (quindi textbox, dropdownlist, ecc..) ci sarebbero diversi modi. 
    Io ti suggerisco quello più valido: si tratta di una classe chiamata Viewmodel. Certo, spiegarlo qui in due frasi sarebbe riduttivo, ma si tratta brevemente di una classe dove per ogni sua proprietà corrisponde ad un controllo presente nella view.
    Ti allego una pagina di codeproject.com che spiega con un esempio l'uso di un ViewModel. Certo, è scritto in c# però.

    A sto punto guardati la playlist di Camuso su Youtube, spiega moltissime cose.
    Ciao


    Paolo Pranzo

    giovedì 26 marzo 2020 17:46
  • Paolo,

    grazie ancora di tutto. 

    Nel frattempo ho saputo che molto probabilmente il lavoro che avevo iniziato con MVC non servirà più, ma credo che, anche se a tempo perso, andrò avanti, se non altro per formazione personale.

    Riguardo alla questione della firma <HttpPost>, hai ragione che nel primo codice che avevo postato c'era, e proprio per testare quello che mi avevi detto, ho provato a togliere la firma, e ti confermo che la action viene chiamata ugualmente. E' possibile che questa distinzione tra get e post sia necessaria solo quando ci sono entrambe le versioni di una specifica action?

    venerdì 27 marzo 2020 13:11
  • Hai ragione, ho fatto una prova adesso ed effettivamente pur avendo un solo ActionResult senza nessuna firma (quindi in teoria sarebbe GET) e richiamandolo in POST viene correttamente eseguito.
    Ovvio però che se l'unico ActionResult è firmato come GET, richiamandolo in POST verrebbe sollevata un'eccezione.

    Questa informazione mi mancava, grazie giacché.
    Saluti


    Paolo Pranzo

    venerdì 27 marzo 2020 14:13
  • Perfetto, grazie ancora di tutto.

    venerdì 27 marzo 2020 14:16