Saturday, July 5, 2008

ASP.Net FAQs Set I

Do ASP.NET forms authentication cookies provide any protection against replay attacks? Do they, for example, include the client's IP address or anything else that would distinguish the real client from an attacker?
No. If an authentication cookie is stolen, it can be used by an attacker. It's up to you to prevent this from happening by using an encrypted communications channel (HTTPS). Authentication cookies issued as session cookies, do, however, include a time-out valid that limits their lifetime. So a stolen session cookie can only be used in replay attacks as long as the ticket inside the cookie is valid. The default time-out interval is 30 minutes. You can change that by modifying the timeout attribute accompanying the element in Machine.config or a local Web.config file. Persistent authentication cookies do not time-out and therefore are a more serious security threat if stolen.

By default, a persistent forms authentication cookie issued by ASP.NET is valid for 50 years. Is it possible to shorten that?
Yes. Unfortunately, there is no configuration setting you can tweak to customize the lifetime of a persistent authentication cookie, but you can customize it programmatically. Here's a snippet of code that returns a persistent authentication cookie from a forms login page and limits the cookie's lifetime to 7 days:

string url = FormsAuthentication.GetRedirectUrl ("Elmo", true);
FormsAuthentication.SetAuthCookie ("Elmo", true);
HttpCookie cookie = Response.Cookies[FormsAuthentication.FormsCookieName];
cookie.Expires = DateTime.Now + new TimeSpan (7, 0, 0, 0);
Response.Redirect (url);

To set the cookie's lifetime to something other than 7 days, simply modify the TimeSpan value.

I wrote an HTTP handler and registered it in the section of a local Web.config file, but the handler never gets called. What could be wrong?
In addition to being mapped to a file type (or specific file name) in a CONFIG file, an HTTP handler has to be registered in the IIS metabase. For example, if you register an HTTP handler with the Web.config file shown below, you also have to map *.igen to Aspnet_isapi.dll in the IIS metabase. Otherwise, ASP.NET doesn't see the request and can't forward it to the handler.

How do I send e-mail from an ASP.NET application?

MailMessage message = new MailMessage ();
message.From = "webmaster@wintellect.com";
message.To = "Everyone@wintellect.com";
message.Subject = "Scheduled Power Outage";
message.Body = "Our servers will be down tonight.";
SmtpMail.SmtpServer = "localhost";
SmtpMail.Send (message);
MailMessage and SmtpMail are classes defined in the .NET Framework Class Library's System.Web.Mail namespace. Due to a security change made to ASP.NET just before it shipped, you need to set SmtpMail's SmtpServer property to "localhost" even though "localhost" is the default. In addition, you must use the IIS configuration applet to enable localhost (127.0.0.1) to relay messages through the local SMTP service.

How do I read an image from a database using ADO.NET and display it in a Web page?
The following ASPX file reads and displays an image from the Pubs database that comes with Microsoft SQL Server.

<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Drawing" %>
<%@ Import Namespace="System.Drawing.Imaging" %>
<%@ Import Namespace="System.IO" %>

Some Web service classes derive from System.Web.WebServices; others do not. What's the deal?
WebService contributes properties named Application, Session, Context, Server, and User to derived classes enabling Web services to access the ASP.NET objects of the same name. If you don't use these objects in your Web service-for example, if you don't use application state or session state-then you don't have to derive from WebService, either. Incidentally, if you want to use ASP.NET session state in a Web method, use the following WebMethod attribute to enable session state for that method:

[WebMethod (EnableSession="true")]

What are VSDISCO files?
VSDISCO files are DISCO files that support dynamic discovery of Web services. If you place the following VSDISCO file in a directory on your Web server, for example, it returns references to all ASMX and DISCO files in the host directory and any subdirectories not noted in elements:

