Wednesday 19 March 2008

Silverlight 2 Yahoo Search

So I've put up my first version of my Silverlight 2.0 Yahoo Search application.

You can use the application for real searches here

So the UI is terrible (needs lots of work), i haven't implemented paging (so you only see the top 5 results). However it is a start and it allows me to play with lots of different concepts.

I will do a blog posting about it in more detail. However the key points is that it uses Yahoo's search api and searches occur between your machine and yahoo search (cross domain call, no proxying at my server).

If you have read scott guthries DIGG article, lots of the coding is based on it.

I will make the source available soon, and post more about some of the details.

Have fun!

Monday 17 March 2008

Silverlight, WCF and ASP.NET Authenticated Sites

Silverlight, WCF and ASP.NET Authenticated Sites

Introduction

In order to show integration of a service which we will expose to our own internal Silverlight applications (not third party developers), we will create a time service which will return the server time in UTC. The time service will be hosted within an ASP.NET 3.5 Website.

The service that we create will only be available to logged in users of our website and will leverage the existings forms authentication system. This will mean that our silverlight application will not have to implement it’s own login system but hook into the existing authentication system.

Creating the time service website
In order to create the time service i have performed the following steps:

1) Created a new ASP.NET Website within Visual Studio
2) Created a default.aspx page which has a link to the members area and to the service




<div>
Welcome to my home page, <a href="Members/Default.aspx">click here to go to my members section</a>
</div>



3) Created a login.aspx page (which uses the inbuilt asp.net login control)




<div>
<asp:Login ID="Login1" runat="server"></asp:Login>
</div>




4) Implemented authentication in Web.Config


<authentication mode="Forms">
<forms slidingExpiration="true" timeout="15" loginUrl="~/login.aspx" defaultUrl="~/Members/Default.aspx"></forms>
</authentication>


5) Implemented MyMembershipProvider
This is a simple custom membership provider class which accepts bob as a username and doesn’t care what the password is.

6) Implemented membership in Web.Config


<membership defaultProvider="MyMembershipProvider">
<providers>
<remove name="Default"/>
<add name="MyMembershipProvider" applicationName="MyMembershipProvider" passwordFormat="Encrypted" type="MyMembershipProvider"/>
</providers>
</membership>


7) Restrict access to members and internal services folders


<location path="Members">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</location>
<location path="InternalServices">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</location>>


8) Implemented Members default page (Members/Default.aspx)



<div>
<a href="SilverlightTimeAppTestPage.aspx">Click here to go to the time service</a>
</div>



We now have a standard host website which will allow users called Bob to login to the Members area, and restrict non authenticated users from this area of the site.

Implementing the Time Service

So we now wish to create a time service which will reside in the services folder (and will be only available to authenticated users).

1) Right click on the Services Folder -> Add New Item -> WCF Service (and name it TimeService.svc)
2) Define ItimeService.cs (should be in your app_code directory) as



[ServiceContract]
public interface ITimeService
{
[OperationContract]
DateTime GetServerTimeUtc();
}


3) Implement the TimeService.cs as



[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class TimeService : ITimeService
{
/// <summary>
/// Get the server time in utc
/// </summary>
public DateTime GetServerTimeUtc()
{
return DateTime.Now.ToUniversalTime();
}
}


Please note we are forcing the service to require to run under asp.net compatibilit mode. This allows us to utilise the asp.net forms authentication system
4) Modify the System.Service Model in Web.Config
The key thing to modify is to change from wsHttpBinding to basicHttpBinding, and to ensure aspnetcompatibility node is enabled



<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="TimeServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="TimeServiceBehavior" name="TimeService">
<endpoint address="" binding="basicHttpBinding" contract="ITimeService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
</system.serviceModel>



Implementing the Client


1) Ensure the location section of the web.config is commented out
2) Create a new Silverlight application
3) Add Service Reference, click Discover and point to the service
4) Call the proxy something sensible such as TimeServiceProxy
5) Right Click on the Members area of the Website, right click, add silverlight link, to include the test page on the website
6) Uncomment the location tags in the web.config
7) Call the time service off a button click, asynchronously updating a textbox

Call the time service off a button click, asynchronously updating a textbox



