Come around 9pm. Fancy dress is optional
Repeat the process to create the sorryyoucantcome.html file and set the contents to match Listing 1-12.It won't be the same without you. Maybe next year.
Name | Phone | |
---|---|---|
{0} | {1} | {2} | ", rsvp.Name, rsvp.Email, rsvp.Phone) Response.Write(htmlString) Next%>
Name | Phone | |
---|---|---|
{0} | {1} | {2} | ", rsvp.Name, rsvp.Email, rsvp.Phone) Response.Write(htmlString) Next%>
Name | Phone |
---|
Name | Height (m) |
---|---|
Everest | 8848 |
Aconcagua | 6962 |
McKinley | 6194 |
Kilimanjaro | 5895 |
K2 | 8611 |
Name | Height (m) |
---|---|
Everest | 8848 |
Aconcagua | 6962 |
McKinley | 6194 |
Kilimanjaro | 5895 |
K2 | 8611 |
Name | Height (m) |
---|---|
Everest | 8848 |
Aconcagua | 6962 |
McKinley | 6194 |
Kilimanjaro | 5895 |
K2 | 8611 |
Name | Height (m) |
---|
Quantity | Item | Price | Subtotal |
---|---|---|---|
<%# Item.Quantity %> | <%# Item.Product.Name %> | <%# Item.Product.Price.ToString("c")%> | <%# ((Item.Quantity * Item.Product.Price).ToString("c"))%> |
Total: | <%= CartTotal.ToString("c") %> |
Quantity | Item | Price | Subtotal | |
---|---|---|---|---|
<%# Item.Quantity %> | <%# Item.Product.Name %> | <%# Item.Product.Price.ToString("c")%> | <%# ((Item.Quantity * Item.Product.Price).ToString("c"))%> | |
Total: | <%= CartTotal.ToString("c") %> |
">Continue shopping ">Checkout
...
Name | City | Items | Total | |
---|---|---|---|---|
<%#: Item.Name %> | <%#: Item.City %> | <%# Item.OrderLines.Sum(ol => ol.Quantity) %> | <%# Total(Item.OrderLines).ToString("C") %> | |
Name | Description | Category | Price |
---|---|---|---|
Come around 9pm. Fancy dress is optional
In Listing 11-4, you can see the contents of the /Content/sorryyoucantcome.html file. Listing 11-4. The /Content/sorryyoucantcome.html FileIt won't be the same without you. Maybe next year.
These files have the same name and the same content as the ones that we used in Chapter 1, but they are in the Content folder, which we will need to refer to when we need to use them.Name | Phone |
---|
Name | Phone |
---|
Name | Type |
---|---|
<%#: Item.Name %> | <%#: Item.TypeName %> |
Today's date is <%= DateTime.Now.ToShortDateString() %>
A new shirt costs <%= 20.ToString("C") %>
Our test server is in the United States, which means that we see the following output when we request the Price.aspx file: Today's date is 1/8/2013 A new shirt costs $20.00 Adam lives in the UK, which has its own currency and uses a different date format. Our goal is to detect the locale information provided by the browser so that the data Adam sees is formatted for his location. To do this, we have created a new class called LocaleModule.vb in the Events folder. In Listing 14-18, you can see how we have defined the module that solves the locale problem.") Dim sr As New StreamReader( context.Request.MapPath(context.Request.FilePath)) context.Response.Write( context.Server.HtmlEncode(sr.ReadToEnd())) context.Response.Write("") End Sub Public ReadOnly Property IsReusable() As Boolean _ Implements IHttpHandler.IsReusable Get Return False End Get End Property End Class The listing demonstrates the fourth reason that we gave you for writing a custom handler factory: to build on the functionality provided by one of the built-in factories. This isn’t something you would do very often—not least because there are simpler and easier ways to enhance the built-in functionality (custom base classes for code-behind files, modules, the global application class, and others). So, for this example, we have done something a little different—we have built on the functionality of the handler factory that deals with Web Form files so that we can choose to either see the regular output or see the contents of the ASPX file.
") Dim sr As New StreamReader( context.Request.MapPath(context.Request.FilePath)) context.Response.Write( context.Server.HtmlEncode(sr.ReadToEnd())) context.Response.Write("") ... We need to encode the contents of the Web Form file because it contains HTML elements and the browser will interpret them as such. By encoding the contents, we ensure that the markup in the .aspx file is displayed as text.
") context.Response.Write(context.Server.HtmlEncode(sr.ReadToEnd())) context.Response.Write("") End Sub Public ReadOnly Property IsReusable As Boolean _ Implements IHttpHandler.IsReusable Get Return False End Get End Property End Class This handler expects to receive requests for files in the form Default.aspx.src, which it processes to get the file that the user wants to see—Default.aspx, in this case. In Listing 17-12, you can see how we have registered the handler in the Web.config file. Listing 17-12. Registering the Handler in the Web.config File
") context.Response.Write(context.Server.HtmlEncode(sr.ReadToEnd())) context.Response.Write("") End Sub
") context.Response.Write(context.Server.HtmlEncode(sr.ReadToEnd())) context.Response.Write("") End Sub 'Public ReadOnly Property IsReusable As Boolean _ ' Implements IHttpHandler.IsReusable ' Get ' Return False ' End Get 'End Property End Class We have made the handler a subclass of Page. This is a nasty hack, but it gets around the limitation of the Execute method without affecting the functionality of the handler. We have had to comment out the implementation of the IsReusable property because the Page class has been written to prevent subclasses from overriding it—but we are able to override the ProcessRequest method, which is what counts when it comes to writing handlers.
Something has gone terribly wrong and we couldn't do what you asked.
Please try again.
As with the other examples in this book, we focus on technique rather than design (in no small part because our design skills are extremely basic). Our Failure.html page is extremely simple and just tells the user that something has gone wrong. We configure the ASP.NET Framework to use the HTML file through the Web.config file, as shown in Listing 21-6. Listing 21-6. Configuring a Custom Error Page in the Web.config FileSomething has gone terribly wrong and we couldn't do what you asked.
This Web Form generates the same basic response as the Failure.html file, but we have used the query string parameter to transform part of the text into a link that can be used to return to where the error occurred: ... ... To see the dynamic output, we have to change the Web.config file, as shown in Listing 21-8. Listing 21-8. Changing the Custom Error Page ...ASP.NET can't find the file you asked for.
IIS can't find the file you asked for.
Something has gone terribly wrong and we couldn't do what you asked.
(You asked for: )
This Web Form contains span elements that we will use to report on the source of the 404 error and the URL that was requested. In Listing 21-14, you can see how we set the contents of these span elements in the NotFoundShared.aspx.vb code-behind file. Listing 21-14. The Contents of the NotFoundShared.aspx.vb Code-behind File Public Class NotFoundShared Inherits System.Web.UI.Page Protected Sub Page_Load( ByVal sender As Object, ByVal e As System.EventArgs ) Handles Me.Load requestedURL.InnerText = If(Request("aspxerrorpath"), Request.RawUrl) errorSrc.InnerText = If(Request("aspxerrorpath") Is Nothing, "IIS", "ASP.NET") End Sub End Class This technique hinges on whether errors generated by ASP.NET have the aspxerrorpath query string parameter. If the parameter is present, then we assume we are dealing with an ASP.NET error and use its value to get the URL that the user asked for. If the parameter is not present, then we assume we are dealing with an error from IIS and get the requested URL from the HttpRequest.RawURL property, which we access via the Request property.Something went wrong with the Default.aspx Web Form.
This error page identifies the Default.aspx Web Form as the source of the error. In a real project, you can use this feature to give the user specific instructions or support information. To see the error page, start the application, enter apple into one of the form fields, and click the Submit button. Alternatively, check the Generate Error box and click Submit. The ErrorPage attribute is used for unhandled exceptions that occur in controls or the Web Form itself.Again, something has gone terribly wrong with <%: Request("errorSource") %>
and we couldn't do what you asked.
The error was a <%: Request("errorType") %>
We use code nuggets to display the value of request parameters called errorSource and errorType. You can see how we set these values when handling the Error event in the Default.aspx.vb code-behind file in Listing 21-19. Listing 21-19. Handling the Error Event in the Default.aspx.vb Code-behind File Public Class _Default Inherits System.Web.UI.Page Protected Sub Page_Load( ByVal sender As Object, ByVal e As System.EventArgs ) Handles Me.Load If IsPostBack AndAlso Request.Form("pageAction") = "error" Then Throw New ArgumentNullException() End If End SubSomething has gone wrong. We found the following problems:
This is the Default.aspx Web Form
This is a basic Web Form that displays a message indicating its name. This will make it easier to follow the examples in this chapter as we examine the relationship between URLs and the files that they target. We don’t need to make any changes to the code-behind file.Original virtual path: <%: Request.FilePath %>
Original physical path: <%: Request.PhysicalPath %>
Current virtual path: <%: Request.CurrentExecutionFilePath %>
Current physical path: <%: Request.MapPath(Request.CurrentExecutionFilePath) %>
Extensionless Handler
") Dim vpath As String = context.Request.Path If vpath = "/" Then context.Server.Transfer("/Default.aspx") ElseIf File.Exists(context.Request.MapPath(vpath & ".aspx")) Then context.Server.Transfer(vpath & ".aspx") Else context.Response.StatusCode = 404 context.ApplicationInstance.CompleteRequest() End If End Sub Public ReadOnly Property IsReusable() As Boolean _ Implements IHttpHandler.IsReusable Get Return False End Get End Property End Class This handler receives requests for URLs without file extensions and uses the HttpServerUtility.Transfer method to pass the request on to a Web Form. The way that we work out what Web Form to target is rudimentary. If the requested URL is /, then we target Default.aspx and for all other requests, we just append .aspx to the requested URL and see whether there is a file in the application with that name. If there is, we transfer the request to it and return a 404 response otherwise. We need to register our handler so that it can receive requests. In Listing 22-10, you can see the addition we made to the Web.config file.Content from file:
<%= GetFileContent() %> We will use this Web Form to display the contents of the /Content/Colors.html file that we obtain through the GetFileContent code-behind method. We know that the Colors.html file contains an HTML fragment, so we have used the non-encoding code-nugget. This allows the HTML fragment to be displayed without modification. Listing 22-20 shows the contents of the FileInfo.aspx.vb code-behind file. Listing 22-20. The Contents of the FileInfo.aspx.vb File Imports System.IO Public Class FileInfo Inherits System.Web.UI.Page Protected Function GetFileContent() As String Dim path As String = "/Content/Colors.html" Dim file__1 As String = Request.MapPath(path) Return File.ReadAllText(file__1) End Function End Class We start with the path /Content/Colors.html and use the MapPath method to get the full physical path, which we then use with the System.IO.File.ReadAddText method to get the file contents and return them to the code-nugget. This is a trivial example—we could get the same effect by using the HttpResponse.WriteFile method—but beingThis is Default.aspx
We need to be able to tell which Web Form has been used to generate a response, so the markup contains the name of the file. Our next step is to create a folder called Store, to which we have added a Web Form called Cart.aspx. The contents of this file are shown in Listing 23-2—once again, the markup contains the name of the file.This is /Store/Cart.aspx
We haven’t made any changes to the way that paths are handled by the application, so to access these Web Forms we have to request the /Default.aspx and /Store/Cart.aspx URLs.This is /Store/Cart.aspx
Route Path: <%: GetURLFromRoute() %>
You can see how we have implemented the code-behind method in Listing 23-7 that shows the /Store/Cart.aspx.vb file. Listing 23-7. Implementing a Codebehind Method in the /Store/Cart.aspx.vb File Imports System.Web.Routing Public Class Cart Inherits System.Web.UI.Page Protected Function GetURLFromRoute() As String Dim myRoute As Route = TryCast(RouteData.Route, Route) If myRoute IsNot Nothing Then Return myRoute.Url Else Return "Unknown RouteBase" End If End Function End Class You will notice that we use the TryCast keyword to convert the RouteBase object returned by the RouteData.Route property to a Route object. We have to be careful to avoid DirectCastDirectCast like this: Dim myRoute As Route = DirectCast(RouteData.Route, Route) We can’t tell in advance if we are working with custom subclasses of the RouteBase class—an explicit cast will cause an exception if we are. The TryCast keyword will assign Nothing to our variable if the RouteData.Route property doesn’t return a Route object, which allows us to gracefully handle custom implementation objects. The Route class defines the properties we have described in Table 23-5. Most of these properties perform a similar function to those in the RouteData class, although we can use them to reconfigure the route dynamically. (This is something that we rarely do. We prefer to use the RouteConfig class as the only place in the application where routes are configured.)This is the RouteLoop.aspx Web Form
Match | Route | Values |
---|---|---|
<%# Item.matches %> | <%# Item.path %> | <%# Item.values %> |
This is the PostTest.aspx Web Form
Make a Get Request This Web Form contains an a element whose href element targets the same /methodtest URL, but because we are using an a element the request will be made using the GET method. Now that we have the two Web Forms, we can create routes in the /App_Start/RouteConfig.vb file to associate the forms together with the same URL, as shown in Listing 24-5. Listing 24-5. Creating Routes with HTTP Method Constraints in the /App_Start/RouteConfig.vb File Imports System.Web.Routing Public Class RouteConfig Public Shared Sub RegisterRoutes(routes As RouteCollection) routes.MapPageRoute("default", "", "~/Default.aspx") routes.MapPageRoute( "postTest", "methodtest", "~/PostTest.aspx", False, Nothing, New RouteValueDictionary() From { {"httpMethod", New HttpMethodConstraint("POST")} }) routes.MapPageRoute("getTest", "methodtest", "~/GetTest.aspx", False, Nothing, Nothing) End Sub End Class The two routes we have defined are for the same /methodtest URL. What differentiates them is that the first route has a constraints collection that contains an HttpMethodConstraint object. This has the effect of only allowing the route to match requests that are for the /methodtest and which are POST requests. These requests will be directed to the PostTest.aspx Web Form. The route won’t match other kinds of requests so the next route, which has no such constraint, will be used to route the request to the GetTest.aspx Web Form.This is Default.aspx
Right click the Default.aspx Web Form in the Solution Explorer and select Set As Start Page from the pop-up menu. We created the second Web Form in the Admin folder and called it Restricted.aspx. You can see the contents of this file in Listing 25-4. Listing 25-4. The Contents of the /Admin/Restricted.aspx File <%@ Page Language="vb" AutoEventWireup="false" CodeBehind="Restricted.aspx.vb" Inherits="ManagingUsers.Restricted" %>This is /Admin/Restricted.aspx
We have not modified the code-behind class for either Web Form—they are just placeholders so we can demonstrate authorization.This is /Admin/Open.aspx
To see the effect of the module, start the application and request the /Admin/Open.aspx URL without authenticating. You will see a response generated from the Web Form and you will see the same result if you disable the module and perform the same test.This is Default.aspx
This is /Admin/Restricted.aspx
This is /Admin/Open.aspx
URL | Length | Blocked Duration | Total Duration |
---|---|---|---|
<%: GetResult().Url %> | <%: GetResult().Length %> | <%: GetResult().Blocked %> | <%: GetResult().Total%> |
Start Time | URL | Length |
---|---|---|
<%# Item.StartTime %> | <%# Item.Url %> | <%# Item.Length %> |