Getting ready to say goodbye to Silverlight and hello to Blazor

While trying to think about how to make meaningful progress and hedge my bets at the same time I thought “Can I migrate my WCF services to Web API and make my Silverlight client to REST calls to the new services?”.

So I started to do more reading and prototyping code to test out my ideas.

I found a lot of things that didn’t work but enough that did to get my test application running.

Here’s what I did:Set up a Web API test server serving some services over HTTPSSet up the server for JWT Bearer token AuthenticationSet up a test controller with the Authorize attributeSet up that controller with an [HttpPost([“action”]) methodHave that method accept and return a classSet up an ASP.

Net Web Pages hosted Silverlight ApplicationHave the ASP.

Net Web site provide a JWT bearer token to the Silverlight applicationSet up a REST library in the Silverlight clientHave the Silverlight client make a REST POST with the JWT token and a C# object in the bodyHave the Silverlight client receive a C# object back from the REST callThis is not everything that’s needed to build an application, this is just most of the parts that I need for modifying my system that is already in place.

This is also aimed at my specific application needs but there should be enough information for anyone already using Silverlight and WCF to implement this.

Here is step by step how I did everything mentioned above.

I created a Web API project using .

Net Core 2.

1 in Visual Studio 2019.

The Web API are the Silverlight client are going to have different origins so I added CORS code to configuration in Startup.

cs (allowing everything from everywhere for testing):services.

AddCors(options =>{options.

AddPolicy("EnableCORS", builder =>{builder.

AllowAnyOrigin().

AllowAnyHeader().

AllowAnyMethod().

AllowCredentials().

Build();});});Accompanied by adding it to the configure method:app.

UseCors("EnableCORS");Silverlight also refuses to access any services from a different origin so to make Silverlight happy I needed to make the server give it a clientaccesspolicy.

xml file.

This file needs to be right under the wwwroot folder and will look like so when there are no restrictions in place:<?xml version="1.

0" encoding="utf-8"?><access-policy><cross-domain-access><policy><allow-from http-request-headers="*"><domain uri="*"/><domain uri="http://*"/><domain uri="https://*"/></allow-from><grant-to><resource path="/" include-subpaths="true"/></grant-to></policy></cross-domain-access></access-policy>An interesting thing to note was that this actually didn’t work as originally written with just <domain uri=”*”/> in the file.

I had to add the two additional domain entries to get it to work.

By default .

Net Core is not going to serve this file up.

In order to have it become accessible to the client I had to add the following line of code to Configure in Startup.

cs:app.

UseStaticFiles();With that set up I then moved on to configuring Authentication on the server.

I added the following code to the ConfigureServices method in Startup.

cs and then did a few Quick Actions to resolve the dependencies://security keystring securityKey = "Never put your secret key in your code because that would be bad";//symmetric security keyvar symmetricSecurityKey = new SymmetricSecurityKey(Encoding.

UTF8.

GetBytes(securityKey));services.

AddAuthentication(JwtBearerDefaults.

AuthenticationScheme).