private void btnTime_Click(object sender, RoutedEventArgs e)
{
TimeServiceProxy.TimeServiceClient proxy = new SilverlightTimeApp.TimeServiceProxy.TimeServiceClient();
proxy.GetServerTimeUtcCompleted += new EventHandler<SilverlightTimeApp.TimeServiceProxy.GetServerTimeUtcCompletedEventArgs>(proxy_GetServerTimeUtcCompleted);
proxy.GetServerTimeUtcAsync();
}

void proxy_GetServerTimeUtcCompleted(object sender, SilverlightTimeApp.TimeServiceProxy.GetServerTimeUtcCompletedEventArgs e)
{
txtTime.Text = e.Result.ToString();
}


You now have a WCF service that you can use internally with your own silverlight applications, making use of asp.net authentication mechanism, and is protected so only logged in users can use the service from your silverlight applications.

As a security note do not expose this service to third party developers (see my previous post).

The full sample application can be downloaded here from my skydrive

Cross Domain Security Practices

There seems to be a little confusion regarding cross domain security, not only in the Silverlight world but in the flash world also. The confusion seems to be mainly regard about security risks.

There are 2 real scenarious regarding api's:

1) Exposing a service to your own applications
2) Exposing a service for consumption to third party developers (e.g. Mashups).

Exposing to your own applications
I will be doing a post on this later this week. However if you are writing a service to expose to your own silverlight applications, then you should not open up your application to third parties. Therefore in your clientaccesspolicy.xml or crossdomain.xml you should ensure that you do not allow third parties to access your site. If you wish to restrict services to logged in users of your site, you can use asp.net compatibility mode with a WCF service to provide this level of protection (I will blog about this later this week).

Exposing to third party applications
This is a very key point, if you wish to allow Silverlight or Flash third party developers access to your service (via clientaccesspolicy.xml or crossdomain.xml), you must seperate the service from your existing authentication system. Rather than using a session based authentication system, you must use a message based authentication system.

The best thing to do is use a different subdomain for your service / api from the mainwebsite, and only open up third party sites in subdomain. i.e. www.chrishay.com would not allow third party developers to access services, but api.chrishay.com would. www.chrishay.com can use forms based authentication to log in users, but api.chrishay.com should not leverage this. api.chrishay.com should use a message based system (e.g. providing a token,username/password as part of the message)

The reason we have to be very careful about this is the following:

If I exposed my services (lets say bank account service) to third party developers via my crossdomain.xml (or clientaccesspolicy.xml), and allowed my API to authenticate using asp.net authentication (or whatever session based system). There is a potential that if I then browse onto MrNastySite.com it would be able to make a crossdomain call to my site, leverage my already authenticated user and start extracting private information (as it would already be considerd as logged in). By implementing a message based system, with a seperate domain (with seperate policy files), with a seperate authentication system, there is no way for mrnastysite.com of hooking into my previous session and stealing my data.

So the golden rule is this when exposing services to third party developers.

1) Expose your API services on a different domain from your main website
2) Restrict your main website domain to your own applications only
3) Maintain a seperate clientaccesspolicy.xml (or crossdomain.xml) for your API domain
4) Use a message based authentication system for your third party api (if you need to implement authentication), do not use a session based system (cookies etc)

Anyway I hope this helps clear up some confusion. It is not only small companies that have experienced this confusion. Only up until about a week ago, did Twitter have their main website open to third party developers (they have now restricted this policy).

Friday 14 March 2008

Stretches that don't work for ListBoxItems

I have a listbox where I want my items to stretch across the full listbox.

Unfortunately this doesn't seem to work in Silverlight 2.0 Beta 1, (works perfect in WPF).

Code below:


<ListBox x:Name="myList"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Content="Hello World"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>


After sometime searching I found the bug is in the control (within the generic.xaml file). The HorizontalAlignment is set to left for the ListBoxItem rather than using the template binding, anyways you can override this style using ListBoxItemContainer Style.

I have provided the code below which fixes the problem. I believe Microsoft will fix this bug in time, so you can remove it once fixed.



