338 lines
14 KiB
XML
338 lines
14 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!--
|
|
/*
|
|
* Copyright 2002-2008 the original author or authors.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
-->
|
|
<chapter xml:id="springair" xmlns="http://docbook.org/ns/docbook" version="5">
|
|
<title>SpringAir - Reference Application</title>
|
|
|
|
<sect1>
|
|
<title>Introduction</title>
|
|
|
|
<para>The SpringAir sample application demonstrates a selection of
|
|
Spring.NET's powerful features making a .NET programmer's life easier. It
|
|
demonstrates the following features of Spring.Web</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>Spring.NET IoC container configuration</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Dependency Injection as applied to ASP.NET pages</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Master Page support</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Web Service support</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Bi-directional data binding</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Declarative validation of domain objects</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Internationalization</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Result mapping to better encapsulate page navigation
|
|
flows</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The application models a flight reservation system where you can
|
|
browse flights, book a trip and even attach your own clients by leveraging
|
|
the web services exposed by the SpringAir application.</para>
|
|
|
|
<para>All pages within the application are fully Spring managed.
|
|
Dependencies get injected as configured within a Spring Application
|
|
Context. For NET 1.1 it shows how to apply centrally managed layouts to
|
|
all pages in an application by using master pages - a well-known feature
|
|
from NET 2.0.</para>
|
|
|
|
<para>When selecting your flights, you are already experiencing a fully
|
|
localized form. Select your preferred language from the bottom of the form
|
|
and see, how the new language is immediately applied. As soon as you
|
|
submit your desired flight, the submitted values are automatically unbound
|
|
from the form onto the application's data model by leveraging Spring.Web's
|
|
support for Data Binding. With Data Binding you can easily associate
|
|
properties on your PONO model with elements on your ASP.NET form.</para>
|
|
</sect1>
|
|
|
|
<sect1>
|
|
<title>Getting Started</title>
|
|
|
|
<para>The application is located in the installation directory under
|
|
'examples/SpringAir. The directory 'SpringAir.Web.2003' contains the .NET
|
|
1.1 version of the application and the directory 'SpringAir.Web.2005'
|
|
contains the .NET 2.0 version. For .NET 1.1 you will need to create a
|
|
virtual directory named 'SpringAir.2003' using IIS Administrator and point
|
|
it to the following directory
|
|
examples\Spring\SpringAir\src\SpringAir.Web.2003. The solution file for
|
|
.NET 1.1 is examples\Spring\SpringAir\SpringAir.2003.sln. For .NET 2.0
|
|
simply open the solution examples\Spring\SpringAir\SpringAir.2005.sln. Set
|
|
your startup project to be SpringAir.Web and the startpage to
|
|
.\Web\Home.aspx</para>
|
|
</sect1>
|
|
|
|
<sect1>
|
|
<title>Container configuration</title>
|
|
|
|
<para>The web project's top level Web.config configures the IoC container
|
|
that is used within the web application. You do not need to explicitly
|
|
instantiate the IoC container. The important parts of that configuration
|
|
are shown below</para>
|
|
|
|
<programlisting language="myxml"><spring>
|
|
<parsers>
|
|
<parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
|
|
</parsers>
|
|
|
|
<context>
|
|
<resource uri="~/Config/Aspects.xml"/>
|
|
<resource uri="~/Config/Web.xml"/>
|
|
<resource uri="~/Config/Services.xml"/>
|
|
|
|
<!-- TEST CONFIGURATION -->
|
|
|
|
<resource uri="~/Config/Test/Services.xml"/>
|
|
<resource uri="~/Config/Test/Dao.xml"/>
|
|
|
|
<!-- PRODUCTION CONFIGURATION -->
|
|
|
|
<!--
|
|
<resource uri="~/Config/Production/Services.xml"/>
|
|
<resource uri="~/Config/Production/Dao.xml"/>
|
|
-->
|
|
</context>
|
|
</spring></programlisting>
|
|
|
|
<para>In this example there are separate configuration files for test and
|
|
production configuration. The Services.xml file is in fact the same
|
|
between the two, and the example will be refactored in future to remove
|
|
that duplication. The Dao layer in the test configuration is an in-memory
|
|
version, faking database access, whereas the production version uses an
|
|
ADO.NET based solution.</para>
|
|
|
|
<para>The pages that comprise the application are located in the directory
|
|
'Web/BookTrip'. In that directory is another Web.config that is
|
|
responsible for configuring that directory's .aspx pages. There are three
|
|
main pages in the flow of the application.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>TripForm - form to enter in airports, times, round-trip or
|
|
one-way</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Suggested Flights - form to select flights</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>ReservationConfirmationPage - your confirmation ID from the
|
|
booking process.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The XML configuration to configure the TripForm form is shown
|
|
below</para>
|
|
|
|
<programlisting language="myxml"> <object type="TripForm.aspx" parent="standardPage">
|
|
<property name="BookingAgent" ref="bookingAgent" />
|
|
<property name="AirportDao" ref="airportDao" />
|
|
<property name="TripValidator" ref="tripValidator" />
|
|
<property name="Results">
|
|
<dictionary>
|
|
<entry key="displaySuggestedFlights" value="redirect:SuggestedFlights.aspx" />
|
|
</dictionary>
|
|
</property>
|
|
</object></programlisting>
|
|
|
|
<para>As you can see the various services it needs are set using standard
|
|
DI techniques. The Results property externalizes the page flow,
|
|
redirecting to the next page in the flow, SuggestedFlights. The 'parent'
|
|
attribute lets this page inherit properties from a template. The is
|
|
located in the top level Web.config file, packaged under the Config
|
|
directory. The standardPage sets up properties of Spring's base page
|
|
class, from which all the pages in this application inherit from. (Note
|
|
that to perform only dependency injection on pages you do not need to
|
|
inherit from Spring's Page class).</para>
|
|
</sect1>
|
|
|
|
<sect1>
|
|
<title>Bi-directional data binding</title>
|
|
|
|
<para>The TripForm page demonstrates the bi-directional data binding
|
|
features. A Trip object is used to back the information of the form. The
|
|
family of methods that are overridden to support the bi-directional data
|
|
binding are listed below.</para>
|
|
|
|
<programlisting language="csharp"> protected override void InitializeModel()
|
|
{
|
|
trip = new Trip();
|
|
trip.Mode = TripMode.RoundTrip;
|
|
trip.StartingFrom.Date = DateTime.Today;
|
|
trip.ReturningFrom.Date = DateTime.Today.AddDays(1);
|
|
}
|
|
|
|
protected override void LoadModel(object savedModel)
|
|
{
|
|
trip = (Trip)savedModel;
|
|
}
|
|
|
|
protected override object SaveModel()
|
|
{
|
|
return trip;
|
|
}
|
|
|
|
protected override void InitializeDataBindings()
|
|
{
|
|
BindingManager.AddBinding("tripMode.Value", "Trip.Mode");
|
|
BindingManager.AddBinding("leavingFromAirportCode.SelectedValue", "Trip.StartingFrom.AirportCode");
|
|
BindingManager.AddBinding("goingToAirportCode.SelectedValue", "Trip.ReturningFrom.AirportCode");
|
|
BindingManager.AddBinding("leavingFromDate.SelectedDate", "Trip.StartingFrom.Date");
|
|
BindingManager.AddBinding("returningOnDate.SelectedDate", "Trip.ReturningFrom.Date");
|
|
}</programlisting>
|
|
|
|
<para>This is all you need to set up in order to have values from the Trip
|
|
object 'marshaled' to and from the web controls. The
|
|
InitializeDataBindings method set this up, using the Spring Expression
|
|
Language to define the UI element property that is associate with the
|
|
model (Trip) property.</para>
|
|
</sect1>
|
|
|
|
<sect1>
|
|
<title>Declarative Validation</title>
|
|
|
|
<para>The method called when the Search button is clicked will perform
|
|
validation. If validation succeeds as well as additional business logic
|
|
checks, the next page in the flow is loaded. This is shown in the code
|
|
below. Notice how much cleaner and more business focused the code reads
|
|
than if you were using standard ASP.NET APIs.</para>
|
|
|
|
<programlisting language="csharp"> protected void SearchForFlights(object sender, EventArgs e)
|
|
{
|
|
if (Validate(trip, tripValidator))
|
|
{
|
|
FlightSuggestions suggestions = this.bookingAgent.SuggestFlights(Trip);
|
|
if (suggestions.HasOutboundFlights)
|
|
{
|
|
Session[Constants.SuggestedFlightsKey] = suggestions;
|
|
SetResult(DisplaySuggestedFlights);
|
|
}
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>The 'Validate' method of the page takes as arguments the object to
|
|
validate and a IValidator instance. The TripForm property TripValidator is
|
|
set via dependency injection (as shown above). The validation logic is
|
|
defined declaratively in the XML configuration file and is shown
|
|
below.</para>
|
|
|
|
<programlisting language="myxml"> <v:group id="tripValidator">
|
|
|
|
<v:required id="departureAirportValidator" test="StartingFrom.AirportCode">
|
|
<v:message id="error.departureAirport.required" providers="departureAirportErrors, validationSummary"/>
|
|
</v:required>
|
|
|
|
<v:group id="destinationAirportValidator">
|
|
<v:required test="ReturningFrom.AirportCode">
|
|
<v:message id="error.destinationAirport.required" providers="destinationAirportErrors, validationSummary"/>
|
|
</v:required>
|
|
<v:condition test="ReturningFrom.AirportCode != StartingFrom.AirportCode" when="ReturningFrom.AirportCode != ''">
|
|
<v:message id="error.destinationAirport.sameAsDeparture" providers="destinationAirportErrors, validationSummary"/>
|
|
</v:condition>
|
|
</v:group>
|
|
|
|
<v:group id="departureDateValidator">
|
|
<v:required test="StartingFrom.Date">
|
|
<v:message id="error.departureDate.required" providers="departureDateErrors, validationSummary"/>
|
|
</v:required>
|
|
<v:condition test="StartingFrom.Date >= DateTime.Today" when="StartingFrom.Date != DateTime.MinValue">
|
|
<v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary"/>
|
|
</v:condition>
|
|
</v:group>
|
|
|
|
<v:group id="returnDateValidator" when="Mode == 'RoundTrip'">
|
|
<v:required test="ReturningFrom.Date">
|
|
<v:message id="error.returnDate.required" providers="returnDateErrors, validationSummary"/>
|
|
</v:required>
|
|
<v:condition test="ReturningFrom.Date >= StartingFrom.Date" when="ReturningFrom.Date != DateTime.MinValue">
|
|
<v:message id="error.returnDate.beforeDeparture" providers="returnDateErrors, validationSummary"/>
|
|
</v:condition>
|
|
</v:group>
|
|
|
|
</v:group></programlisting>
|
|
|
|
<para>The validation logic has 'when' clauses so that return dates can be
|
|
ignored if the Mode property of the Trip object is set to
|
|
'RoundTrip'.</para>
|
|
</sect1>
|
|
|
|
<sect1>
|
|
<title>Internationalization</title>
|
|
|
|
<para>Both image and text based internationalization are supported. You
|
|
can see this in action by clicking on the English, Srpski, or Српски links
|
|
on the bottom of the page.</para>
|
|
</sect1>
|
|
|
|
<sect1>
|
|
<title>Web Services</title>
|
|
|
|
<para>The class BookingAgent that was used by the TripForm class is a
|
|
standard .NET class, i.e no WebMethod attributes are on any of its
|
|
methods. Spring can expose this object as a web service by declaring the
|
|
following XML defined in the top level Config/Services.xml file</para>
|
|
|
|
<programlisting language="myxml"> <object id="bookingAgentWebService" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
|
|
<property name="TargetName" value="bookingAgent"/>
|
|
<property name="Name" value="BookingAgent"/>
|
|
<property name="Namespace" value="http://SpringAir/WebServices"/>
|
|
<property name="Description" value="SpringAir Booking Agent Web Service"/>
|
|
<property name="MemberAttributes">
|
|
<dictionary>
|
|
<entry key="SuggestFlights">
|
|
<object type="System.Web.Services.WebMethodAttribute, System.Web.Services">
|
|
<property name="Description" value="Gets those flight suggestions that are applicable for the supplied trip."/>
|
|
</object>
|
|
</entry>
|
|
<entry key="Book">
|
|
<object type="System.Web.Services.WebMethodAttribute, System.Web.Services">
|
|
<property name="Description" value="Goes ahead and actually books what up until this point has been a transient reservation."/>
|
|
</object>
|
|
</entry>
|
|
<entry key="GetAirportList">
|
|
<object type="System.Web.Services.WebMethodAttribute, System.Web.Services">
|
|
<property name="Description" value="Return a collection of all those airports that can be used for the purposes of booking."/>
|
|
</object>
|
|
</entry>
|
|
</dictionary>
|
|
</property>
|
|
</object></programlisting>
|
|
|
|
<para></para>
|
|
</sect1>
|
|
</chapter> |