How to Upgrade ASP Pages to ASP.NET Pages That Use JScript .NET

 

Andrew Clinick
Microsoft Corporation

Jason Cooke

January 22, 2002

Introduction

One of my new years resolutions was to delegate more, in the hope that I might be able to get more things done. As a first step towards this resolution (which we all know won't last more than a month anyway), this month's scripting clinic has been written largely by the Microsoft® JScript® documentation writer, Jason Cooke. Jason has the dubious task of translating some vague whiteboard designs into documentation, and if you look at the JScript .NET documentation, you'll see he's done a fine job. Thanks, Jason.

This month's article is all about how to migrate your existing JScript ASP pages to JScript .NET and ASP.NET. In particular, it shows you how to take advantage of the exciting new features that ASP.NET provides.

The design of ASP.NET and JScript .NET facilitates the process of upgrading from ASP and JScript pages. As a result, you can take advantage of the great, new features of the ASP.NET object model, the new functionality of JScript .NET, and the additional features included in the .NET Framework.

Upgrading consists of two, distinct phases:

  1. In the first phase, you identify unsupported code constructs and convert them to conforming code. For the most part, the compiler helps you through this stage by providing syntax and run-time errors.
  2. The second phase involves changing code to take advantage of the new opportunities presented by the .NET Framework. This phase is open-ended and depends upon the particular application.

This article focuses mainly on the first phase, by addressing the new features of JScript .NET and by explaining how the interaction between JScript .NET and ASP.NET differs from the interaction between JScript and ASP.

The server does not process client-side script, although server-side script can generate client-side script. Therefore, client-side script is not an upgrading issue, and is not discussed in this article.

Note   For more information about the differences between ASP and ASP.NET, see Migrating to ASP.NET: Key Considerations.

Who Should Read This Article?

This article is for developers who are already familiar with ASP and JScript and want to learn how to upgrade pages to ASP.NET pages written with JScript .NET.

There are several reasons developers should read this article:

  • Pages upgraded to ASP.NET and JScript .NET usually experience a boost in performance because the Web server compiles ASP.NET pages rather than interpreting them. With ASP technology, the Web server compiles ASP pages to an intermediate language (IL). Each time the server accesses an ASP page, the ASP script engine must re-interpret the IL. With ASP.NET, the Web server compiles ASP.NET pages to IL and then to native code. Since native code needs no interpretation before running, for subsequent hits, ASP.NET pages run faster than ASP pages.

    Both ASP.NET pages and ASP pages can access and use COM components. The .NET Framework automatically generates a wrapper for each COM component called by an ASP.NET page. Although using the default wrapper allows for easy upgrading of ASP pages to ASP.NET, its use can negatively affect performance.

  • Upgraded pages can take advantage of new features, such as built-in Web farm session state management, rich server controls, and enhanced debugging and tracing capabilities provided by ASP.NET, JScript .NET, and the .NET Framework.

  • You can leverage your existing ASP and JScript knowledge to write ASP.NET pages without learning an entirely new language.

  • You can use Web Services, which use the industry standard SOAP protocol and are not restricted to using HTTP GET to request server pages. For more information, see Remote Scripting in a .NET World.

Common Concerns

A common question about upgrading to ASP.NET is: "What happens to existing ASP pages?" You might imagine that upgrading replaces the existing ASP engine with an ASP.NET engine, requiring you to upgrade all ASP pages at the same time. Thankfully, this is not the case!

You can have both the ASP engine and the ASP.NET engine installed at the same time. They work perfectly well side-by-side because ASP pages use the .asp extension, while ASP.NET pages use the .aspx extension. This allows you to upgrade one page at a time!

Note   If multiple pages share a session or application state to exchange information, you must either use alternate methods (such as posting form data from one page to another or use a database) to pass the information or upgrade those pages at the same time. This is a required process, because ASP and ASP.NET use separate engines; information that an ASP page stores in a shared state is not accessible from an ASP.NET page, and vice versa.

How to Upgrade

Upgrading code, even non-trivial code, can be straightforward once you know the tricks. The following are the major upgrading issues:

  • Getting Started: Changing the extension of an ASP page (.asp) to an ASP.NET extension (.aspx) is the first step in upgrading. Many pages "just work" with this change, since ASP.NET and JScript .NET are backwards compatible. For issues that don't fall into this category, read on.

  • Declaring Variables: ASP.NET requires explicit declaration of all variables, whereas in an ASP page, declarations are optional. This is the most common problem, and it is easy to fix.

  • Code Placement and Scoping Issues: ASP.NET has stricter rules than ASP about where you can place executable code, and where you can access variables and functions. These problems can be solved by moving code from <script runat=server> blocks to <%...%> blocks, or vice versa.

  • Modifications to Object Extensibility: In ASP.NET pages, you cannot add or modify the properties of the built-in objects. Instead, you must use functions and variables instead of modifying built-in objects. You can override and add expando methods to user-defined objects, but you must apply the expando modifier (a new feature of JScript .NET) to method definitions that access the other members of the object.

  • COM Interop: ASP.NET does not recognize default properties that may be set on objects returned by COM objects. You must call all properties explicitly.

    JScript .NET offers a way to pass variables by reference, and provides data types that other languages use but are not available in JScript.

  • Changes to the ASP.NET Object Model: There are a few notable differences between the ASP object model and the ASP.NET object model. First, the methods of the page, which are not case-sensitive in ASP, are case-sensitive in ASP.NET. Secondly, some methods return data in a different format in the two models. Finally, the Cookies collection in ASP.NET is more object-oriented than in ASP.

  • Less Common Sources of Problems: A few JScript features, such as the arguments object, redefined functions, and quiet failure of assignments to read-only identifiers, are not supported by JScript .NET in ASP.NET pages or in fast mode. There are workarounds for code that relies on the first two features, but no code should rely on the third one. In addition, large pages that have many <%...%> blocks may not compile, but moving definitions to <script runat=server> blocks should help.

Getting Started

The Web server uses the page extension to determine the content of a page. The .aspx and .asax extensions identify ASP.NET pages. These are the analogues of .asp and .asa for ASP pages.

Changing the extension of an ASP page to an ASP.NET extension is the first step in upgrading. The change instructs the server to use the ASP.NET engine to compile and execute the page. Many pages "just work" after the extension change, since the architects of ASP.NET and JScript .NET designed them to maintain as much backwards compatibility as possible. Many ASP pages are already compatible with ASP.NET. These pages may benefit from further changes, to make them more robust and to improve performance.

One important difference between ASP and ASP.NET is that ASP allows the use of multiple languages on a page, while ASP.NET requires only one script language on each page. The default language in an ASP.NET page is Microsoft® Visual Basic® .NET. To override the default on a page-by-page basis, include <%@ Language=JScript %> as the first line of the page. Alternatively, you can also modify the web.config file to set the default language for multiple ASP.NET pages at once, which is useful if all your pages are written in JScript .NET. For more information, see ASP.NET Configuration.

Note   The default language setting in Microsoft® Internet Information Services (IIS), which affects ASP pages, does not affect ASP.NET pages.

In an ASP page, restricting developers to only one language would make programming very difficult because JScript does not support interactions with every COM object. For example, you may be accustomed to using snippets of Visual Basic Scripting Edition (VBScript) code in an ASP page to manipulate SAFEARRAY data or to use COM component methods with ByRef parameters. Since JScript .NET natively supports access to all standard COM objects, mixing languages is no longer required. See the COM Interop section below for more details.

Declaring Variables

The most common source of problems when upgrading JScript to JScript .NET is undeclared variables. To enable the creation of cleaner code, ASP.NET requires the explicit declaration of all variables. In the case of JScript .NET, you must use the var statement to declare each variable.

When adding the required var statements, you should consider type annotating the declared variables to enable early binding. Early binding allows the compiler to produce faster code and report misuses of the variable.

For example, the following code works in an ASP page.

<% x = 42 %>
The value of x is <%= x %>.

In an ASP.NET page, this generates the compile-time error, JS1135: Variable 'x' has not been declared. To fix this error, simply declare the variable x with the var statement:

<% var x = 42 %>
The value of x is <%= x %>.

Furthermore, if you know that x will always store integers, you can type annotate the variable.

<% var x : int = 42 %>
The value of x is <%= x %>.

Code that is early bound runs faster and can identify errors. If you assign a string to a variable that should store only integers, the compiler generates a warning.

Code Placement and Scoping Issues

Code in an ASP or ASP.NET page that runs on the server belongs in either a <script runat=server> block, a <%...%> block, or a <%=...%> block. Some common names for the <%...%> block are render block and percent block, while a <%=...%> block is commonly called a display block. The rules that govern the placement of blocks are the same for ASP and ASP.NET pages. However, since ASP.NET introduces a rich, event-based programming model, the rules that govern code placement (what code can go in which blocks) and scoping (how access to data defined in different blocks is shared) are more refined than in ASP.

Code Placement

ASP uses a simple program model. Any type of JScript code (variable and function definitions, global code) can appear in both <script runat=server> and <%...%> blocks. ASP combines the code in all script blocks and runs it after the page has loaded but before it runs the code in any <%...%> blocks. This means that you get essentially the same behavior if you combine all the code from the <script runat=server> blocks and place it in a <%...%> block at the start of the page.

ASP.NET introduces an event-based programming model that is much richer than the ASP model. You can place any form of JScript code in a <%...%> block. Unlike ASP, all code within <script runat=server> blocks must appear within function declarations, within method declarations, or as an initializer for a variable definition. These definitions are called by code in <%...%> blocks or when events (such as button clicks) occur in the page. The definitions in <script runat=server> blocks are available to any code in <%...%> blocks, regardless of which block appears first on the page. You may use an expression (such as a function call) to initialize variables. The compiler generates a syntax error (JS1002: Syntax error) if a <script runat=server> block includes executable code that is not used to initialize a variable or that is not contained within a function or method declaration.

In both ASP.NET and ASP pages, code in <%...%> blocks runs in the order in which it appears in the page. In addition, the worker process raises several events during the lifetime of an ASP.NET page that call certain methods, which are listed below. The methods must be defined within <script runat=server> blocks.

The following list shows the order in which the methods are called in relation to the initialization code in <script runat=server> blocks and the <%...%> blocks.

  1. <script runat=server>: All variables and constants are initialized in the order that they appear on the page.
  2. Page_Init method: This method is called when the page is initialized and allows required initializations to be performed. Server controls may not be ready for access when this method is called.
  3. Page_Load method: This method is called when the page is loaded into the Page object and allows actions that are common to each HTTP request for the page, such as setting up a database query, to be performed. Server controls are available and form controls reflect client-side data.
  4. <%...%>: The code in these blocks is run in the order that it appears on the page.
  5. Page_Unload method: This method is called when the page is unloaded from memory and allows the page to perform final cleanup, such as closing files and database connections.

For more information, see WebControl Methods.

Variable Scope in ASP.NET

In an ASP page, <script runat=server> blocks, <%...%> blocks, and eval calls have the same scope. This means that any block or eval call can access an identifier defined in any other block or eval call.

ASP.NET implements a richer (but more restrictive) scoping model.

  • <script runat=server> blocks
    Definitions of functions, variables, classes, and interfaces made within <script runat=server> blocks are available to script anywhere else on the page. They are essentially page-level definitions. Any executable code in a <script runat=server> block must be contained within function declarations, within method declarations, or as an initializer for a variable definition.

  • <%...%> blocks
    Code runs within these blocks. You can define variables and functions within these blocks, but it is not recommended. Definitions within a <%...%> block are accessible only from other <%...%> blocks if both blocks are contained in the same server control, or if both are outside server controls. All page-level functions and variables are accessible from a <%...%> block.

  • <%=...%> blocks
    These blocks have the same scope as <%...%> blocks, but they can contain only one expression. The block displays the value of the expression.

  • Server controls
    Server controls are blocks, other than the script block, that use the runat=server attribute. Common examples are the <form> and <button> tags. Server controls make it easy to access data that users enter on a page.

    A definition in a <%...%> block nested within a server control is only accessible from other <%...%> blocks within the same server control. Although server controls may be nested, the <%...%> blocks within nested blocks cannot access definitions made in the enclosing server control, and vice versa.

  • eval calls
    Code runs within eval calls. You can declare variables and functions within these calls, but those declarations are accessible only from code within the same call. You can assign new values to any variable accessible from an eval call, but you cannot change its type or introduce new declarations. (Since eval calls are slow and bypass some of the error checking that compilation enforces, you should consider rewriting code that contains an eval.)

To avoid the complications that scoping causes, declare all ASP.NET functions and variables within <script runat=server> blocks instead of between ASP <%…%> style delimiters. You can still declare variables in <%…%> render blocks, but you should make sure that you understand the scope of the variable.

For example, the following code causes an undeclared variable error (JS1135: Variable 'x' has not been declared) when used in an ASP.NET page.

<% var x = 42 %>
<form id="form1" runat=server>
   <%= x %>
</form>

You might think that the <%=...%> block would have the same scope as the <%...%> block. However, upon closer inspection, the <%=...%> block is inside a server control, and hence has a different scope.

There are two ways to fix the error. First, you can declare x inside a <script runat=server> block instead of <%...%> block. This gives x a page scope. Secondly, you can remove the runat=server attribute from the form tag. This changes the server control to a form block, which has the same scope as the rest of the page. However, if your application requires the functionality of a server control, this is not an option.

Code Placement and Definition Scope Summary

For the best performance and maintainability, place all definitions in <script runat=server> blocks, place all executable code in <%...%> blocks, and avoid using eval.

Modifications to Object Extensibility

In an ASP page, there are several ways to modify the properties of user-defined objects and objects built into JScript. For example, you can add expando properties, add a method to the object's prototype object, or override the default methods.

With ASP.NET, you cannot add to or modify the properties of the built-in objects. This attribute ascribes a dependable structure to the built-in objects, which improves performance and stability. However, when upgrading, this difference means that you must make slight modifications to methods added to or overridden in built-in objects. Convert the methods to helper functions, and change calls to the methods to function calls.

You can override and add expando methods to user-defined objects in an ASP.NET page, but syntax for defining constructor functions and methods is slightly different from ASP pages.

Extending Built-in Objects

Code in an ASP page may modify the prototype property of built-in objects. In other words, you can override existing properties and add new properties to the built-in objects.

In an ASP.NET page, you should not modify the prototype property of any built-in object. Any such modification will fail, silently in some cases, generating errors in other cases. Inside a <%...%> block, the two methods for declaring a new prototype method (using the function Object.prototype.method syntax, or using explicit assignment to the prototype property) fail silently. Declaring a prototype function using the function Object.prototype.method syntax in a <script runat=server> block fails with an error. (You cannot make assignments inside a <script runat=server> block.)

It is easy to tell that ASP.NET failed to add a method to the prototype property because a call to the supposedly added method generates an error. Detecting the failure to modify default methods is more difficult; you must check the program output.

For example, in an ASP page, you can add a new method to the Date object to return the month number and override the default toString method.

<%@ language=jscript %>
<html><body>
<%
   // Declare a monthNum to be added to all Date objects.
   function monthNum() {
      return this.getMonth() + 1;
   }
   Date.prototype.monthNum = monthNum
   // Override the default toString method for all Date objects.
   function Date.prototype.toString() {
      return this.monthNum() + "/" +
             this.getDate() + "/" +
             this.getYear();
   }
   // Define a date.
   var dt = new Date();
   // Call the expando method.
   Response.Write("The month is " + dt.monthNum() +".<BR>\n");
   // Use the toString method implicitly.
   Response.Write("Date is " + dt +".<BR>\n");
%>
</body></html>

Because the preceding code modifies the prototype method of the built-in object, it does not work correctly for an ASP.NET page. To verify this, change the extension of the page from .asp to .aspx and reload the page, which generates the run-time error Microsoft.JScript.JScriptException: Function expected.

If you were to add debug=true to the page directive (the <%@...%>), you would find that the error occurs when calling the monthNum method of the dt object. This demonstrates that the modifications to the prototype method of the Date object silently failed; the compiler did not add the monthNum method or override the toString method.

To fix the code in the ASP.NET page, redefine the methods as normal functions that accept a Date object as a parameter. This involves replacing the this statement with the parameter name you choose. Then replace each call to one of the new or overridden methods with a call to the equivalent normal function, passing the Date object as the parameter.

After applying these changes and moving the definitions to a <script runat=server> block, the page above becomes:

<%@ language=jscript %>
<html><body>
<script runat=server>
   // Declare a monthNum function which takes Date objects.
   function monthNum(value : Date) : int {
      return value.getMonth() + 1;
   }
   // Declate a new toString function for Date objects.
   function datetoString(value : Date) : String {
      return monthNum(value)+ "/" +
             value.getDate() + "/" +
             value.getYear();
   }
   // Define a date.
   var dt = new Date();
</script>
<%
   // Call the monthNum function.
   Response.Write("The month is " + monthNum(dt) +".<BR>\n");
   // Call the new datetoString function explicitly.
   Response.Write("Date is " +  datetoString(dt) +".<BR>\n");
%>
</body></html>

You can also add override methods and add new methods to individual instances of built-in objects in ASP pages. Assigning a function to an existing method of an object overrides that method, while assigning a function to a new method of an object creates a new expando property.

Code in ASP.NET pages cannot make these types of modifications to built-in objects. However, you can rewrite the methods as helper functions as in the previous section.

It is a good idea to search for all instances of the modified methods and to make sure to modify all references to them.

Note   It is difficult to find all calls to methods, such as toString and toNumber, which the compiler brings into play automatically. In addition, code in your ASP page may call built-in methods that subsequently call the overridden methods of built-in objects. When upgrading a page that overrides the methods of built-in objects, you must carefully track how the methods are used, as well as verify that the upgraded page retains the same functionality.

Extending User-Defined Objects

ASP and ASP.NET pages support user-defined objects. Everything that you can do with user-defined objects in ASP also applies in ASP.NET. For example, you can modify the prototype property of, and add expando properties to, user-defined objects.

However, there is a change in how to define constructor functions for user-defined objects. In ASP pages, a constructor function is the same as any other type of function, with the exception that it may contain a this statement. Simply copying a constructor function from an ASP page into an <script runat=server> block in an ASP.NET page generates errors, because ASP.NET requires the expando modifier to identify constructor functions.

For example, this ASP page uses the prototype method of a user-defined object to add a PI property and an area method.

<%@ language=jscript %>
<html><body>
<%
   // Define the constructor.
   function Circle (radius) {
      this.r = radius;
   }
   // Declare an area method for all Circle objects.
   function Circle.prototype.area () {
      // The formula for the area of a circle is pi*r^2.
      return this.pi * this.r * this.r; 
   }
   Circle.prototype.pi = Math.PI;
   // Create a new circle.
   var ACircle = new Circle(2);
   // Display the area of the circle.
   Response.Write("Area of the circle is " + ACircle.area() +".<BR>\n");
%>
</body></html>

The page above "just works" as an ASP.NET page. However, if you follow the suggestion about moving function declarations to a <script runat=server> block, the code generates the error JS1151: Objects of type 'pagename_aspx' do not have such a member. The compiler indicates that the error occurs on the line containing the this.r statement. What is going on here?

It turns out that for function declarations in <script runat=server> blocks, the this statement is interpreted as a reference to the ASP.NET page instead of to the expando object. When the this statement is used to access a member that is not part of the page (in this case, a variable r), the compiler returns an error. Use the expando modifier to instruct the compiler that the function is used in connection with a user-defined object, and that the this statement refers to that object.

Adding the expando modifier to both functions that use the this statement fixes the original error, but the compiler generates a new error, JS1110: Expression must be a compile time constant.

The compiler indicates that the error occurs on the line containing the definition of the Circle.prototype.area method. This occurs because all assignments to the prototype object must appear inside <%...%> or another function; they are not allowed at the top level of a <script runat=server> block. One way to fix this error is to define the method as a normal expando function in a <script runat=server> block and add that function to the prototype object in the Page_Init method or in a <%...%> block.

Applying these fixes, the code now reads:

<%@ language=jscript %>
<html><body>
<script runat=server>
   // Define the constructor.
   expando function Circle (radius) {
      this.r = radius;
   }
   // Declare an area method for all Circle objects.
   expando function CircleArea () {
      // The formula for the area of a circle is pi*r^2.
      return this.pi * this.r * this.r; 
   }
</script>
<%
   Circle.prototype.pi = Math.PI;
   Circle.prototype.area = CircleArea;
   // Call the area function on a Circle object.
   // Create a new circle.
   var ACircle = new Circle(2);
   // Display the area of the circle.
   Response.Write("Area of the circle is " + ACircle.area() +".<BR>\n");
%>
</body></html>

Class-based objects, which are new features of JScript .NET in ASP.NET, tightly integrate scripts with the .NET Framework for increased performance and reliability.

COM Interop

You can upgrade an ASP page that uses COM components to an ASP.NET page, but you should be aware of some subtle points. Code in an ASP.NET page does not call COM components directly; instead, the .NET Framework creates a runtime callable wrapper (RCW) that serves as an interface between the managed code in the ASP.NET page and the unmanaged code in the COM component. For more information, see Microsoft .NET/COM Migration and Interoperability.

The exact details of how the RCW works are somewhat complicated, but the important point is that ASP.NET automatically creates the RCW and, in most cases, the code from an ASP page that uses COM objects continues to work in an ASP.NET page. However, in a few cases you must modify the code. For more information, see Calling COM Components from .NET.

In addition, JScript .NET provides new features that allow you to call all COM objects directly from JScript code. In ASP pages, you must use VBScript to call COM components that take parameters by reference, or to test values that may hold a database null. The new functionality of JScript .NET is not only useful but required, since you cannot use two different programming languages on the same ASP.NET page.

Objects Returned by COM Components Have No Default Properties

A COM component may specify a default property, but the .NET Framework does not call these properties automatically. To compensate, you must call default properties explicitly.

For example, to display the value of the field returned by rs.Fields("Name") in an ASP page, where rs is an ADO Recordset object, you could use:

<%= rs.Fields("Name") %>

This code is equivalent to the more verbose version:

<%= rs.Fields.Item("Name").Value %>

In an ASP page, both pieces of code work, since Item is the default property of the Fields collection, and Value is the property of the Item property.

When using the first, terse line of code in an ASP.NET page, the type of the object (System.__ComObject) is displayed instead of the default property. This is because the RCW does not recognize default properties; you must call them explicitly. You must use the second, verbose line of code to get the expected behavior in ASP.NET.

Can Pass by Reference with JScript .NET

One common reason for including some VBScript in an ASP page is to pass variables to COM components by reference. However, JScript .NET introduces the & operator, which passes a variable by reference when calling a function.

For example, consider the ADO COM object, which has a method named NextRecordset, which accepts a by reference parameter named RecordsAffected.

To get the value of RecordsAffected in an ASP page, you must call the method using VBScript. To access the return values of the method in JScript, you can use a wrapper function written in VBScript that stores the value of the ByRef parameter in an auxiliary variable.

<script runat=server language=VBScript>
Function nextRecordsetWrapper (recset)
   ' This function returns the value of the method and stores the 
   ' return value of the RecordsAffected in recAffected.
   Dim tempRecAffected
   tempRecAffected = recAffected
   nextRecordsetWrapper = recset.NextRecordset (tempRecAffected)
   recAffected = tempRecAffected
End Function
</script>
<%
// Code that opens the connection to the database
// and stores the Recordset in recset
...
// Define the variable to store the ByRef parameter.
var recAffected;
// Call the wrapper function. 
recset = nextRecordsetWrapper(recset);
// The value of recAffected is now the same as if 
// the recset.NextRecordset(recAffected) was called.
%>

You must rewrite the preceding code for use in ASP.NET, since ASP.NET does not support mixed languages. You may write the entire page using only JScript .NET, since you can use the & operator to pass variables by reference in JScript .NET.

<script runat=server language=JScript>
// Define the variable to store the ByRef parameter.
var recAffected;
</script>
<%
// Call the COM component method directly.
// Use & to pass by reference.
recset = recset.NextRecordset(&recAffected);
%>

Manipulating Data with JScript .NET

The other common reason for using VBScript is to manipulate data types that JScript cannot handle directly. Several examples are: null pointers, SafeArray data, and VT_DATE values. Although JScript has analogues of each of these data types, they do not interoperate cleanly, so you must use VBScript code to manipulate them.

However, the JScript .NET data types interoperate well with code written in other languages, such as COM objects or HTML controls. See Dealing with the Cookies Collection below for an example of where you must use VBScript in an ASP page, and how you can write the ASP.NET page entirely in JScript .NET.

Changes to the ASP.NET Object Model

The objects exposed by ASP.NET are instances of classes or derived classes from the .NET Framework Class Library. This differs from ASP, which exposes objects as Variants. In most cases, objects that ASP.NET exposes are backwards compatible. This section presents a few of the situations where you might encounter problems.

Renaming Methods

In ASP, the capitalization of Server methods is unimportant. For example, calling Server.URLEncode is the same as calling Server.UrlEncode. However, in an ASP.NET page, the engine interprets the two calls as calls to different methods. How can you determine the correct capitalization? Just use the casing convention specified by the .NET Framework.

The casing convention specifies capitalization of only the first letter of each word. This rule applies even when the words are normally all uppercase, such as URL or HTML. A benefit of this convention is that it helps you to parse the meaning of an identifier. For example, the convention specifies that the name of the method that encodes a URL is UrlEncode.

When upgrading, if you encounter a JS1151 error when attempting to call a method, first check capitalization of the method name. For example, to access the method for URL encoding strings from the Server object, you can use Server.URLEncode in an ASP page, but you must use Server.UrlEncode in an ASP.NET page. Other examples of proper capitalization for ASP.NET pages are Server.UrlDecode and Server.HtmlEncode.

If code in an ASP.NET page attempts to call Server.URLEncode, the compiler returns the line number of the call and generates the error JS1151: Objects of type 'System.Web.HttpServerUtility' do not have such a member. This error indicates that the Server object is an instance of the .NET Framework System.Web.HttpServerUtility class. You can use that information to look up the members of the class in the documentation and find the correct capitalization of the method.

Changes in What Objects Are Returned

The Request object in ASP.NET returns different data in a different format than in ASP. In particular, in an ASP page, the Request(name), Request.QueryString(name), and Request.Form(name) methods each return a collection of items. In an ASP.NET page, each returns a single String value.

For example, in ASP the following code checks if a form on the page has been submitted:

if(Request.QueryString("username").Count == 0)

This works because Request.QueryString(name) returns a collection of items in ASP, and you can check the Count property to see if it is empty (that is, if the form had been submitted or not).

In ASP.NET, Request.QueryString(name) returns a single String value so you cannot check the Count property. To retrieve the collection, you must use the Requst.QueryString.GetValues(name) method. If your only interest is determining if the form has been submitted, you can check to see if the value of Request.QueryString(name) is not null. Rewriting the line of code above to work in ASP.NET gives:

if(Request.QueryString("username") == null)

Dealing with the Cookies Collection

ASP.NET imposes more uniform rules on how you can access the Cookies collection than ASP. In ASP.NET, the elements of the Cookies collection are objects that you can manipulate independently from the collection. In ASP, on the other hand, elements of the Cookies collection are an integral part of the collection.

To clearly see the difference, consider the following function that sets a cookie in an ASP page. Note that the JScript code calls a VBScript function that returns the date in the correct format.

<script language=VBScript runat=server>
Function tomorrow ()
   tomorrow = Date() + 1 ' Set the date to tomorrow.
End Function
</script>
<script language=JScript runat=server>
function setcookie(name, value) {
   Response.Cookies(name) = value;
   Response.Cookies(name).Expires = tomorrow();
}
</script>

This function does not work in an ASP.NET page. First, you must remove the VBScript block, since ASP.NET requires that all the code on a page be in the same language. Since the data types in JScript .NET interoperate with the data types in other languages, you can use JScript to rewrite the tomorrow function.

After making that change, the code produces a new error, JS5040: 'Response.Cookies' is read-only. This occurs because you cannot directly modify the Response.Cookies object in ASP.NET. You must use the methods of the Response.Cookies object to modify the Cookies collection. To add a new cookie, first initialize a new HttpCookie object, and then use the Add method to add a new cookie object to the collection. After making these changes, the function becomes:

<script language=JScript runat=server>
function tomorrow() {
   var d = new Date;
   d.setDate(d.getDate() + 1);
   return d;
}
function setcookie(name, value) {
   var objCookieObject : HttpCookie;
   objCookieObject = new HttpCookie(name)
   objCookieObject.Value = value;
   objCookieObject.Expires = tomorrow();
   Response.Cookies.Add(objCookieObject)
}
</script>

For more information about the Cookies collection, see HttpCookieCollection Class.

Changes to the ASP.NET Security Model

The ASP worker process that executes code in ASP pages runs by default under the local System Windows® security account, the same account used by IIS. This account has administrative security privileges, which means that that any malicious code that runs within the worker process can effectively perform administrative level tasks on the machine.

To add another layer of security to machines running ASP.NET (in addition to the new security features inherent to the .NET Framework), the ASP.NET worker process runs by default under an account named ASPNET. The program that installs ASP.NET creates the ASPNET account automatically (with cryptographically strong random password), and assigns it local Users security privileges. An account in the Users group has far fewer privileges than an account in the local System group, and the weaker account is suitable for use in most Web applications.

Note   In Beta releases of ASP.NET, the worker process identity was local System.

This security change also restricts how you can use ASP.NET. Some problems you may encounter while upgrading are listed below. In most cases, you can resolve these problems by running the ASP.NET worker process under a System account. However, this may not be an appropriate strategy for your application. You should be aware that running code as System means that it runs as a member of the Administrators group. For more information about making the changes listed below, or for information related to other scenarios in which security restrictions prevent your page from working, see the .NET Framework Upgrade Page.

Accessing Resources

The ASPNET account has access to the same resources as the Users group. In addition, it has read permissions for the Framework installation directory (installed by default at %windir%\Microsoft.NET\Framework\%FrameworkVersion%), full control of the Temporary ASP.NET files subdirectory in the Frameworks installation directory, and full control to the %windir%\Temp directory. If you have an application that needs to access additional files or directories on the server, then you must explicitly grant the access privileges for the local ASPNET account.

The ASPNET account cannot use the Microsoft® Active Directory® Service Interfaces (ADSI) to administer the network. In addition, it cannot access information on remote machines, which prevents the ASP.NET worker process from reading remote shares by default.

Impersonation

Impersonation enables the ASP (or ASP.NET) worker process to run using the security privileges of the entity requesting the page from the worker process. ASP.NET does not support impersonation by default, because impersonation requires the ASP.NET worker process to run under an account with administrative privileges.

If you want to upgrade an ASP page that uses impersonation to an ASP.NET page, be sure to carefully consider the consequences, and whether the application really requires impersonation. To use impersonation in ASP.NET, you must change the worker process identity back to the more privileged local System account.

WMI and System.Management

WMI (System.Management) allows you to manage and monitor Windows machines, and create or administer user accounts. By default, you cannot use WMI from an ASP.NET application, because the ASPNET account does not have sufficient privileges. To enable WMI, you can either enable impersonation, or you can configure the ASPNET account to use interactive logons. Whereas impersonation allows full access to WMI and requires the worker process to run under the System account, interactive logins provide limited read-only access to WMI, while allowing the worker process to run under the ASPNET account.

Less Common Sources of Problems

The following are some less obvious differences between JScript and JScript .NET. Consider these differences during the upgrade process.

The Arguments Object Is Not Available

In JScript, an arguments object is available inside every function definition. This allows functions to accept an arbitrary number of arguments. The arguments object also provides a reference to the current function as well as to the calling function. The arguments object is unavailable in JScript .NET within an ASP.NET page. Use a parameter array when defining a function, to allow the function to accept an arbitrary number of arguments.

For example, the following function works in an ASP page:

function argTest() {
   var s = "Number of arguments passed: " + arguments.length + "<BR>\n";
   for (i =0 ; i < arguments.length; i++) // Get argument contents.
      s += "  Argument " + i + " = " + arguments[i] + "<BR>\n";
   // Return a list of the arguments.
   return(s);
}

However, because the function uses the arguments object, using argTest in an ASP.NET page causes the compiler to generate the error JS1135: Variable 'arguments' has not been declared.

You can use a parameter array to rewrite argTest to work in ASP.NET. Without going into the details, if ... arguments : Object[] is the only parameter of the function, the function can access all the arguments passed in the same way as it did with the arguments object. The function statement describes how to declare that a parameter should be used as a parameter array by using the "... name : typedArray" syntax.

function argTest(... arguments : Object[]) {
   var s = "Number of arguments passed: " + arguments.length + "<BR>\n";
   for (i =0 ; i < arguments.length; i++) // Get argument contents.
      s += "  Argument " + i + " = " + arguments[i] + "<BR>\n";
   // Return a list of the arguments.
   return(s);
}

For more information, see Function Statement.

Note   There is no simple way to get the name of the calling function in an ASP.NET page.

Functions Cannot Be Redefined

In ASP, functions declared with the function statement are treated in the same way as variables that hold a Function object. Any function identifier can store any type of data.

This ASP page shows how to change the behavior of a function.

<%@ language=jscript %><html><body>
<%
function printHello() {
   Response.Write("Hello!<BR>\n");
}
function printBye() {
   Response.Write("Bye!<BR>\n");
}
printHello = printBye;
printHello(); // Prints Bye!
%>
</body></html>

Since functions are constants in ASP.NET, you cannot redefine a function. If you use the page shown above as an ASP.NET page, the compiler generates the error JS5040: 'printHello' is read-only.

Use a variable to hold a reference to a function if you need to change the function.

<%@ language=jscript %><html><body>
<%
function originalprintHello() {
   Response.Write("Hello!<BR>\n");
}
var printHello = originalprintHello;
function printBye() {
   Response.Write("Bye!<BR>\n");
}
printHello = printBye;
printHello(); // Prints Bye!
%>
</body></html>

Cannot Assign to a Read-Only Variable, Field, or Method

Although you can write a statement that appears to assign a value to a read-only identifier in an ASP page, the assignment fails quietly. The only way to discover this failure is to check whether the value of the identifier has changed. You can imagine the confusion this could cause.

In ASP.NET, assigning a value to a read-only identifier generates the compile-time error JS5040: Assignment to read-only field or property. To fix the error, you can either remove the assignment or try assigning to an identifier that is not read only.

Large Pages

Large pages with a large amount of code inside <%...%> blocks (especially #include files) may never compile. When the common language runtime tries to compile all the code at one time (including all the nested methods), the process may consume all available memory if the size of all combined <%...%> blocks is large. To solve this problem, move methods out of <%...%> blocks and into <script runat=server> blocks.

Conclusions

A majority of the JScript code in existing ASP pages works as JScript .NET code in ASP.NET pages. However, you must be aware of a number of incompatibilities when upgrading. During the upgrade process, remember the following points:

  • Change the extension of the ASP page (.asp) to the ASP.NET extension (.aspx).
  • Declare all variables explicitly.
  • Make sure that you have placed the appropriate code in the appropriate blocks.
  • Do not add or modify the properties of the built-in objects, and use the expando modifier for the methods of user-defined objects.
  • Explicitly call the properties of objects returned by COM components.
  • Remember that the methods of the page object are case-sensitive, some methods return data in a different format than ASP pages, and the Cookies collection in ASP.NET is different from the Cookies collection in ASP.
  • A few JScript features (such as the arguments object, redefining functions, and quiet failure of assignments to read-only identifiers) are not supported by JScript .NET in ASP.NET pages.

Now that you know the basics about upgrading, you are ready to take advantage of the full power of JScript .NET and ASP.NET. You can use the server controls that the .NET Framework provides to easily perform complex tasks, such as showing custom calendars. Another advantage is that you can significantly boost the performance of your site by making additional changes.

An unexpected benefit of upgrading is that JScript .NET encourages good programming practices, which catch more code errors than JScript. As a result, you may uncover bugs present in your ASP pages. In addition, since JScript .NET complies with the common language specification, it is easy to take advantage of the many powerful new features included in ASP.NET and the .NET platform in general.

Additional Resources

The following related information in the MSDN Library may enhance your understanding of the upgrade process.

Five Steps to Getting Started with ASP.NET

Migrating to ASP.NET: Key Considerations

Remote Scripting in a .NET World

ASP.NET Configuration

WebControl Methods

Microsoft .NET/COM Migration and Interoperability

Calling COM Components from .NET

HttpCookieCollection Class

Function Statement

 

Scripting Clinic

Andrew Clinick is a senior program manager in the Microsoft Programmability group, so chances are, if there's script involved, he's probably had something to do with it.