<ListBox x:Name="myList"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsEnabled" Value="true" />
<Setter Property="Foreground" Value="#FF000000" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="Cursor" Value="Arrow" />
<Setter Property="TextAlignment" Value="Left" />
<Setter Property="TextWrapping" Value="NoWrap" />
<!-- Cannot currently parse FontFamily type in XAML so it's being set in code -->
<!-- <Setter Property="FontFamily" Value="Trebuchet MS" /> -->
<Setter Property="FontSize" Value="12" />
<Setter Property="Background" Value="White" />
<Setter Property="Padding" Value="2,0,0,0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid x:Name="RootElement" Background="{TemplateBinding Background}">
<Grid.Resources>
<Storyboard x:Key="Normal State" />
<!-- <Storyboard x:Key="Unfocused Selected State"/> -->
<Storyboard x:Key="Normal Selected State">
<ColorAnimation Storyboard.TargetName="fillStop0" Storyboard.TargetProperty="Color" Duration="0" To="#FFD9EFFF"/>
<ColorAnimation Storyboard.TargetName="fillStop1" Storyboard.TargetProperty="Color" Duration="0" To="#FFBDD2E6"/>
<ColorAnimation Storyboard.TargetName="fillStop2" Storyboard.TargetProperty="Color" Duration="0" To="#FFA1B6CD"/>

<ColorAnimation Storyboard.TargetName="strokeStop0" Storyboard.TargetProperty="Color" Duration="0" To="#FF77B9EB"/>
<ColorAnimation Storyboard.TargetName="strokeStop1" Storyboard.TargetProperty="Color" Duration="0" To="#FF4887CD"/>
</Storyboard>
<Storyboard x:Key="MouseOver State">
<ColorAnimation Storyboard.TargetName="fillStop0" Storyboard.TargetProperty="Color" Duration="0" To="#FFF9FAFA"/>
<ColorAnimation Storyboard.TargetName="fillStop1" Storyboard.TargetProperty="Color" Duration="0" To="#FFE6EFF7"/>
<ColorAnimation Storyboard.TargetName="fillStop2" Storyboard.TargetProperty="Color" Duration="0" To="#FFD3E4F5"/>

<ColorAnimation Storyboard.TargetName="strokeStop0" Storyboard.TargetProperty="Color" Duration="0" To="#00000000"/>
<ColorAnimation Storyboard.TargetName="strokeStop1" Storyboard.TargetProperty="Color" Duration="0" To="#00000000"/>
</Storyboard>
<!-- <Storyboard x:Key="MouseOver Unfocused Selected State"/> -->
<Storyboard x:Key="MouseOver Selected State">
<ColorAnimation Storyboard.TargetName="fillStop0" Storyboard.TargetProperty="Color" Duration="0" To="#FFF9FAFA"/>
<ColorAnimation Storyboard.TargetName="fillStop1" Storyboard.TargetProperty="Color" Duration="0" To="#FFE6EFF7"/>
<ColorAnimation Storyboard.TargetName="fillStop2" Storyboard.TargetProperty="Color" Duration="0" To="#FFD3E4F5"/>

<ColorAnimation Storyboard.TargetName="strokeStop0" Storyboard.TargetProperty="Color" Duration="0" To="#FF77B9EB"/>
<ColorAnimation Storyboard.TargetName="strokeStop1" Storyboard.TargetProperty="Color" Duration="0" To="#FF4887CD"/>
</Storyboard>
</Grid.Resources>

<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>

<Rectangle IsHitTestVisible="False">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0.316111,0.0165521" EndPoint="0.316111,0.724833">
<GradientStop Name="fillStop0" Color="#00000000" Offset="0"/>
<GradientStop Name="fillStop1" Color="#00000000" Offset="0.682203"/>
<GradientStop Name="fillStop2" Color="#00000000" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
<Rectangle.Stroke>
<LinearGradientBrush StartPoint="0.318122,0.0360108" EndPoint="0.318122,0.715784">
<GradientStop Name="strokeStop0" Color="#00000000" Offset="0"/>
<GradientStop Name="strokeStop1" Color="#00000000" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Stroke>
</Rectangle>

<Rectangle x:Name="FocusVisualElement" Stroke="Black" StrokeDashArray="1,2" Visibility="Collapsed" IsHitTestVisible="False"/>
<ContentPresenter
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontStretch="{TemplateBinding FontStretch}"
FontStyle="{TemplateBinding FontStyle}"
FontWeight="{TemplateBinding FontWeight}"
Foreground="{TemplateBinding Foreground}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
Padding="{TemplateBinding Padding}"
TextAlignment="{TemplateBinding TextAlignment}"
TextDecorations="{TemplateBinding TextDecorations}"
TextWrapping="{TemplateBinding TextWrapping}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
<Line Stretch="Fill" Grid.Row="1" X1="0" X2="1" Y1="0" Y2="0" StrokeThickness="1" Stroke="#FFA4A4A4" IsHitTestVisible="False"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Content="Hello World"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Tuesday 11 March 2008

Deep Zoom Silverlight 2.0 Beta 1 Poster

This is very cool, it's a Deep Zoom version of the Silverlight 2.0 Beta 1 Poster.

This is via Joe Stegman's blog

UK 2008 Launch Event - Swaggily Fortunes

Wednesday March 19th 2008 is of course the UK 2008 Launch Event.

As part of the event, those NxtGenUG Boyz http://www.nxtgenug.net are running one of their gameshows, which will take place about 6.00pm during the evening drinks and nibbles. "Swaggily" Fortunes is a take on the well known TV Quiz show in which teams of contestants are asked, for example, "We asked 100 Developers ... To Name a .NET Language". Those teams that guess correctly what the answers were get to win some fantastic "swag" -- after a few rounds of mayhem! You can help be part of this 'great' occasion by going to http://www.nxtgenug.net/2008launch/swagfortunes.aspx and answering 8 short questions.

Your answers will go towards making up the choices which the teams have to guess. The NxtGenUG lads have run this several times in the past always to great reviews, and it makes for a fun, relaxed way to wind down from the days sessions."

Monday 3 March 2008

Silverlight 2 - App.Xaml Events

In Silverlight 2, we have the new App.Xaml file which we can use to store application resources (styles etc), that can be used throughout our Silverlight application.

For an example of using styles within the app.xaml file in Silverlight2, see scott guthries blog posting

The app.xaml.cs file also acts a little like a Global.asx file (for asp.net developers). An example of the app.xaml.cs file is below:



public partial class App : Application
{

public App()
{
this.Startup += this.Application_Startup;
this.Exit += this.Application_Exit;
this.UnhandledException += this.Application_UnhandledException;

InitializeComponent();
}

private void Application_Startup(object sender, StartupEventArgs e)
{
// Load the main control
this.RootVisual = new Page();
}

private void Application_Exit(object sender, EventArgs e)
{

}
private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{

}
}


As you can see there are three very useful methods here:

Application_Startup
This allows you to perform any initialization that you might need to perform in your application on startup.

In order to keep the user experience sweet, I would not recommend any synchronous network calls in this method as it will delay the loading of the application.

Please notice that you must set the RootVisual property to a new instance of your Silverlight Page (plumbeed in for you automatically however).

Application_Exit
Since this will be called when your application ends, it might be great to log this back to the server via a web service call. This really would give a great indication on the usage of your application.

I am a little concerned about this call, as it might give those nasty advertising folks a chance to spawn off more instances of the same page (might need to have check on this one, when the beta comes out).

Application_UnhandledException

Since the Application_UnhandledException method will get called if an unhandled exception occurs, this allows you to build a logging mechanism that could report errors directly back to your server.

I would suggest that exposing a standard web service on your server that you could call from this method. Therefore you will be able to log and review any errors in your application (i might knock up such a service soon).

Sunday 2 March 2008

Back from SQLBits 2

I was up at SQLBits in Birmingham yesterday.

I had a fantastic time and seen some great sessions, namely Andras, Mike Taulty and Andrew Fryer. Unfortunately I couldn't stay until the end (which is a shame as I wanted to see Tony Rogersons session).

It was nice to catchup with loads of folks and I had a lot of very kind people who have seen me present chat to me and say very nice things. I just want to say a quick thanks to those people i spoke to, its great to get such feedback as it inspires you to do more sessions.

As it was yesterday the sqlbits folks were short of Grok Talks, so I put together a quick 10 minute session on using Linq To Sql, Paging in Linq to SQL and the application name in connection strings. I really did throw it together with no practice, so I hope those people who saw my grok talk enjoyed it.

Another big mention must go to Colin Angus Mackay who did two grok talks that day. Well done Colin!

Anyways thanks to all at SQLBits for putting together such a great day (and i wish i could have stayed to the end).