Click here to Skip to main content
Click here to Skip to main content

Classic ASP and MVC

By , 3 May 2013
 
(optional Download CreateDatabase.zip)

Unzip code of the article into a new Virtual Directory test_mvc (http://localhost/test_mvc/) on your IIS server. Make sure you have enabled ASP scripts for the virtual directory. Make sure the default.asp is a default web page for this Virtual Directory.

 

 Introduction    

How many companies are still extensively using ASP this days and why? Typically the case is a company with huge ASP system in the core and few developers who know the system from it’s childhood. The system is usually too complex, with spaghetti-like style of code inside (because of a nature of ASP). I believe there is a little or no ways for new developers to be involved in support or development of the code. The reason is that the threshold for new developers is too high: 

  - market encourages developers to learn new stuff and forget old ones.

  - huge spaghetti-like code makes the effort to study the code meaningless.

These ASP system exist because they work. Existing developers may want to change something, but the the threshold for them is high too:

  - high workload with existing app support

  - necessity to study new languages (like vb.net or c#) and technologies (like MVC)

What is easier for ASP developer: to write a new code with C#+MVC or continue with ASP?

Microsoft has dropped the support of ASP and there is a little of innovations this days. Some use JavaScript, some use JQuery, XML but that is it. There is very little of  innovations for the server-side that may makes the life of programmers easier.

The aim of this effort is to bring MVC concept to ASP. To give a way to write/re-write the ASP app in MVC-like style. Untangle spaghetti-like code. To let newcomers recognize the common code-patterns and pick up the support of existing ASP apps easier. 

 Background 

 There is a MVC out there.  Let’s embed MVC patterns into the classic ASP. 

 Router

In the core of MVC there is a Router. Router selects the Controller and Action (mostly).

Router is a code that decides which Controller and Action to call. 

Samples of URLs (controllers are highlighted)

Http://localhost/Home/Index 

Http://localhost/Home/About 

Http://localhost/User/Edit/1 

 Controller    

Wth is this Controller? Why should you care to have Controllers in your code? I may say that it is a significant part of the MVC concept, but these words mean nothing. 

Let’s go from practice. It’s rare, but suppose you have a document to develop a part of some system:

Where you are going to store the code for this logic? As an ASP developer you would probably create a set of pages to pull and display  some data from the database, and a couple of pages to receive Forms with user input to update the database back. It is OK when the system is not too large. It is OK when the User interacts with the system in a simple way. 

How is about that system?

You will have to create some sort of "Architecture" in your code to do not get totally lost in the code when the system is finished. When I say  "Architecture" I mean some repetitive patterns of the code to handle the similar situations. Anyways the system will be a mess of HTML, SQL,  code to access the database, handle user input, and display data at the end of development. By the way, I believe that the system on the picture is VERY SIMPLE.  What happened if it is 10-20 times bigger?

A remedy for the problem is the code encapsulation with repetitive standard structures of the code and special agreement regarding the naming of the components. So there is a place for special component - a Controller, which stores the business logic and interacts with user:  

 

Controller is a class. It holds all the aspects of user interaction regarding ONE type of business entity. There should be a number of controllers in the Application. Controllers MUST have names ending with Controller: HomeController, PublicationController,  StatusController, UserController. Controller SHOULD NOT have a user interface code or HTML markup. It is a good practice to avoid the SQL in the controller too. Controller should handle user input, query and update the object model and prepare the data to be displayed. All Controllers should reside in the folder /Controllers

Controller handles the user input , do the things and controls WHAT is going to be displayed (but not HOW).

Controller should have methods that compose the logic of Controller. 

like this:

Class PublicationController
 Dim Model
 Dim ViewData
 
 Public Sub List ()
    ...
  End Sub
  Publis Sub Edit (vars)
    ...
  End Sub
  Publis Sub Delete(vars)
    ...
  End Sub
...
End Class  

These Methods of Controller are Actions. Router should be able to call them.

These are samples of URLs with Actions are highlighted. 

Http://localhost/Home/Index

Http://localhost/Home/About 

Http://localhost/User/Edit/1 

Router disassembles the URL and calls one of the Controllers and one of the Actions within the Controller.

There is no standard Router for ASP. It will be a default.asp to work as a Router.

The URLs we are going to employ will be similar to:

http://localhost/test_mvc/default.aspx?controller=Home&action=Index
http://localhost/test_mvc/default.aspx?controller=Home&action=About
http://localhost/test_mvc/default.aspx?controller=User&action=Edit&id=2

or their shorter form (if you have selected default.asp as a default page for the virtual directory):
http://localhost/test_mvc/?controller=User&action=List

There is no REFLECTION in the VBScript and we don’t know whether there is a Controller class and it’s Action method out  there. And we don’t need to.

Router simply calls the Action method of the Controller object with VBScript   Eval  instructions.

So the  these two are equal:

Set controllerInstance = New HomeController  

controller = "Home"  
Set controllerInstance = Eval("New "+ controller + "Controller")    

But the second one is more flexible and can be used to route the Http request based on the controller name. The Action is selected using the same technique.

The code of Router

 {text of defaut.asp} 

<!--#include file="utils/utils.inc" -->
<!--#include file="models/models.inc" -->
<!--#include file="controllers/controllers.inc" -->
<%
Const defaultController = "Home"
Const defaultAction = "Index"

    If not Route () then
        result = RouteDebug ()
    End If


Function ContentPlaceHolder()
    If not Route () then
        result = RouteDebug ()
    End If
End Function


Function Route ()

    Dim controller, action , vars
    controller  = Request.QueryString("controller")
    action      = Request.QueryString("action")
    set vars        = CollectVariables()
    Route = False

    If IsEmpty(controller) or IsNull(controller) then
        controller = defaultController
    End If
    
    If IsEmpty(action) or IsNull(action) then
        action = defaultAction
    End If

    Dim controllerName
    controllerName = controller + "Controller"
    Dim controllerInstance 
    Set controllerInstance = Eval ( " new " +  controllerName)
    Dim actionCallString 
    If (Instr(1,action,"Post",1)>0) then
        actionCallString = " controllerInstance." + action + "(Request.Form)"
    ElseIf Not (IsNothing(vars)) then
        actionCallString = " controllerInstance." + action + "(vars)"
    Else
        actionCallString = " controllerInstance." + action + "()"
    End If
    Eval (actionCallString)
    Route = true
End Function



Function RouteDebug ()
    Dim controller, action , vars
    controller  = Request.QueryString("controller")
    action      = Request.QueryString("action")

    Response.Write(controller)
    Response.Write(action)
    
    dim key, keyValue
    for each key in Request.Querystring
        keyValue = Request.Querystring(key)
        'ignore service keys
        if InStr(1,"controller, action, partial",key,1)=0 Then
            Response.Write( key + " = " + keyValue )
        End If
    next
    
End Function

Function CollectVariables
    dim key, keyValue
    Set results = Server.CreateObject("Scripting.Dictionary")
    for each key in Request.Querystring
        keyValue = Request.Querystring(key)
        'ignore service keys
        if InStr(1,"controller, action, partial",key,1)=0 Then
            results.Add key,keyValue 
        End If
    next
    if results.Count=0 Then
        Set CollectVariables = Nothing
    else 
        Set CollectVariables = results
    End If
End Function

%>

 

 Actions 

 So the Controller is instantiated and one of it's Actions is called. Next, Action executes the business logic. There may be a business logic of any kind in the controller. You may query or update the database, send e-mails, process files or user input, or even launch a rocket to the Mars. Typically there is a logic to update/insert/delete/list records from/to database, but it may be literally anything there. There should be NO HTML markup in the Controller. 

At the end of Action, if you want to display SOMETHING (besides the static content of View), you need to initialize the variable Model and include the View as the last line of the Action.  

Look at the example below: If the user hit the link http://localhost/test_mvc/?controller=User&action=List , Router  selects the UserController and List action.  The List action prepares the list of Users and assign this list to the variable Model. Variable Model is used in the View  ../views/User/List.asp This view is attached at the end of action List.  

{ text of /Controller/UserController.asp }   

 <% 

class UserController
 Dim Model
 Dim ViewData
  
 private sub Class_Initialize()
  Set ViewData = Server.CreateObject("Scripting.Dictionary")
 end sub

 private sub Class_Terminate()
 end sub

 public Sub List()
    Dim u 
    set u = new UserHelper
    set Model = u.SelectAll
 
    %>   <!--#include file="../views/User/List.asp" --> <%
 End Sub
 
 public Sub Create()
    set Model = new User
    %>   <!--#include file="../views/User/Create.asp" --> <%
 End Sub
 
  public Sub CreatePost(args)
    Dim obj, objh
    set objh = new UserHelper
    set obj = new User

    obj.FirstName = args("FirstName")
    obj.LastName = args("LastName")
    obj.UserName = args("UserName")
    obj.ProjectID = args("ProjectID")
    'form values should be cleaned from injections
    'checkboxes shoud use the syntax: obj.ProjectID = (args("ProjectID") = "on")
    obj.Id = objh.Insert(obj)
 
    Response.Redirect("?controller=User&action=list")
 End Sub

 
 public Sub Edit(vars)

    Dim u
    set u = new UserHelper
    set Model = u.SelectById(vars("id"))
 
   %>   <!--#include file="../views/User/Edit.asp" --> <%
 End Sub
 
 public Sub EditPost(args)
    Dim obj, objh
    set objh = new UserHelper
    set obj = objh.SelectById(args("id"))

    obj.FirstName = args("FirstName")
    obj.LastName = args("LastName")
    obj.UserName = args("UserName")
    obj.ProjectID = args("ProjectID")
    'form values should be cleaned from injections
    'checkboxes shoud use the syntax: obj.ProjectID = (args("ProjectID") = "on")
    objh.Update(obj)
 
    Response.Redirect("?controller=User&action=list")
 End Sub

 
 public Sub Delete(vars)
    Dim u
    set u = new UserHelper
    set Model = u.SelectById(vars("id"))
   %>   <!--#include file="../views/User/Delete.asp" --> <%
 End Sub
 
 
  public Sub DeletePost(args)
    Dim res, objh
    set objh = new UserHelper
    res = objh.Delete(args("id"))
    if  res then
        Response.Redirect("?controller=User&action=list")
    else
        Response.Redirect("?controller=User&action=Delete&id=" + CStr(args("id")))
    end if
 End Sub


 public Sub Details(vars)
    Dim u
    set u = new UserHelper
    set Model = u.SelectById(vars("id"))

   %>   <!--#include file="../views/User/Details.asp" --> <%
 End Sub
 
 End Class
%>

 Note: View is selected automatically with .NET MVC. View should have the same name as Action. They reside in the folder with the same name as the Controller.For example
/Views/Home/Index.asp
/Views/User/Edit.asp

 

 Views 

 View provides appearance.  It is only Views should have an HTML markup inside them. There should be NO or absolute minimum of the logic in a View. The business of a View is to take the Model and display it to the user in a best way.   

{ text of /Views/User/List.aspx } 

 List Users

<%=Html.ActionLink("Create new User", "User", "Create" , "") %> <br/>

<table>
    <tr>

        <td>FirstName</td>

        <td>LastName</td>

        <td>UserName</td>

        <td>ProjectID</td>

        <td></td>
    </tr>

    <%
    if  IsNothing(Model) then
        %> <tr><td colspan="4">No records</td> </tr><%
    Else
        Dim obj
        For each obj in Model.Items
        %>

        <tr>

            <td><%=Html.Encode(obj.FirstName) %></td>

            <td><%=Html.Encode(obj.LastName) %></td>

            <td><%=Html.Encode(obj.UserName) %></td>

            <td><%=Html.Encode(obj.ProjectID) %></td>

            <td>
                <%=Html.ActionLink("Edit", "User", "Edit" , "id=" + CStr(obj.Id)) %> |
                <%=Html.ActionLink("Delete", "User", "Delete" , "id=" + CStr(obj.Id)) %> |
                <%=Html.ActionLink("Details", "User", "Details" , "id=" + CStr(obj.Id)) %>
                
            </td>
        </tr>
        <% 
        Next
     End If
     %>
</table>

So why these Views are so amazing? 

It is all in the Order and Separation.

Design your Views separately from your code. Express Yourself. Change the appearance as easy as 1-2-3. Have a fullscreen or mobile view (or both) without necessity to touch the business logic code.

Concentrate on the business logic and data when you work with Controller/Action. 

Scale your system up to dozens Controllers and hundreds of Views. Enjoy the transparency and easy code navigation. Get rid of spaghetti-like code forever.

   

 History  

There are going to be the following topics: 

Data flow: Where the Data is ? What the Model is and how to send Model and ViewData from Action to View?

Common view for all the pages - masterpages and partial views  

Form handling: User input handling 

Server side master-details pages  

Client side master-details pages 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Emmet M
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130513.1 | Last Updated 4 May 2013
Article Copyright 2013 by Emmet M
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid