Skip to content

Welcome

Welcome to my blog about the software tools / techniques I have used.

The first posts, were originally hosted in a variety of places,  &  I thought it would be nice to bring them all together.   Its mainly a journal of  some of the things I have worked on, that might be useful to me in the future.  If it helps anyone else, then great.

I have been developing Web Applications with Domino / Lotus Notes since the 90s, mainly in areas where workflow & security are important. Before that I worked on financial systems with Foxpro, Oracle & PL/1.

In parallel with Domino, recently I have focused more on C# .NET, Sharepoint, JQuery , Javascript, CSS  & HTML5 .

Richard

When Your C# deserialize fails

I had a problem deserializing some real world XML into classes created by doing an Edit \ Paste Special \ Paste XML as classes in Visual Studio 2012.

My Application (MyAPP )consumes numerous SOAP services provided by an external organisation (xtOrg) , & most work with the following code:

var responseXML = getXMLSOAPResponse(credential,url, SOAPObj);
var reader = new StringReader(responseXML.ToString());
XmlSerializer ser = new XmlSerializer(typeof(Envelope));
var instance = (Envelope)ser.Deserialize(reader);

In one service, the deserialize failed with the error “specified type was not recognized: name= PersonNameType” (See the bottom of the page for the XML)  This was frustrating because I only needed a subset of the data, a list of ‘OtherReferences’, which as far as I could see were parsing OK, before the failing data.
The ASP &  MSDN forums were full of people that offered solutions, but I could not get any of their suggestions to work, so I addressed it manually, using XPATH manual parsing.

All I wanted from this were the OtherRef fields, I could manually parse them with Xpath. This might not be suitable for a complete deserialization of the XML, but it suited the simple data I wanted : a list of other references:

List<OtherRef> retval = new List<OtherRef>();
XmlDocument xmldoc = new XmlDocument();

var responseXML = getXMLSOAPResponse(credential,url, SOAPObj);
// getSoapResponse is a custom function to fire a predefined 
// SOAPObject, at a url, with a credential. It returns a string

xmldoc.LoadXml(responseVal);
// retrieve XML & load to XMLDOC

XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmldoc.NameTable);
nsmgr.AddNamespace("ns4", "http://www.xtOrg.co.uk/frameworki/common");
//Create a namespace manager to handle the namesspaces we want within the 
//XML. ( I suspect that this was part of the problem in my original code )


XmlNodeList nodelist = xmldoc.SelectNodes("//ns4:OtherRef", nsmgr);
// get the otherrefs Node into a nodelist. 
// This is the tricky part, and is an iterative process, 
//depending on the degree of nesting you want 

foreach (XmlNode node in nodelist)
{
XmlNodeList Childnodelist = node.ChildNodes;
// Build another Nodelist for the current Node
string OtherRefValue = "";
string OtherRefType = "";
// Set up empty fields for the object we want to build: a 2 column list

 foreach (XmlNode Chnode in Childnodelist)
 {
// parse the type & value into the correct temporary variable type 
 if (Chnode.Name == "ns4:OtherRefValue")
 {
 OtherRefValue = Chnode.InnerText; 
 }

if (Chnode.Name == "ns4:OtherRefType")
 {
 OtherRefType = Chnode.InnerText; 
 }
 }

 OtherRef tempRef = new OtherRef();
//Create a strongly typed OtherRef object 

 tempRef.OtherRefType = OtherRefType;
 tempRef.OtherRefValue = OtherRefValue;
// and populate it 

 retval.Add(tempRef);
// and add it to the list 
}

Then you get a strongly typed list.  Its not a solution for every scenario, especially if you wanted to create  a POCO object of the entire XML.

One other gotcha with Edit \ Paste Special \ Paste XML as classes, is that sometimes number objects can be defined to be the smallest available ,  Eg uint which in sometimes can only store a value  of 65,535….  Suppose your service retrieves a value of 100000…

Here is the XML That was returned.  The green text shows the data I wanted & the red shows the data that was causing the problem

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
 <env:Header>
 <wsa:RelatesTo xmlns:wsa="http://www.w3.org/2005/08/addressing">0b032838-d2de-db86-a3c2-nnnnnnnn</wsa:RelatesTo>
 </env:Header>
 <env:Body>
 <ns6:GetPersonDetailsResponse xmlns:ns3="http://www.govtalk.gov.uk/people/PersonDescriptives" xmlns:ns4="http://www.xtOrg.co.uk/api/common" xmlns:ns5="http://www.govtalk.gov.uk/people/AddressAndPersonalDetails" xmlns:ns6="http://www.xtOrg.co.uk/api/getpersondetails" xmlns:ns7="http://www.xtOrg.co.uk/api/searchperson" xmlns:ns8="http://www.xtOrg.co.uk/api/createpersonepisode" xmlns:ns9="http://www.xtOrg.co.uk/api/createperson" xmlns:ns10="http://www.xtOrg.co.uk/api/CreateEp" xmlns:ns11="http://www.xtOrg.co.uk/api/createform">
 <PersonDetails>
 <apiPersonId>99999</apiPersonId>
 <PersonNames>
 <ns4:PersonName>
 <ns3:PersonGivenName>Bruce</ns3:PersonGivenName>
 <ns3:PersonFamilyName>Skywalker</ns3:PersonFamilyName>
 <ns4:PersonNameType>MAIN</ns4:PersonNameType>
 </ns4:PersonName>
 </PersonNames>
 <PersonAddresses>
 <ns4:PersonAddress CorrespondenceAddress="true" StartDate="2015-06-26">
 <ns4:PostalAddress>
 <ns5:Line>22 Acacia Ave</ns5:Line>
 <ns5:Line>AnyTown</ns5:Line>
 </ns4:PostalAddress>
 <ns4:PersonAddressType>MAIN</ns4:PersonAddressType>
 <ns4:AddressInLA>N</ns4:AddressInLA>
 <ns4:Accommodation>
 <ns4:DwellingType>MAIN</ns4:DwellingType>
 </ns4:Accommodation>
 </ns4:PersonAddress>
 </PersonAddresses>
 <PersonBirthDate EstimatedDateOfBirth="false">1913-06-11</PersonBirthDate>
 <PersonContact>
 <ns4:Telephone TelUse="home" TelMobile="no" TelPreferred="no">
 <ns5:TelNationalNumber>01234567890</ns5:TelNationalNumber>
 </ns4:Telephone>
 </PersonContact>
 <OtherRefs>
 <ns4:OtherRef>
 <ns4:OtherRefValue>9999999999</ns4:OtherRefValue>
 <ns4:OtherRefType>NHS</ns4:OtherRefType>
 </ns4:OtherRef>
 <ns4:OtherRef>
 <ns4:OtherRefValue>123456</ns4:OtherRefValue>
 <ns4:OtherRefType>PRN</ns4:OtherRefType>
 </ns4:OtherRef>
 <ns4:OtherRef>
 <ns4:OtherRefValue>P123456</ns4:OtherRefValue>
 <ns4:OtherRefType>LEGACY_PERSON_ID</ns4:OtherRefType>
 </ns4:OtherRef>
 </OtherRefs>

 <Gender>1</Gender>
 <MaritalStatus>n</MaritalStatus>
 <InvolvedWorkers>
 <ns4:Worker>
 <ns4:apiWorkerId>100137910</ns4:apiWorkerId>
 <ns4:WorkerName xsi:type="ns4:PersonNameType" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <ns3:PersonGivenName>Joe</ns3:PersonGivenName>
 <ns3:PersonFamilyName>Bloggs</ns3:PersonFamilyName>
 <ns4:PersonNameType>MAIN</ns4:PersonNameType>
 </ns4:WorkerName>
 <ns4:WorkerContact>
 <ns4:Telephone TelMobile="yes" TelPreferred="no">
 <ns5:TelNationalNumber>07976282585</ns5:TelNationalNumber>
 </ns4:Telephone>
 </ns4:WorkerContact>
 </ns4:Worker>
 <ns4:Worker>
 <ns4:apiWorkerId>100137914</ns4:apiWorkerId>
 <ns4:WorkerName xsi:type="ns4:PersonNameType" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

 <ns3:PersonGivenName>Fred</ns3:PersonGivenName>
 <ns3:PersonFamilyName>Smith</ns3:PersonFamilyName>
 <ns4:PersonNameType>MAIN</ns4:PersonNameType>
 </ns4:WorkerName>
 <ns4:WorkerContact>
 <ns4:Telephone TelUse="work" TelMobile="no" TelPreferred="no">
 <ns5:TelNationalNumber>01926785623</ns5:TelNationalNumber>
 </ns4:Telephone>
 <ns4:Telephone TelMobile="yes" TelPreferred="no">
 <ns5:TelNationalNumber>0778523689</ns5:TelNationalNumber>
 </ns4:Telephone>
 </ns4:WorkerContact>
 </ns4:Worker>
 </InvolvedWorkers>
 </PersonDetails>
 </ns6:GetPersonDetailsResponse>
 </env:Body>
</env:Envelope>

Jquery Datepicker with onclick remote validation

Step 1 define field in Model with the System.Web.MVC.remoteattribute

 [Remote("isDateAvailable", "Validation")]
 [DisplayName("Appointment Date")]
 public DateTime AppointmentOn { get; set; }
This tells the system to do server side validation in the client,  & look in ValidationController.cs for a function called isDateAvailable,  & return the validation as a JSON object to the client.

Step 2 define   isDateAvailable in ValidationController.cs

public JsonResult isDateAvailable(string AppointmentOn )
{
   string Message = "";
   if (AppointmentOn  != null)
            {
 // Has a valid date been passed
                DateTime dt = new DateTime();
                try
                {
                    dt = Convert.ToDateTime(AppointmentOn);
                }
                catch (Exception e)
                {
                    Message = "Invalid Date";
                    return Json(Message, JsonRequestBehavior.AllowGet);
                }
//If we have reached here date is a valid format  - so now do the validation required
                if (PerformMyCustomTestonDate(dt))
                {
// test was passed
                    return Json(true, JsonRequestBehavior.AllowGet);
                }
                else
                {
//  return false test failed 
                    Message = "The date you requested is not available";
                    return Json(Message, JsonRequestBehavior.AllowGet);
                }
            }
            else
            {
                Message = "Date cannot be EMPTY";
                return Json(Message, JsonRequestBehavior.AllowGet);
            }
return Json(Message, JsonRequestBehavior.AllowGet);
}

Step 3 Wire up jquery ui date picker with the validation

$('#EpisodeReceived').datepicker({
 changeMonth: true,
 changeYear: true,
 dateFormat: "dd/mm/yy",
 onSelect: function (dateText, inst) {
// On select workout what element is being clicked & get the form
                var id ="#"+ $(this).attr('id');
                var Aform = $(id).closest('form');
// Tell the form to validate this field
                var validator = $(Aform).validate();
                var thisValidate = validator.element(id);
   }
   });

 

 

Angular JS filter array by date & jquery date picker

Step 1 Add Date picker filter fields from & to to form decorated with angular ng tags to HTML page

From Date:<input type="text" ng-model="fromDate" id="from" ng-click="First()">
To Date:<input type="text" ng-model="toDate" id="to" ng-click="First()">

When clicked into it runs the “First()” function that takes you to the first row in the array

Step 2 Jquery Date picker for the Fields with ids = “from” & “to”   to JQUERY $ function

$(function () {
 $("#from").datepicker({
 changeMonth: true,
 changeYear: true,
 dateFormat: "dd/mm/yy",
 onClose: function (selectedDate) {
 $("#to").datepicker("option", "minDate", selectedDate);
 }});
 $("#to").datepicker({
 changeMonth: true,
 changeYear: true,
 dateFormat: "dd/mm/yy",
 onClose: function (selectedDate) {
 $("#from").datepicker("option", "maxDate", selectedDate);
 }});
 });

Step 3 Set up ng-repeat loop with filter on the HTML Page:

......   other table html
 <tr ng-repeat ="item in filtered = ( rows | filter:otherTextfilter | dateFilter:fromDate:'from' | dateFilter:toDate:'to') | .... other filters / sort etc...
 <td>{{ item.field1}} </td> <td>{{ item.PersonBirthDate}} </td>   etc....
 </tr>

Step 4 Define dateFilter  in the Angular js controller:

myApp.filter('dateFilter', function () {
return function (items, parmDate, mode) {
// function reads in the items array, 
//the value of the parameter date field, 
//& whether this is a greater than or less than comparison.
//  If this is initialising or there are no parameters, return the list unfiltered
if (parmDate === undefined) {
 return items;
 }
// If the parmDate is not yet in the format dd/mm/yyyy - return it unfiltered
if (parmDate.length < 8) {
 return items;
 }
// we must have a valid parmdate to to have reached here.
mode = mode.toLowerCase();
 var dateArr = parmDate.split("/");
 var  reccnt = 0;
 try{
 // Convert the parameter date string into a date.  
//This assumes dd/mm/yyyy - the format set in the jquery date picker in step 2
var filtered = [];
var parmDateDTE = new Date(dateArr[2], dateArr[1] - 1, dateArr[0]);
// adjust parmDateDte by 1 day to allow for cleaner greater than or less than
// tests.  GE or LE tests are messy due to time components
if (mode == "from") {
 parmDateDTE.setDate(parmDateDTE.getDate() - 1);
 }
if (mode == "to") {
 parmDateDTE.setDate(parmDateDTE.getDate() + 1);
 }
angular.forEach(items, function (item) {
// create a date field for each item in the array. Redefine it each time so that if
//the item field is missing, itemDateVal is set to today's date .
 //A null would trigger an error .  But you could set itemDateVal to anything 
var itemDateVal = new Date();
 if (item.PersonBirthDate) {
 itemDateVal = new Date(item.PersonBirthDate.Value);
 }
 // Push the item to the filtered list if it matches the criteria. 
if (mode == "from") {
 if (itemDateVal > parmDateDTE) {
 filtered.push(item);
 reccnt++;
 } }
if (mode == "to") {
 if (itemDateVal < parmDateDTE) {
 filtered.push(item);
 reccnt++; 
}}
 });
return filtered;
 }
 catch (err){
 alert(err.message);
 }};
});
First goes in the main controller

$scope.First = function (value) {
 $scope.startFrom = 0;
 $scope.goTo = $scope.pageSize;
 $scope.pageNo = getPageNo();
 return $scope.startFrom;
 }

C# WebClient and Soap Services with WSSe security

Adding a WSDL to a .NET project’s service references is meant to give a series of proxy classes to run a remote SOAP service.

Sometimes it doesn’t always work.

When that happened to me, I decided to manually build a SOAP packet, & use .NET  WebClient.upload to run the service directly.

Step 1 SOAPUI

To eliminate my own code from the mix,  I downloaded SOAPUI, & built a new project and request based on the service WSDL.  There is one gotcha here.  The default request generates a Soap header like

<soapenv:Header/>   whereas we need one that looks like
<soapenv:Header>
</soapenv:Header>

To generate the WSSE Header in Soap UI ( with a plain text password), click the Auth(Basic) button, add the Username Password  & click Use global preference.

Then place the cursor between the header start / end tags,  right click to show a menu & select Add WSS Username token

<soapenv:Header>
 <wsse:Security soapenv:mustUnderstand="1" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
 <wsse:UsernameToken wsu:Id="UsernameToken-&&tokenValue">
 <wsse:Username>&&idValue</wsse:Username>
 <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">&&passwordValue</wsse:Password>
 <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">&&generatedNonce</wsse:Nonce>
 <wsu:Created>2015-12-02T09:43:35.833Z</wsu:Created>
 </wsse:UsernameToken>
 </wsse:Security>
 </soapenv:Header>

The Green “play” triangle lets you debug your SOAP query & get responses ( both success & failure) in the window on the right. One gotcha is that you will need to generate new tokens on each query.  Simply delete the Username node or the Security node & generate a new token with the right click menu.

Step 2 Build your SOAP in .NET  & test in SOAPUI

In my .NET project I created a string builder object & used it to build my SOAP packet.  I ran the code in debug mode & pasted the resulting XML string into SOAP UI, where I could validate it & run it against the web service.  This was quite an iterative process.  One particular area of fun was building the WSSE header in C#.  I used these functions ( where credid & credpassword are the credentials required by the service.

 private string BuildSoapHeader(string credid, string credpassword)
 {
 var nonce = getNonce();
 string nonceToSend = Convert.ToBase64String(Encoding.UTF8.GetBytes(nonce));
 string utc = DateTime.Now.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'"); ;
 StringBuilder rawSOAP = new StringBuilder(@"<soapenv:Envelope xmlns:sear=""http://www.remotesite.com/serviceName"" xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"">");
 rawSOAP.Append(@"<soapenv:Header>");
 rawSOAP.Append(@"<wsse:Security soapenv:mustUnderstand=""1"" xmlns:wsse=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"" xmlns:wsu=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"">");
 rawSOAP.Append(@"<wsse:UsernameToken wsu:Id=""UsernameToken-D1A5C91F8C11FC7F2614479411111111"">");
 rawSOAP.Append(@"<wsse:Username>" + credid + "</wsse:Username>");
 rawSOAP.Append(@"<wsse:Password Type=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"">" + credpassword + "</wsse:Password>");
 rawSOAP.Append(@"<wsse:Nonce EncodingType=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"">" + nonceToSend + "</wsse:Nonce>");
 rawSOAP.Append(@"<wsu:Created>" + utc + "</wsu:Created>");
 rawSOAP.Append(@"</wsse:UsernameToken>");
 rawSOAP.Append(@"</wsse:Security>");
 rawSOAP.Append(@"</soapenv:Header>");
 return rawSOAP.ToString();
 }
 protected string getNonce()
 {
 string phrase = Guid.NewGuid().ToString();
 return phrase;
 }