AddJwtBearer(options =>{options.

TokenValidationParameters = new Microsoft.

IdentityModel.

Tokens.

TokenValidationParameters{ValidateIssuer = true,ValidateAudience = true,ValidateIssuerSigningKey = true,ValidIssuer = "lou.

com",ValidAudience = "readers",IssuerSigningKey = symmetricSecurityKey};});To make that work I then added a line in Configure as well:app.

UseAuthentication();Because I was building a test environment to see if I could actually get everything to work I worked on the default ValuesController that gets created by the Web API template.

The first thing I did was add an [Authorize] attribute directly below the [ApiController] attribute on the ValuesController class.

Next I created a test class with another test class that I would pass back and forth to see if my REST calls and serialization and deserialization would actually work.

I called my class Foo() and gave it two public string properties and nested another class Bar() with a public string property inside of it.

I then set up this method in the ValuesController for my Silverlight client to attempt to call:// POST api/values/RetrieveFooFromServer[HttpPost("[action]")]public ActionResult<Foo> RetrieveFooFromServer([FromBody] Foo value){try{Foo result = new Foo();result.

Bar = value.

Bar + " received";result.

Ear.

Car = "We are nested";return Ok(result);}catch{return BadRequest();}}A direct port of my existing business logic will result in all of my WCF service calls needing to be [HttpPost(“[action]”)] to named methods and receiving and returning C# classes so this is what I tested.

At this point all of the configuration is done on the server side although it’s not testable as is because I don’t have code to generate my JWT yet.

The next step was to have my ASP.

Net web page pass a JWT bearer token to the Silverlight client after the user signs in.

I opened up NuGet package manager and added the following package to the Web project:System.

IdentityModel.

Tokens.

JwtThis then pulled down a few more packages as dependencies.

I then put the following code to generate my JWT into a service method in one of my WCF services that returns a string so that the Silverlight client could retrieve the token after the user logged in://return queryString + " Success";//security keystring securityKey = "Never put your secret key in your code because that would be bad";//symmetric security keyvar symmetricSecurityKey = new SymmetricSecurityKey(Encoding.

UTF8.

GetBytes(securityKey));//credentials for signing tokenvar signingCredentials = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.

HmacSha256Signature);//add claimsvar claims = new List<Claim>();claims.

Add(new Claim(ClaimTypes.

Role, "User"));//create tokenvar token = new JwtSecurityToken(issuer: "lou.

com",audience: "readers",expires: DateTime.

Now.

AddHours(1),signingCredentials: signingCredentials,claims: claims);//return tokenreturn new JwtSecurityTokenHandler().

WriteToken(token);At this point I can log into my Silverlight client and pull down the JWT bearer token from the server to my client.

The last step is to actually make the REST call to Web API from Silverlight.

I think I downloaded seven different REST packages from NuGet and I think I managed to fail to get at least five of them to communicate with the server.

Most of the packages are either no longer being developed or have dropped support for Silverlight at this point.

The one that worked for me and gave me clean, readable code was RestSharp.

Starting with version 106.

0.

0 Silverlight support was dropped but I was able to pull 105.

2.

3 down from NuGet and it works.

RestSharp 105.

2.

3Following code examples I read online I wound up building two methods, one to build the request and one with generics to execute the result and retrieve the return value.

The code looks like this:static async Task<string> TestRestSharpWebAPICall(string token){Foo foo = new SilverlightAsyncTest.

Foo();var request = new RestRequest("/api/values/RetrieveFooFromServer", Method.

POST);request.

RequestFormat = DataFormat.

Json;request.

AddParameter("Authorization", "Bearer " + token, ParameterType.

HttpHeader);request.

AddJsonBody(foo);Foo result = await ExecuteAsync<Foo>(request);return result.

Bar;}static Task<T> ExecuteAsync<T>(RestRequest request) where T : new(){var client = new RestClient("https://localhost:44311");var taskCompletionSource = new TaskCompletionSource<T>();client.

ExecuteAsync<T>(request, (response) => taskCompletionSource.

SetResult(response.

Data));return taskCompletionSource.

Task;}In the var request statement in the test method I put in the /api/values/RetrieveFooFromServer path for the ValuesController that was configured in the Web API project.

In the var client statement in ExecuteAsync<t>() I put localhost along with the port that the WebAPI client was being run on when I started debugging it in Visual Studio.

When I got to the point where all of this was complete and in place I was able to launch the Web API from Visual Studio 2019 then launch the Silverlight client from Visual Studio 15 and have it call the Web API successfully and submit the object and retrieve an object back.

None of this worked on the first try and I did a lot of reading and a lot of trial and error but I now have a working configuration which is what I have presented in this article.

The next step for me is to put it in place in my development environment with real business cases and proper settings and security.

Once that passes some tests I can start converting my systems over to Web API and prepping for the eventual release of Blazor 1.

0 (or going with my fallback plan of Angular).

.

. More details

Leave a Reply