How does dynamic discovery work? ASP.NET maps the file name extension VSDISCO to an HTTP handler that scans the host directory and subdirectories for ASMX and DISCO files and returns a dynamically generated DISCO document. A client who requests a VSDISCO file gets back what appears to be a static DISCO document.
Note that VSDISCO files are disabled in the release version of ASP.NET. You can reenable them by uncommenting the line in the section of Machine.config that maps *.vsdisco to System.Web.Services.Discovery.DiscoveryRequestHandler and granting the ASPNET user account permission to read the IIS metabase. However, Microsoft is actively discouraging the use of VSDISCO files because they could represent a threat to Web server security.

How does a Web service client call Web methods asynchronously?
Web service proxy classes generated by Wsdl.exe contain asynchronous as well as synchronous versions of the Web service's methods. Suppose a Web service implements the following Add method:
[WebMethod]
public int Add (int a, int b)
{
return a + b;
}


A proxy generated by Wsdl.exe has BeginAdd and EndAdd methods for calling Add asynchronously. Assuming calc is an instance of the proxy class, here's how a client calls Add asynchronously:

// Initiate an async call
IAsyncResult res = calc.BeginAdd (2, 2, null, null);
.
.
.
// Get the results
int sum = calc.EndAdd (res);

If the call hasn't completed when EndAdd is called, EndAdd blocks until it does. If desired, a client can ask to be notified when an asynchronous call returns by providing a reference to an AsyncCallback delegate wrapping a callback method. In the next example, EndAdd won't block because it isn't called until the client is certain the method call has returned:


AsyncCallback cb = new
AsyncCallback (AddCompleted);
IAsyncResult res = calc.BeginAdd (2, 2, cb, null);
.
.
.
public void AddCompleted (IAsyncResult res)
{
int sum = calc.EndAdd (res);
}

Another option is to use the IsCompleted property of the IAsyncResult interface returned by BeginAdd to determine whether the call has completed and avoid calling EndAdd until it does:

IAsyncResult res = calc.BeginAdd (2, 2, null, null);
.
.
.
if (res.IsCompleted) {
int sum = calc.EndAdd (res);
}
else {
// Try again later
}


I wrote code that uses the SmtpMail and MailMessage classes to send e-mail from an ASP.NET application. The code worked fine in beta 2, but it throws an exception in the release version of ASP.NET. What's wrong?
Please see FAQ "How do I send e-mail from an ASP.NET application?" (http://www.wintellect.com/resources/faqs/default.aspx?faq_id=1&page=4#4)

How do I upload files to Web pages in ASP.NET?
Use the HtmlInputFile class, which you can declare an instance of with an tag. The following example is a complete ASPX file that lets a user upload an image file and a comment descibing the image. The OnUpload method writes the image and the comment to a table named Pictures in a SQL Server database named MyPictures.


<%@ Import Namespace="System.Data.SqlClient" %>

Top of Form

File name

Comment




How do I create an ASPX page that periodically refreshes itself?
Most browsers recognize the following META tag as a signal to automatically refresh the page every nn seconds:

Here's an ASPX file that displays the current time of day. Once displayed, it automatically refreshes every 5 seconds:
<%@ Page Language="C#" %>

<% Response.Write (DateTime.Now.ToLongTimeString ()); %>

How can an ASP.NET application determine whether cookies are enabled in a browser?
Determining whether cookies are enabled requires a round trip to the browser and back. If you can live with an extra round trip, the basic strategy is to return a cookie in an HTTP response and redirect to a page that checks for the cookie. Here's a page that does just that:

And here's the page that it redirects to (OtherPage.aspx). This page uses the presence or absence of the cookie to determine whether cookies are enabled and displays the result:

<%@ Page Language="C#" %>

<% HttpCookie cookie = Request.Cookies["Foo"]; if (cookie != null && cookie.Value == "Bar") Response.Write ("Cookies are enabled"); else Response.Write ("Cookies are not enabled"); %>

Is it possible to prevent a browser from caching an ASPX page?
You bet. Just call SetNoStore on the HttpCachePolicy object exposed through the Response object's Cache property, as demonstrated here:
<%@ Page Language="C#" %>
<% Response.Cache.SetNoStore (); Response.Write (DateTime.Now.ToLongTimeString ()); %>

SetNoStore works by returning a Cache-Control: private, no-store header in the HTTP response. In this example, it prevents caching of a Web page that shows the current time.

How do I create a DataGrid with Delete buttons and pop up a message box asking the user for confirmation before deleting a record?
The ASPX file below demonstrates the proper technique. It populates a DataGrid with content from the Titles table of the Pubs database that comes with Microsoft SQL Server. The DataGrid's leftmost column contains a row of Delete buttons. The OnDeleteRecord method simulates a record deletion by writing the Title field of the record to be deleted to a Label control. The OnAttachScript method, which is called once for each row in the DataGrid in response to ItemCreated events, attaches to each button an OnClick attribute that activates a bit of client-side JavaScript. That script displays a confirmation dialog and prevents the page from posting back to the server (thus preventing OnDeleteRecord from being called) if the user clicks Cancel rather than OK.

<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>

How do I localize an ASP.NET application so that it formats dates and times based on the requestor's locale?
Deploy the following Global.asax file in the application's virtual root:


<%@ Import Namespace="System.Threading" %>
<%@ Import Namespace="System.Globalization" %>

Application_BeginRequest executes at the beginning of each and every request. This example reads the requestor's preferred language from the Request object's UserLanguage property (which is populated with information found in the request's Accept-Language header), creates a CultureInfo object from it, and assigns the CultureInfo object to the CurrentCulture property of the thread that's processing the request. FCL methods that are culture-aware (such as DateTime.ToShortDateString) will format dates, times, currency values, and numbers accordingly.

What does AspCompat="true" mean and when should I use it?
AspCompat is an aid in migrating ASP pages to ASPX pages. It defaults to false but should be set to true in any ASPX file that creates apartment-threaded COM objects--that is, COM objects registered ThreadingModel=Apartment. That includes all COM objects written with Visual Basic 6.0. AspCompat should also be set to true (regardless of threading model) if the page creates COM objects that access intrinsic ASP objects such as Request and Response. The following directive sets AspCompat to true:


<%@ Page AspCompat="true" %>

Setting AspCompat to true does two things. First, it makes intrinsic ASP objects available to the COM components by placing unmanaged wrappers around the equivalent ASP.NET objects. Second, it improves the performance of calls that the page places to apartment-threaded COM objects by ensuring that the page (actually, the thread that processes the request for the page) and the COM objects it creates share an apartment. AspCompat="true" forces ASP.NET request threads into single-threaded apartments (STAs). If those threads create COM objects marked ThreadingModel=Apartment, then the objects are created in the same STAs as the threads that created them. Without AspCompat="true," request threads run in a multithreaded apartment (MTA) and each call to an STA-based COM object incurs a performance hit when it's marshaled across apartment boundaries.
Do not set AspCompat to true if your page uses no COM objects or if it uses COM objects that don't access ASP intrinsic objects and that are registered ThreadingModel=Free or ThreadingModel=Both.

I've developed a custom Windows Forms control that I'd like to use in a Web Form. I've heard that ASP.NET can use Windows Forms controls. Is that true? If so, how do I create a Windows Forms control in a Web Form?
You can embed Windows Forms controls in Web pages using
tags similar to those that declare ActiveX controls. To demonstrate, here's the source code for a very simple slider control-one that derives from the FCL's TrackBar class:
namespace Wintellect
{
public class WebSlider : System.Windows.Forms.TrackBar {}
}

Compile this source code into a DLL with the following command (assuming the source code file is named Controls.cs):

csc /t:library controls.cs

Copy the resulting DLL (Controls.dll) to the virtual directory of your choice on your Web server. Now create a text file named Slider.aspx in the same directory and add the following HTML:

The tag declares an instance of the control and names it Slider. It also uses tags to initialize some of the control's properties. Open the ASPX file in your browser and the slider control should be displayed. IE downloads the control implementation from the Web server, so you don't have to install Controls.dll on the client. The client must, however, have the .NET Framework installed. Internet Explorer 5.01 or higher is required on the client, too.

If I use the slider control in the previous example in a Web page, what must I do to allow a server-side event handler to determine the position of the slider's thumb?
The trick is to intercept the form submit event fired before the form posts back to the server and add the thumb position to the form's postback data. Here's a modified version of the ASPX file in the previous example that does just that. The onsubmit attribute in the tag calls the JavaScript function SubmitForm before the form posts back to the server. SubmitForm writes the slider's thumb position to a hidden control named __THUMBPOS. The browser submits the __THUMBPOS control's value to the server, and the server-side event handler extracts the value from the request. In this example, the event handler writes the thumb position to the Web page.



If I use the slider control in the previous example in a Web page, the slider's thumb snaps back to its default position each time the page posts back to the server. Is there a way to make the thumb stay put?
The ASPX file below shows one way to do it. The tag that controls the slider's thumb position is no longer embedded in the page's HTML; instead, it's output programmatically with Response.Write. That enables the page to emit a tag containing a default value if the page is fetched outside of a postback, or a tag containing the __THUMBPOS value submitted in the request if the page is being returned following a postback. It's not pretty, but it works.
<%@ Page Language="C#" %>





How can I create a DataGrid that displays a column of images obtained from a database?
The following ASPX file demonstrates how:
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>

This ASPX file contains a DataGrid that displays data from the Employees table of SQL Server's Northwind database. Each row rendered by the DataGrid represents one employee, and each row's leftmost column contains the employee's picture. The picture comes from the table's Photo field. The image is rendered by the tag, which emits an tag accompanied by a src attribute that points to a file named NorthwindImageGrabber.ashx. Inside the ASHX file is an HTTP handler that retrieves an image from the database. A query string appended to the URL tells the handler which image to retrieve. Here's NorthwindImageGrabber.ashx's source code:

<%@ WebHandler Language="C#" Class="ImageGrabber" %>

using System;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;
using System.Data.SqlClient;
using System.IO;

public class ImageGrabber : IHttpHandler
{
public void ProcessRequest (HttpContext context)
{
string id = (string) context.Request["id"];

if (id != null) {
MemoryStream stream = new MemoryStream ();
SqlConnection connection = new SqlConnection
("server=localhost;database=northwind;uid=sa;pwd=");
Bitmap bitmap = null;
Image image = null;

try {
connection.Open ();
SqlCommand cmd = new SqlCommand
("select photo from employees where employeeid='" +
id + "'", connection);
byte[] blob = (byte[]) cmd.ExecuteScalar ();

stream.Write (blob, 78, blob.Length - 78);
bitmap = new Bitmap (stream);

// Shrink the image, but maintain its aspect ratio
int width = 48;
int height = (int) (width *
((double) bitmap.Height / (double) bitmap.Width));
image = bitmap.GetThumbnailImage (width, height, null,
IntPtr.Zero);

context.Response.ContentType = "image/jpeg";
image.Save (context.Response.OutputStream, ImageFormat.Jpeg);
}
finally {
if (image != null)
image.Dispose ();
if (bitmap != null)
bitmap.Dispose ();
stream.Close ();
connection.Close ();
}
}
}

public bool IsReusable
{
get { return true; }
}
}


The ProcessRequest method, which is called every time the ASHX file is requested, retrieves the image from the database and returns it to the client as a JPEG. For good measure, it also shrinks the image down to thumbnail size using Image.GetThumbnailImage. NorthwindImageGrabber.ashx discards the first 78 bytes of each image because the Northwind database's Photo field doesn't store raw images; it stores BMP bitmaps prefixed by 78 bytes of unrelated header information.
Is it possible to write an ASP.NET handler that works like an ISAPI filter-that is, that sees requests and responses and perhaps modifies them, too?
You can do it by writing an HTTP module-a class that implements IHttpModule-and registering it in Web.config. Here's a simple HTTP module written in C# that appends "Hello, world" to every response:
using System;
using System.Web;

public class MyModule : IHttpModule
{
public void Init (HttpApplication application)
{
application.EndRequest += new EventHandler (Application_EndRequest);
}
void Application_EndRequest (Object source, EventArgs e)
{
HttpApplication application = (HttpApplication) source;
HttpContext context = application.Context;
context.Response.Write ("Hello, world");
}

public void Dispose ()
{
}
}

Here's how you register it if MyModule is in an assembly named CustomModules:


An HTTP module can handle the per-request events fired by HttpApplication instances, and it can fire events of its own that can be processed in Global.asax. To deploy the module, simply drop the DLL containing MyModule into the application root's bin subdirectory.

How can ASP.NET apps transmit data from one page to another?
One way to transfer data from page to page is to have the sending page encode the data in a query string and the receiving page read the data from the request. Here's the source code for a page named PageOne.aspx that encodes a string typed by the user in a query string and passes it to PageTwo.aspx:

And here's the page it redirects to, which echoes what the user typed:

<%@ Page Language="C#" %>

<% Response.Write ("You typed \"" + Request["Input"] + "\""); %>

Another way to pass data from one page to another--a technique that has the added benefit of keeping the data on the server and not exposing it to the user--is to pass the data in session state, as demonstrated here:

<%@ Page Language="C#" %>

<% Response.Write ("You typed \"" + Session["Input"] + "\""); %>

If you use Server.Transfer rather than Response.Redirect to transfer control to another page, you can use public fields in the sending page's code-behind class to transmit data. The following example demonstrates how:
<%@ Page Inherits="PageOneClass" %>

// PageOneClass.cs
using System;
using System.Web.UI;
using System.Web.UI.WebControls;

public class PageOneClass : Page
{
public string _Input;
protected TextBox Input;
public void OnNextPage (Object sender, EventArgs e)
{
_Input = Input.Text;
Server.Transfer ("PageTwo.aspx");
}
}
<%@ Page Language="C#" %><% PageOneClass prevpage = (PageOneClass) Context.Handler; Response.Write ("You typed \"" + prevpage._Input + "\""); %>
How do I display an ASPX or HTML page in a new browser window in ASP.NET?
The following tag creates a hyperlink that, when clicked, opens an ASPX file in a new window:
How do I initialize a TextBox whose TextMode is "password" with a password? Initializing the TextBox's Text property doesn't seem to work.
This won't work:
But this will:

The latter code fragment manually adds a value="imbatman" attribute to the tag output by the TextBox control, causing the specified text to appear in the TextBox.
You can also initialize a password TextBox by including a Value attribute in the control tag, as demonstrated below:

I know I can write custom server controls by deriving from Control or WebControl. But can I modify the behavior of existing controls by deriving from them and modifying their output?
You bet. Here's a custom control named NumTextBox that derives from TextBox and adds an onkeydown attribute to the tag that TextBox outputs. That attribute references a local JavaScript function that filters out non-numeric keys, producing a TextBox that accepts only numbers. For good measure, NumTextBox senses the browser type and renders differently to Internet Explorer and Netscape Navigator, enabling it to work in either browser. It also overrides TextBox's Text property and implements a set accessor that throws an exception if a non-numeric string is written to it.

using System;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Wintellect
{
public class NumTextBox : TextBox
{
string IEClientScriptBlock =
"";

string NetscapeClientScriptBlock =
"";

public override string Text
{
get { return base.Text; }
set
{
// Make sure value is numeric before storing it
Convert.ToInt64 (value);
base.Text = value;
}
}

protected override void OnPreRender (EventArgs e)
{
string browser = Context.Request.Browser.Type.ToUpper ();
int version = Context.Request.Browser.MajorVersion;

if (browser.IndexOf ("IE") > -1 && version >= 4)
Page.RegisterClientScriptBlock ("NumTextBoxScript",
IEClientScriptBlock);
else if (browser.IndexOf ("NETSCAPE") > -1 && version >= 4)
Page.RegisterClientScriptBlock ("NumTextBoxScript",
NetscapeClientScriptBlock);
}

protected override void Render (HtmlTextWriter writer)
{
string browser = Context.Request.Browser.Type.ToUpper ();
int version = Context.Request.Browser.MajorVersion;

if (browser.IndexOf ("IE") > -1 && version >= 4)
writer.AddAttribute ("onkeydown",
"javascript:return isKeyValid (window.event.keyCode)");
else if (browser.IndexOf ("NETSCAPE") > -1 && version >= 4)
writer.AddAttribute ("onkeydown",
"javascript:return isKeyValid (event.which)");

base.Render (writer);
}
}
}

Here's an ASPX file you can use to test the control. It assumes that the control is compiled into an assembly named NumTextBoxControl.

<%@ Register TagPrefix="win" Namespace="Wintellect" Assembly="NumTextBoxControl" %>


Is it possible to associate hidden values--say, values from an identity field in a database table--with items in a DataGrid?
You bet. Just declare a BoundColumn in the DataGrid and set its Visible property to false, like so:

The column won't show up in the DataGrid, but you'll be able to read data from it following a postback just as if it were visible.

How do I configure a DataGrid to show a column of row numbers: 1, 2, 3, 4, and so on?
The easiest way to do it is to use a TemplateColumn. The following ASPX file demonstrates how. The TemplateColumn displays the value of a rownum field that is incremented each time a row is output.
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>

<%# rownum++ %>

Is it possible to call Fill on a DataAdapter and fill two DataTables in a DataSet with a single call?
You bet. Here's a sample that demonstrates how by performing a double query and binding each of the resulting DataTables to a different DataGrid.

<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>

I'm trying to use Server.CreateObject to instantiate a legacy COM component in an ASPX page. If I use VB.NET, I can create and call the COM object just fine. But the same code written in C# doesn't compile. The compiler complains that the method I'm calling isn't a member of Object. What gives?
You've discovered an interesting feature of VB.NET--namely, that it trades type safety for simplicity when late binding to COM objects. Check out the following VB.NET code sample, which instantiates a COM object (ProgID="Wintellect.Math") and calls its Add method to add 2 and 2:

Dim Sum As Integer
Dim WinMath As Object
WinMath = Server.CreateObject ("Wintellect.Math")
Sum = WinMath.Add (2, 2)

This code works just fine, despite that fact that Add is not a member of System.Object. The VB.NET compiler relaxes its type-checking rules to simplify your code. The C# compiler, however, does not. The following code won't compile:

Object math = Server.CreateObject ("Wintellect.Math")
int sum = WinMath.Add (2, 2)
The solution for C# programmers is to late-bind to the COM object using System.Type.InvokeMember. Here's the C# equivalent of the VB.NET code above:

Type t = Type.GetTypeFromProgID ("Wintellect.Math");
Object math = Server.CreateObject (t);
Object[] args = { 2, 2 };
int sum = (int) t.InvokeMember ("Add", BindingFlags.InvokeMethod, null, math, args);

It's not pretty, but it works, and it perfectly illustrates the extra effort required to accomplish late binding in C#.

I'm trying to use ASP.NET's HtmlInputFile control to upload files to a Web server, but the control's PostedFile property is always null—even after I select a file and post back to the server. What am I doing wrong?
Most likely you forgot to include an enctype="multipart/form-data" attribute in your tag. The following HTML form doesn't support file uploads:

But the next one does. Decorate the tag as shown here and file uploads will work just fine:

No comments: