Tag: .NET

JavaScript frameworks explained to an ASP.NET dev

For most of my career I've been an ASP.NET dev and a JavaScript dev. If I was going to say I was more of an expert in one of them it would be the .NET side of things but I've never really lost touch with JavaScript.

Right now I think it's fair to say technologies in the world are starting to shift how we build websites, with JavaScript frameworks reaching a point with features like static site generation where they actually now offer a decent performance incentive to use them. At some point Blazor may get to a point where it reverses this, but right now there's a compelling argument to move.

For a ASP.NET dev this can be a daunting task. You might be thinking of trying out a headless CMS with a JavaScript front end, but just take a look at this screen grab from Prismic's sdk list.

There's 7 different JavaScript based SDK's listed there! Over half of the total and none of them are that Angular thing you had heard about. Where do you start?

Lets compare to .NET

Well recently I've been updating my JS skills again trying out some of the frameworks I hadn't used before, so I thought I'd share some learnings. The good news is as always it's not really as different as it first seems. To take some of the pain out of understanding what all these frameworks are I thought it would be good to try and relate them back to .NET and what the almost equivalent is.

Assembly Code

No not actual assembler but what does our code actually compile to. In the .NET world we have CIL (Common Intermediate Language), previously known as MSIL (Microsoft Intermediate Language) that our C#, F#, VB etc all compile down to before then being converted to the correct machine code for where they run.

In the front end world think of JavaScript being a bit like this (apart from the fact you actually write JavaScript and we don't write CIL).

View Engine

To render views into HTML, in the ASP.NET world we have Razor, but not just Razor. We also have WebForm, Brail, Bellevue, NDjango (see more here), it just happens that we mostly just use Razor.

I see the equivalents of these being ReactJS, VueJS and Angular. Its not an exact match as they also aren't exact equivalents or each other, but they're largely your functionality that will take a model and turn it into HTML.

Web Application Framework

The problem with the name framework is it applies to basically anything, but this is what I'm going with for describing ASP.NET MVC/ASP.NET Razor Pages/Web Forms, you know all those things built on-top of .NET that make it a website rather than a desktop app. They do things like routing, organising our files into controller and view folders, know how to respond to http requests etc.

Here we have Next.js, Nuxt.js and maybe Gatsby. The link between these and View Engine is a bit stronger than the ASP.NET MVC world as you essentially have a one to one mapping Next.js -> React, Nuxt.js -> Vue but they are what adds routing, static site generation and organization to your code.

Lower Level Framework

Now this one could be wrong :)

In .NET we have different version of the framework. e.g. .NET Framework /3.5/4, .NET Core, .NET 5, Mono. On the front end side they have Node.

Languages

In .NET we have choices including C#, F#, VB among other.

JavaScript has JavaScript (which I know I said was assembly), TypeScript, Coffee Script maybe more.

Not so daunting

There's probably a bunch of flaws with my comparison list and reasons people can point out why things I've said are the same are in fact different, but my point was really to show that while .NET may appear as one button on a SDK list alongside 7 JavaScript based SDK's its not that different. Out of the 7 Node is based on JavaScript. Vue and React are based on Node, and Next/Gatsby/Nuxt are based on Vue/React. There just isn't the same concept of all of it being built by one company or one particular combination being dominant in the same way that ASP.NET MVC + C# + Razor has been for the last generation of .NET websites.

Why is my Session ID changing on 3D Secure payments?

If you have a website where you are implementing 3D Secure payments, you may find that you have an issue where on receipt of the payment setup confirmation the users Session ID has changed with no apparent cause.