Step 3 Use .NET to send your SOAP to the service, & get a RAW object back.

string url = "www.actualServiceEndpoint.com/serviceName";
 string credid = "&&&userid";
 string credpassword = "&&&Passwordval";
 string searchparms = " <" + parmname + ">" + parmval + "</" + parmname + ">";
 StringBuilder rawSOAP = new StringBuilder();
 rawSOAP.Append(BuildSoapHeader(credid, credpassword));
 rawSOAP.Append(@"<soapenv:Body><sear:SearchPersonRequest><ServiceParameters>");
 rawSOAP.Append(searchparms);
 rawSOAP.Append(@"</ServiceParameters></sear:SearchPersonRequest></soapenv:Body></soapenv:Envelope>");
 
string SOAPObj = rawSOAP.ToString();
 using (var wb = new WebClient())
 {
 
 wb.Credentials = new NetworkCredential(credid, credpassword);
var responseVal = wb.UploadString(url, "POST", SOAPObj);
 
 }

This code builds your SOAP, creates a web client with credentials for the remote service  & provides responseVal as a ‘home’ the reply.

Step 4 parsing the results

Go to the Models area ( I did this in MVC) & create a new class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MosaicAPITest.Models
{
 public class MyNewClass1
 {/// Cursor goes here
 }
}

Go To SOAPUI, & copy the XML from a successful query to the clipboard.

Put the cursor where indicated in the class definition, then go to the EDIT menu.  Paste Special , Paste XML as classes. & all your Serialize / Deserialize classes are there.

Update your service call code as follows ( I am parsing out a SearchPersonResults object:

wb.Credentials = new NetworkCredential(credid, credpassword);
 var responseVal = wb.UploadString(url, "POST", SOAPObj);
 XmlSerializer ser = new XmlSerializer(typeof(Envelope));
 var reader = new StringReader(responseVal.ToString());
 var instance = (Envelope)ser.Deserialize(reader);
foreach (SearchPersonResults person in instance.Body.SearchPersonResponse)
 {
 people.Add(person);
 }

 

 

 

 

Dynamic URL links from web.config which are rewritten in jquery to reflect a querystring parameter from the current page

The need for this came because we needed an extendable menu (hence the web config settings) for a page, which had a  querystring parm of  ‘ID = nnnn’, was to be injected into those menus at run time. In standard MVC you could do this in the controller. But the previous developer had written this as a single page application, & also got the system to dynamically overwrite the querystring & store some of those values as cookies as the user navigated the system!  Throughout this example {IDVAR} is the value of id=parameter

So we needed to use the controller & jquery to address this.

Web.Config values for url template, title, & which button to mark as active :

<add key="RemotelinksUtls" value="http://app1:1234/#/dash?ID={IDVAR}/&z=m|http://app3/?q={IDVAR}&vv=po|http://app2:999/Home/Index?ID={IDVAR}&=ff" />
<add key="RemotelinksTitle" value="Application One | App3 | app Two " />
<add key="RemotelinksActive" value="1" />

Snippet from the home controller

 public ActionResult Index()
 {
 Regex pattern = new Regex("[:.0123456789]");
// get config values into memory variables
 string SLinks = (ConfigurationManager.AppSettings["RemotelinksUtls"]);
 string Titles = ConfigurationManager.AppSettings["RemotelinksTitle"];
 string ActiveString = ConfigurationManager.AppSettings["RemotelinksActive"];
// Convert the ActiveString into an int so we can compare the loop position with it.
 int ActiveElement = 0;
 int.TryParse(ActiveString, out ActiveElement);
 // Split the url &title strings at | into lists
 List<string> RemoteList = new List<string>();
 List<String> LList = SLinks.Split('|').ToList();
 List<String> TList = Titles.Split('|').ToList();
 int LinkCnt = LList.Count;
// Use the lists to create a link button for each element & set one to be active 
 string templink = "";
 for (int i = 0; i < LinkCnt; i++)
 {
 templink = "<span ><a href=\"http://" + LList[i] + "\" class=";

 if (i == ActiveElement)
 { // if the current element is the one which is set to be active, set its class to primary
 templink += "\"btn btn-primary\" >";
 }
 else
 {// Or set its class to default
 templink += "\"btn btn-default\" >";
 }
 templink += TList[i] + "</a></span> ";
//add the link to the RemoteList list 
 RemoteList.Add(templink);
 }
// Put the RemoteList into the Remotelinks viewbag item
 ViewBag.Remotelinks = RemoteList;
return View();
 }

In the index.cshtml loop through viewbag.Remotelinks, nested under the div remlinks to render the HTML link buttons

At this point, the hrefs will all include the static value  q={IDVAR}. In an ordinary MVC program we’d have reset {IDVAR} in the controller.  But we aren’t going back to the controller because this is a single page application.  Additionally as the querystring gets rewritten,  we need to set / check the cookie values.  It would be more efficient to have reused some of the JS variables, but for the sake of clarity I have kept them distinct

This is done using JQUERY & javascript:

$( document ).ready(function() {
// if there's an id parm in the querystring , use that , otherwise use the existing cookie
var qparm = "";
//Set scope for in memory variable
var idno = qs("id");
if (!$.isEmptyObject(idno))
{//Set the cookie
setCookie("IDNumber",idno)
qparm = idno;
}
else {//Read the Cookie
var Lastno=getCookie("IDNumber")
qparm = Lastno;
}

//set all of the hrefs under the 'remlinks' div, so that the relevant id value 
// ( from query string if its there, from the cookie if its not)  replaces
// {idvar} - some websamples show you concatenating this, 
//but suppose we have hash variables too...

$('#remlinks a').each(function () {
var href = $(this).attr('href');
if (href) {
href = href.replace('{IDVAR}',qparm)
$(this).attr('href', href);
}
});
// end remlinks each loop
});
//end document ready fn

function qs(key) {
//parse querystring for key value
key = key.replace(/[*+?^$.\[\]{}()|\\\/]/g, "\\$&"); 
// escape RegEx control chars
var match = location.search.match(new RegExp("[?&]" + key + "=([^&]+)(&|$)"));
return match && decodeURIComponent(match[1].replace(/\+/g, " "));
}

function setCookie(key, value) {
var expires = new Date();
//if needed, expire cookie tomorrow
//expires.setTime(expires.getTime() + (1 * 24 * 60 * 60 * 1000));
// document.cookie = key + '=' + value + '; path =/;expires=' + expires.toUTCString();
// or leave expires off, so we loose cookie when browser closes 
document.cookie = key + '=' + value + '; path =/;';
}

function getCookie(key) {
var keyValue = document.cookie.match('(^|;) ?' + key + '=([^;]*)(;|$)');
return keyValue ? keyValue[2] : null;
}

Oauth2 to a custom api with basic authentication in System.Net.WebClient

My .NET app needed to consume a 3rd party REST Api , which had its own Basic Authentication  .NET’s  MVC samples come with predefined libraries for Google or Facebook Authentication, which are not easy to extend to other authenticators.

I also had a look at DotNetOpenAuth,  & that too was problematic.

So I went to the 3rd party’s API web page & used Fiddler to workout what was going on in the API ( actual endpoints, request header values etc)  & reproduced that in a console App using System.Net.WebClient  & Newtonsoft.JSON

  • The system  uses BASIC Authentication to return a JSON web token.
  • That token was then added to the getData url to get data

Sure, this isn’t production grade, but its a started for 10.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Runtime.Serialization;
using System.IO;
using Newtonsoft.Json;

namespace ConsoleOauth
{
 class Program
 {
 
 static void Main(string[] args)
 {
//Set creds
 string uid="MyAppId";
 string upass="MyAppPass";
//set Token Generator URL
 string t_Url="https://remotesite/api/oauth/token?client_id="+uid+"&grant_type=client_credentials";
WebClient Wclient = new WebClient();
WebClient Wclient2 = new WebClient();
string authInfo = uid + ":" + upass;
// credentials are added to the Authorization header as "Basic "plus Base64 encoded Id & password..  over SSL 
// We also need to say we'll accept JSON
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
Wclient.Headers["Authorization"] = "Basic " + authInfo;
Wclient.Headers["Accept"] = "application/json";
//Download the token from the token_generator url
var t_json = Wclient.DownloadString(t_url);
// deserialize the json into an object of type J_Tok ( JSON TOKEN) so its useable
J_tok t_obj = JsonConvert.DeserializeObject<J_tok>(t_json);

// Authorised Now get data

// Build GetData url & add the web token to the end
string P_url = "https://remoteSite/api/getData/123?coll=xyz&access_token="+t_obj.accesstoken;
//Set the headers 
Wclient2.Headers["Accept"] = "application/json";
Wclient2.Headers["Content-type"] = "application/json";

//Get the Data as JSON
var P_String = Wclient2.DownloadString(P_url);
//Deserialize the results 
Person P_obj = JsonConvert.DeserializeObject<Person>(P_String);
}
}

JSON TOKEN Object J_Tok  Derived from pasting json string in JSON2CSharp.

 [DataContract]
 public class J_TOK
 {
 [DataMember]
 public string access_token { get; set; } 
 [DataMember]
 public string token_type { get; set; } 
 [DataMember]
 public string scope { get; set; }
 }

Person Object Person Derived from pasting json string in JSON2CSharp.

[DataContract]
public class Person
{

[DataMember]
public string title { get; set; }
[DataMember]
public string firstNames { get; set; }
[DataMember]
public string lastName { get; set; }
[DataMember]
public string gender { get; set; }
[DataMember]
public DateTime? dateOfBirth { get; set; }
[DataMember]
public bool ageEstimated { get; set; }
[DataMember]
public DateTime? dateOfDeath { get; set; }
[DataMember]
public string ethnicity { get; set; }
[DataMember]
public string maritalStatus { get; set; }
[DataMember]
public string contactAddress { get; set; }
[DataMember]
public List<object> telephoneNumber { get; set; }
[DataMember]
public string emailAddress { get; set; }
[DataMember]
public string relationship { get; set; }
[DataMember]
public List<Person> person { get; set; }
[DataMember]
public List<Person> worker { get; set; }
[DataMember]
public List<Person> gp { get; set; }
}

[DataContract]
public class RootObject
{
public string title { get; set; }
[DataMember]
public string firstNames { get; set; }
[DataMember]
public string lastName { get; set; }
[DataMember]
public string gender { get; set; }
[DataMember]
public DateTime? dateOfBirth { get; set; }
[DataMember]
public bool ageEstimated { get; set; }
[DataMember]
public DateTime? dateOfDeath { get; set; }
[DataMember]
public string ethnicity { get; set; }
[DataMember]
public string maritalStatus { get; set; }
[DataMember]
public string contactAddress { get; set; }
[DataMember]
public List<string> telephoneNumber { get; set; }
[DataMember]
public string emailAddress { get; set; }
[DataMember]
public string relationship { get; set; }
[DataMember]
public List<Person> person { get; set; }
[DataMember]
public List<Person> worker { get; set; }
[DataMember]
public List<Person> gp { get; set; }
}
}

WEB API with attribute routing

My last web API started breaking when I added new functions to it :

eg

public string get()
{}
public string get(int i)
{}
public newFunction (int i)
{}

A partial solution was to add a more specific route to the webapiconfig.cs file

 config.Routes.MapHttpRoute(
 "WithActionApi",
 "api/{controller}/{action}/{id}"
 );

This allowed Routes like Employees/Get & Employees/Get/101 to work but still left Employees/101 failing.

This is where Attribute Routing comes in. You can decorate the function directly with the route to access it

[AppAuthorize(Roles = "APIUser")]
[Route("api/employees/{id}")]
public IEnumerable<EMP> Get(int id)
{
var elist = getEMP(id);
return elist;
}
[AppAuthorize(Roles = "APIUser")]
[Route("api/employees")]
public IEnumerable<Emp> Get()
{
var velist = getAllEmps();
return velist;
}

[Route("api/ByDept/{id}")]
public IEnumerable<Emp> GetDept(int id)
{
var elist = getAllEmpsByDepartment();
return elist;
}
Follow

Get every new post delivered to your Inbox.