Lets have a quick run through of the payment process in this scenario (this is roughly how SagePay and WorldPay both work):

  1. User completes payment details on your site (some wizardry normally happens at this point with an iFrame for the card number to maintain PCI compliance) and the form is submitted to your server
  2. You server call's an API from the payment gateway to setup a 3D Secure payment. It passes the users Session ID along with all the payment details.
  3. Payment gateway responds with a URL for you to redirect the user too by posting a form to it. You do this, likely in an iFrame so that the page still looks like your website (otherwise it's a very ugly page).
  4. User may or may not get prompted by some sort of authentication by the bank. This could be something like receiving a text message with a code to enter.
  5. When authenticated the user is sent back to your website (in the iFrame) with a post request.
  6. Your server picks out the form details from the request and then calls an API to complete the transaction. In this API call you send the Session ID so that the payment gateway can validate it is the same as the one at the start of the process.
  7. Confirmation shown to the user.

Introducing the Same Site Cookie Policy

In 2020 a change made to how cookies function in browsers to defend against cross site scripting. Troy Hunt has a brilliant explanation of the issue with how cookies used to work and how this has changed here. I'm going to try a much shorter explanation;

When a request is made from a browser, as part of the request all the cookie values for that domain are sent with the request. This will include one for the Session ID. The theory here is that because the cookies are only being sent to the domain which set them in the first place, then information is only being shared back with the place that set it to begin with, which is therefor safe.

However the workings of the internet and what domain a button click might call isn't overly obvious to most people. So what if clicking a link on one site causes the user to be redirected to another site? Answer: all the cookies are still sent. The same thing happens if a form is posted from one site to another site. The problem here is that if you are authenticated on the other website, then its possible for a cross site scripting attack to be using your session via your browser without you even realising you were on the site again. This is why it's always a good idea to log out of websites!

The introduction of the same site policy changes how cookies work with three options:

  1. None: which is what the browsers used to do. i.e. send all the cookies with cross-origin request
  2. Lax: some limits on sending cookies with cross-origin request
  3. Strict: tight limits on sending cookies with cross-origin request

None is sending everything and a strict policy will basically stop cookies from being sent when they have a cross-origin request.

A Lax policy is slightly more interesting though, because it depends if the request is a GET or a POST. If it is a GET then the cookies will still be sent, which means that if you follow a link from another site or a search engine your cookies will still be sent to the site. However a POST (like what the 3D Secure page is doing) will no longer send the cookies.

If the policy isn't set, then Lax is used as the default.

So why is my Session ID changing?

The problem is that post request back to your site, unless the Session ID had a cookie policy of None then the Session ID cookie won't be sent. The server will then see that there is no Session ID and treat the users as if they are new on the site and as a result start a new session. From this point on the user has lost the old session and you can't complete the payment. Worse still they've probably just been logged out and anything else using session data has also been lost.

In .Net 4.7.2 and up, Microsoft has implemented the ability to set the cookie policy of the session ID. You can do this in your web.config file like this:

<configuration>
<system.web>
<anonymousIdentification cookieRequireSSL="false" /> <!-- No config attribute for SameSite -->
<authentication>
 <forms cookieSameSite="None" requireSSL="false" />
</authentication>
<sessionState cookieSameSite="None" /> <!-- No config attribute for Secure -->
<roleManager cookieRequireSSL="false" /> <!-- No config attribute for SameSite -->
<system.web>
<configuration>

You can find more about this here. In everything older the policy wont be set and will default to Lax.

However just because you can set the cookie policy to None, it doesn't mean you should. After all, that just re-opens the vulnerability the browser was trying to protect against.

The solution I went with was to have a page that the users is redirected back to from the payment gateway that does nothing other than re-submit the values from the payment gateway to the site again. This way I can be sure of what form data is being posted to the site, and when I re-post it, it is a same site post and not a cross-origin which means the Session ID cookie will be sent.

To stop my initial page setting a new session cookie (which it will try to do because it won't recieve one), I use the IIS URL Rewrite module to strip out the set cookie response headers from the page.

You can do this with an outbound rule as follows:

<rewrite>
    <rules>
    </rules>
    <outboundRules>
      <rule name="Remove SetCookie Header" preCondition="Match Payment Page">
        <match serverVariable="RESPONSE_Set-Cookie" pattern=".*" />
        <action type="Rewrite" value="" />
      </rule>
      <preConditions>
        <preCondition name="Match Payment Page" logicalGrouping="MatchAny">
          <add input="{REQUEST_URI}" pattern="PAGE NAME GOES HERE" />
        </preCondition>
      </preConditions>
    </outboundRules>
  </rewrite>

With this solution the cookies are still secure as the policy is set to Lax and I can take payments using 3D Secure which will soon become a requirement.

Two ways to import an XML file with .Net Core or .Net Framework

It's always the simple stuff you forget how you do. For years I've mainly been working with JSON files, so when faced with that task of reading an XML file my brain went "I can do that" followed by "actually how did I used to do that?".

So here's two different methods. They work on .Net Core and theoretically .Net Framework (my project is .Net Core and haven't checked that they do actually work on framework).

My examples are using an XML in the following format:

<?xml version="1.0" encoding="utf-8"?>
<jobs>
  <job>
      <company>Construction Co</company>
      <sector>Construction</sector>
      <salary>£50,000 - £60,000</salary>
      <active>true</active>
      <title>Recruitment Consultant - Construction Management</title>
  </job>
  <job>
      <company>Medical Co</company>
      <sector>Healthcare</sector>
      <salary>£60,000 - £70,000</salary>
      <active>false</active>
      <title>Junior Doctor</title>
  </job>
</jobs>

Method 1: Reading an XML file as a dynamic object

The first method is to load the XML file into a dynamic object. This is cheating slightly by first using Json Convert to convert the XML document into a JSON string and then deserializing that into a dynamic object.

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace XMLExportExample
{
  class Program
  {
      static void Main(string[] args)
      {
          string jobsxml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><jobs>    <job><company>Construction Co</company><sector>Construction</sector><salary>£50,000 - £60,000</salary><active>true</active><title>Recruitment Consultant - Construction Management</title></job><job><company>Medical Co</company><sector>Healthcare</sector><salary>£60,000 - £70,000</salary><active>false</active><title>Junior Doctor</title></job></jobs>";

          byte[] byteArray = Encoding.UTF8.GetBytes(jobsxml);
          MemoryStream stream = new MemoryStream(byteArray);
          XDocument xdoc = XDocument.Load(stream);

          string jsonText = JsonConvert.SerializeXNode(xdoc);
          dynamic dyn = JsonConvert.DeserializeObject<ExpandoObject>(jsonText);

          foreach (dynamic job in dyn.jobs.job)
          {
              string company;
              if (IsPropertyExist(job, "company"))
                  company = job.company;

              string sector;
              if (IsPropertyExist(job, "sector"))
                  company = job.sector;

              string salary;
              if (IsPropertyExist(job, "salary"))
                  company = job.salary;

              string active;
              if (IsPropertyExist(job, "active"))
                  company = job.active;

              string title;
              if (IsPropertyExist(job, "title"))
                  company = job.title;

              // A property that doesn't exist
              string foop;
              if (IsPropertyExist(job, "foop"))
                  foop = job.foop;
          }

          Console.ReadLine();
      }

      public static bool IsPropertyExist(dynamic settings, string name)
      {
          if (settings is ExpandoObject)
              return ((IDictionary<string, object>)settings).ContainsKey(name);

          return settings.GetType().GetProperty(name) != null;
      }
  }
}

A foreach loop then goes through each of the jobs, and a helper function IsPropertyExist checks for the existence of a value before trying to read it.

Method 2: Deserializing with XmlSerializer

My second approach is to turn the XML file into classes and then deserialize the XML file into it.

This approch requires more code, but most of it can be auto generated by visual studio for us, and we end up with strongly typed objects.

Creating the XML classes from XML

To create the classes for the XML structure:

1. Create a new class file and remove the class that gets created. i.e. Your just left with this

using System;
using System.Collections.Generic;
using System.Text;

namespace XMLExportExample
{

}

2. Copy the content of the XML file to your clipboard
3. Select the position in the file you want to the classes to go and then go to Edit > Paste Special > Paste XML as Classes

If your using my XML you will now have a class file that looks like this:

using System;
using System.Collections.Generic;
using System.Text;

namespace XMLExportExample
{

  // NOTE: Generated code may require at least .NET Framework 4.5 or .NET Core/Standard 2.0.
  /// <remarks/>
  [System.SerializableAttribute()]
  [System.ComponentModel.DesignerCategoryAttribute("code")]
  [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
  [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
  public partial class jobs
  {

      private jobsJob[] jobField;

      /// <remarks/>
      [System.Xml.Serialization.XmlElementAttribute("job")]
      public jobsJob[] job
      {
          get
          {
              return this.jobField;
          }
          set
          {
              this.jobField = value;
          }
      }
  }

  /// <remarks/>
  [System.SerializableAttribute()]
  [System.ComponentModel.DesignerCategoryAttribute("code")]
  [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
  public partial class jobsJob
  {

      private string companyField;

      private string sectorField;

      private string salaryField;

      private bool activeField;

      private string titleField;

      /// <remarks/>
      public string company
      {
          get
          {
              return this.companyField;
          }
          set
          {
              this.companyField = value;
          }
      }

      /// <remarks/>
      public string sector
      {
          get
          {
              return this.sectorField;
          }
          set
          {
              this.sectorField = value;
          }
      }

      /// <remarks/>
      public string salary
      {
          get
          {
              return this.salaryField;
          }
          set
          {
              this.salaryField = value;
          }
      }

      /// <remarks/>
      public bool active
      {
          get
          {
              return this.activeField;
          }
          set
          {
              this.activeField = value;
          }
      }

      /// <remarks/>
      public string title
      {
          get
          {
              return this.titleField;
          }
          set
          {
              this.titleField = value;
          }
      }
  }

}

Notice that the active field was even picked up as being a bool.

Doing the Deserialization

To do the deserialization, first create an instance of XmlSerializer for the type of the object we want to deserialize too. In my case this is jobs.

var s = new System.Xml.Serialization.XmlSerializer(typeof(jobs));

Then call Deserialize passing in a XML Reader. I'm creating and XML reader on the stream I used in the dynamic example.

jobs o = (jobs)s.Deserialize(XmlReader.Create(stream));

The complete file now looks like this:

using System;
using System.IO;
using System.Text;
using System.Xml;

namespace XMLExportExample
{
  class Program
  {
      static void Main(string[] args)
      {
          string jobsxml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><jobs>    <job><company>Construction Co</company><sector>Construction</sector><salary>£50,000 - £60,000</salary><active>true</active><title>Recruitment Consultant - Construction Management</title></job><job><company>Medical Co</company><sector>Healthcare</sector><salary>£60,000 - £70,000</salary><active>false</active><title>Junior Doctor</title></job></jobs>";

          byte[] byteArray = Encoding.UTF8.GetBytes(jobsxml);
          MemoryStream stream = new MemoryStream(byteArray);

          var s = new System.Xml.Serialization.XmlSerializer(typeof(jobs));
          jobs o = (jobs)s.Deserialize(XmlReader.Create(stream));

          Console.ReadLine();
      }
  }
}

And thats it. Any missing nodes in your XML will just be blank rather than causing an error.