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 used Java , PHP, C# .NET, Sharepoint, JQuery , HTML5 and a bit of  Ruby On Rails.

Security is a bit of a hobby horse of mine – so its bound to crop up once or twice.  If you’ve found this site, please do check out OWASP  The Open Web Application Security Project – it has some great FREE stuff that helps build more secure applications.

Richard in Warwick.

TSQL Syntax to get useful data from Sharepoint usage logs

SELECT DocumentPath
,Replace(Replace(Reverse(LEFT(REVERSE([DocumentPath]),Charindex(‘/’,REVERSE([DocumentPath])))),’/’,”),’%20′,’ ‘) As DocName
, logtime
, REPLACE(UserLogin,’myDomain\’,”) as UserId
, Convert(date,LogTime) as DateLogged
, CONVERT(time(0),logtime) as TimeLogged
, ReferrerUrl
, SubString(DocumentPath+’/’, (CHARINDEX(‘/’, DocumentPath+’/’, 0) + 1)
, (CharIndex(‘/’, RIGHT(DocumentPath+’/’, (LEN(DocumentPath+’/’) – (CharIndex(‘/’, DocumentPath+’/’, 0)))), 0) – 1)) As TeamSite
,Replace(item,’%20′,’ ‘) as DocumentSet
FROM [SharepointDomain].[dbo].[RequestUsage]
CROSS APPLY dbo.DelimitedSplit8K(DocumentPath,’/’) split
 WHERE DocumentPath like ‘/ascs/%’
  And DocumentPath not like ‘%dll’
  And DocumentPath not like ‘%asmx’
  And DocumentPath not like ‘%_vti_bin%’
  And DocumentPath not like ‘%_layouts%’
This uses the following features to get levels out of the URL
SubString(DocumentPath+’/’, (CHARINDEX(‘/’, DocumentPath+’/’, 0) + 1)
, (CharIndex(‘/’, RIGHT(DocumentPath+’/’, (LEN(DocumentPath+’/’) – (CharIndex(‘/’, DocumentPath+’/’, 0)))), 0) – 1)) As TeamSite
Jeff Modens DelimitedSplit8k to parse out the second level of the url
Replace(Replace(Reverse(LEFT(REVERSE([DocumentPath]),Charindex(‘/’,REVERSE([DocumentPath])))),’/’,”),’%20′,’ ‘) As DocName


DROP FUNCTION dbo.DelimitedSplit8K
GO
 CREATE FUNCTION dbo.Split8K
/***************************************************************************************************
 Purpose:
 Split a given string at a given delimiter and return a list of the split elements (items).

 Returns:
 iTVF containing the following:
 ItemNumber = Element position of Item as a BIGINT (not converted to INT to eliminate a CAST)
 Item       = Element value as a VARCHAR(8000)

 CROSS APPLY Usage Example:
---------------------------------------------------------------------------------------------------
--===== Conditionally drop the test tables to make reruns easier for testing.
     -- (this is NOT a part of the solution)
     IF OBJECT_ID('tempdb..#JBMTest') IS NOT NULL 
        DROP TABLE #JBMTest
;
--===== Create and populate a test table on the fly (this is NOT a part of the solution).
 SELECT *
   INTO #JBMTest
   FROM (
         SELECT 1,'1,10,100,1000,10000,100000,1000000' UNION ALL
         SELECT 2,'2000000,200000,20000,2000,200,20,2' UNION ALL
         SELECT 3, 'This,is,a,test'                    UNION ALL
         SELECT 4, 'and so is this'                    UNION ALL
         SELECT 5, 'This, too (no pun intended)'
        ) d (SomeID,SomeValue)
;
GO
--===== Split the CSV column for the whole table using CROSS APPLY (this is the solution)
 SELECT test.SomeID, split.ItemNumber, split.Item
   FROM #JBMTest test
  CROSS APPLY
        (
         SELECT ItemNumber, Item
           FROM dbo.DelimitedSplit8k(test.SomeValue,',')
        ) split
;
---------------------------------------------------------------------------------------------------
 Notes:
 1. Optimized for VARCHAR(7999) or less.  No testing or error reporting for truncation at 7999
    characters is done.
 2. Optimized for single character delimiter.  Multi-character delimiters should be resolved
    externally from this function.
 3. Optimized for use with CROSS APPLY.
 4. Does not "trim" elements just in case leading or trailing blanks are intended.
 5. If you don't know how a Tally table can be used to replace loops, please see the following...
    http://www.sqlservercentral.com/articles/T-SQL/62867/
 6. Changing this function to use VARCHAR(MAX) will cause it to run twice as slow.  It's just the 
    nature of VARCHAR(MAX) whether it fits in-row or not.  Some recovery of speed can be realized
    by doing datatype matching for CHARINDEX which changes it's datatype for VARCHAR(MAX).
 7. Multi-machine testing for the method of using UNPIVOT instead of 10 SELECT/UNION ALLs shows
    that the UNPIVOT method is quite machine dependent and can slow things down quite a bit.
 8. Performance testing shows using "TOP" for the limiting criteria of "N" is actually 
    slower and slightly more CPU intensive than the traditional WHERE N < LEN(@pString) + 2. 
 9. Performance testing shows using ORDER BY (SELECT x) where "x" is anything is actually
    slower and slightly more CPU intensive than the traditional ORDER BY (SELECT N).
***************************************************************************************************/
--===== Define I/O parameters
        (
        @pString    VARCHAR(7999),
        @pDelimiter CHAR(1)
        )
RETURNS TABLE
   WITH SCHEMABINDING
AS
RETURN
--===== "Inline" CTE Driven "Tally Table” produces values up to
     -- 10,000... enough to cover VARCHAR(8000)
WITH
      E1(N) AS ( --=== Create Ten 1's
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 --10
               ),
      E2(N) AS (SELECT 1 FROM E1 a, E1 b),   --100
      E4(N) AS (SELECT 1 FROM E2 a, E2 b),   --10,000
cteTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT N)) FROM E4)  
--===== Do the split
 SELECT ROW_NUMBER() OVER (ORDER BY N) AS ItemNumber,
        SUBSTRING(@pString, N, CHARINDEX(@pDelimiter, @pString + @pDelimiter, N) - N) AS Item
   FROM cteTally
  WHERE N <= DATALENGTH(@pString)+1 --DATATLENGTH allows for trailing space delimiters
    AND SUBSTRING(@pDelimiter + @pString, N, 1) = @pDelimiter
;
GO

C# function for file upload to convert HttpPostedFileBase to byte array.

The commented code is here for comparison.   This had the drawback that it generated binary nulls after the end of file marker.  Some PDF files were corrupted by that code.

private byte[] readFileContents(HttpPostedFileBase file)
{
Stream fileStream = file.InputStream;
var mStreamer = new MemoryStream();
mStreamer.SetLength(fileStream.Length);
fileStream.Read(mStreamer.GetBuffer(), 0, (int)fileStream.Length);
mStreamer.Seek(0, SeekOrigin.Begin);
byte[] fileBytes = mStreamer.GetBuffer();

//////using (MemoryStream ms = new MemoryStream())
//////{
////// file.InputStream.CopyTo(ms);
////// fileBytes = ms.GetBuffer();
//////}

return fileBytes;
}

 

Typical use may be within MVC …..   EG

[HttpPost]
public ActionResult Create(myMetaDataClass collection, HttpPostedFileBase uploadfile)
{

// Other code

byte[] fileContents = null;
fileContents = readFileContents(uploadfile);

///other code to put the filecontents into form that can be posted to a web service defined by serviceurl

StringBuilder stringBuilder = new StringBuilder();

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serviceurl);

StringBuilder stringBuilder = new StringBuilder();
// code to parse out myMetaDataClass collection into parameters for webservice

stringBuilder.Append(postParameterKeys.ElementAt(1) + “=” +
HttpUtility.UrlEncode(Convert.ToBase64String(fileContents)) + “&”);

//More string builder code
//

byte[] postData = Encoding.UTF8.GetBytes(stringBuilder.ToString());

request.ContentLength = postData.Length;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(postData, 0, postData.Length);
}

using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
//etc…..

Dynamic JQGrid definition with Razor… & time sensitive toastr..

Build JavaScript Functions to format a url based on that row : This one has a bootstrap button in it, for a “Delete” function

 function fmatDelLnk(cellvalue, options, rowObject) {

var file_name = rowObject[6].toString();

var catref = rowObject[7].toString();

var ServiceURL = $(‘#ServiceURL’).val();

var link=”/Document/Del?identifier=” + $_GET["identifier"] + “&cat=” + catref + “&empid=” + $_GET["empid"] + “&filename=” +  file_name ;

var url = “<a href='” + link + “‘ class=’btn btn-mini ‘ style=’color:white;font-weight:bold’>Delete</a>”;

return url

}

( The inline css is to fix a conflict between bootstrap & other css for hrefs on the page)

//Similar function but with text as a link.  The blank on the end of label is to make the entire cell clickable

   function fmatDLnk(cellvalue, options, rowObject) {

var  file_name  = rowObject[6].toString();

var label = rowObject[1].toString()+”                                  “;

var catref = rowObject[7].toString();

var WebServiceURL = $(‘#WebServiceURL’).val();

var urlLink = “<a href=/Document/Launch?identifier=” + $_GET["identifier"] + “&cat=” + catref + “&empid=” + $_GET["empid"] + “&filename=” +  file_name  + “>” + label+”</a>”;

return urlLink

}

//Similar function but for use in another column. Its label for the href would be different

function fmatELnk(cellvalue, options, rowObject) {

etc…….

}

JQuery to run when the form opens

//Controls whether or not to display a toastr alert from an update function.  This page is launched directly & is a redirect page after updates.  #LastDbUpdate is a form field populated from a session variable indicating when an update was successful

$(document).ready(function () {

var dstr = $(‘#LastDbUpdate’).val();

// max diff is the cutoff period in milliseconds  after which we ignore the update parm 

       var maxdiff = 10000;

var updateVal = $(‘#update’).val();

var diff = maxdiff + 90;

var currTime = new Date();

var LstUpdDte = new Date();

LstUpdDte.setDate(LstUpdDte.getDate() – 1);

// #LastDbUpdatestored on server as the last datetime this user updated the database. All components are seprated by “:” 

       if (dstr != “”)

{   var d  = dstr.split(“:”);

for (var i in d )

{

d[i] = parseInt(d [i], 10);

}

LstUpdDte = new Date(d[0], d[1]-1,d[2], d[3], d[4], d[5],0);

diff = currTime.getTime() – LstUpdDte.getTime();

var d_dte = LstUpdDte.toTimeString().substr(0, 5);

// if an update happened in the last 10000 milliseconds, then display the toastr message 

 

if ((updateVal == ‘true’) && ( diff<maxdiff))

{

toastr.options = {

//                  … assorted toastr display parms

}

toastr.success(“Database updated  ” + d_dte )

}

 

// Build dynamic array for jqgrid determining whether or not to add columns, & making the content reusable between different tables ( EG in a Tabbed interface)

$(function () {

var colMdl = [];

// Add each column definition one at a time to the array, the formatters are doclinks set by the above js functions. Not all the formatters have been included above

colMdl.push({ name: ‘id’, index: ‘id’, hidden: true });

colMdl.push({ name: ‘title’, index: ‘title’, width: 330, formatter: fmatDLnk });

colMdl.push({ name: ‘effectivedate’, index: ‘effectivedate’, width: 105, formatter: fmatELnk });

colMdl.push({ name: ‘assignmentid’, index: ‘assignmentid’, hidden: true });

colMdl.push({ name: ‘createdby’, index: ‘createdby’, width: 105, formatter: fmatCbyLink });

colMdl.push({ name: ‘createddate’, index: ‘createddate’, width: 105, formatter: fmatCDteLnk });

colMdl.push({ name: ‘linkfilename’, index: ‘linkfilename’, hidden: true });

colMdl.push({ name: ‘categoryreferencenumber’, index: ‘categoryreferencenumber’, hidden: true });

 

//Razor if test to determine whether or not add the “Delete” Button as a cell in the row 

       @if (User.IsInRole(“myCustomRole”)) {

<text>

colMdl.push({ name: ‘Delete’, width: 150, formatter: fmatDelLnk });

</text>

}

var colHdr = ['Id', 'Document Title', 'Effective Date', '', 'Created By', 'Created Date', 'Link Filename', 'Category Reference'];

//Razor if test to determine whether or not add the “header” column for a delete button

@if (User.IsInRole(“myCustomRole”)) {

<text>

colHdr.push(‘Actions’);

</text>

}

//Sample table to position code on page  

 

jQuery(“#cat1″).jqGrid({

url: ‘/DocsJSON/read?ident=’+$_GET["ident"] + ‘&empid=’ + $_GET["empid"] + ‘&cat=1′,

datatype: “json”,

colNames: colHdr,

colModel: colMdl,

rowNum: 10,pager: ‘#pagerrecruitment’,

sortname: ‘effectivedate’,viewrecords: true,

sortorder: “desc”,height: “350px”,border: “none”

});

jQuery(“#cat1″).jqGrid(‘navGrid’, ‘#pagerrecruitment’, { edit: false, add: false, del: false, search: false, refresh: false });

 

jQuery(“#cat2″).jqGrid({

url: ‘/DocsJSON/read?ident=’+$_GET["ident"] + ‘&empid=’ + $_GET["empid"] + ‘&cat=2′,

datatype: “json”,

colNames: colHdr,

colModel: colMdl,

rowNum: 10,pager: ‘#pagerclearances’,

sortname: ‘effectivedate’, viewrecords: true,

sortorder: “desc”,height: “350px”, border: “none”

});

jQuery(“#cat2″).jqGrid(‘navGrid’, ‘#pagerclearances’, { edit: false, add: false, del: false, search: false, refresh: false });

 

etc…

Jquery, twitter, bootstrap & MVC form layout

Bootstrap is framework of css classes that simplify web form design. These are some of the conventions I found useful & how I got round the gotchas…
Required js libraries: 

<script src=”@Url.Content(“~/Scripts/jquery-1.5.1.min.js”)” type=”text/javascript”></script>
<script src=”@Url.Content(“~/Scripts/jquery.validate.js”)” type=”text/javascript”></script>
<script src=”@Url.Content(“~/Scripts/jquery.validate.unobtrusive.js”)” type=”text/javascript”></script>
<script src=”@Url.Content(“~/Scripts/modernizr-1.7.min.js”)” type=”text/javascript”></script>
<link rel=”stylesheet” href=”http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css”&gt;
<script src=”../../Scripts/bootstrap.file-input.js” type=”text/javascript”></script>
<script src=”../../Scripts/modernizr-2.5.3.js” type=”text/javascript”></script>

& associated css

Textfields

<div>
       <label>
           @Html.LabelFor(model => model.sourceURL)</label>
       <div>
           : @Html.TextBoxFor(model => model.sourceURL, new { @class = “form-control”,placeholder =”First field name” })
           @Html.ValidationMessageFor(model => model.sourceURL)
       </div>
   </div>
Built using .net razor, relying on the MVC model for validation, labeling etc,this makes it easier to cascade a description or validation through the application. .
The fieldlabel sits in a class “3” wide The field sits in a class 8 wide.     ( There is a sidebar to of width 1 to make up the 12 units bootstrapper is based on. The formcontrol class, left aligns the field. ‘placeholder’ places text in the field until it is clicked on ( Not IE 8 or )
Integer fields
       <div>
           : @Html.TextBoxFor(model => model.anInteger, new { @class = “text-right”, type=”number”,placeholder =”0″})
           @Html.ValidationMessageFor(model => model.anInteger)
       </div>
also set in col-xs-8 class @class = “text-right”, aligns the field to the right to display like a number . type=”number” places a spinner

Date fields

The HTML or razor code is set up like text .  ( even though the model is defined as a datetime) A dynamic calendar picker is created with jqueryui:
<script>
$(function () {
//Bootstrap can interfere with this, but noConflict()clears this
$.noConflict();
// When a field( eg #fromDate) is clicked in, show calendar
// When a value is picked, set a limit on a related date         
$(“#fromDate”).datepicker({dateFormat: “dd/mm/yy”,
onClose: function (sel_Dte)
{     $(“#toDate”).datepicker(“option”, “minDate”, sel_Dte);     }
});
       
$(“#toDate”).datepicker({dateFormat: “dd/mm/yy”,
onClose: function (sel_Dte)
{     $(“#fromDate”).datepicker(“option”, “maxDate”, sel_Dte);     }
     });
   });</script>

File picker

By default File pickers display differently in each browser.  This is a pain.  This website suggested hiding the file picker, & then showing a button to invoke the hidden filepickers click event & then showing its results in a field.    There were a few cross browser issues, but these were addressed as follows :
1) Place hidden file input at end of form , with an onchange event calling a javascript function myFunction
<input type=”file” name=”file” style=”visibility: hidden” id=”pdffile” onchange=”myFunction(this,’#subfile’);” />
2) Place the visible elements in a control group . These consist of a button , whose purpose is to invoke the hidden file input’s click event, & the field “subfile” to show the results:
<div>  
       <label>
           Choose A File
       </label>
       <div>
           : <input type=”text” id=”subfile”>
           <a onclick=”$(‘#pdffile’).click();”>Browse</a>
       </div>
   </div>
3) Add the javascript for myFunction()
   function myFunction(source, target) {
\\ take in a field object & the name of a target
var x = $(source).val();
\\get the value of the field from source, convert it to an array.
\\ get out the last element
       var z = x.split(“\\”).pop();
\\ set the target value
       $(target).val(z);
   }
The sequence runs as follows:
  1. User clicks visible button .
  2. Visible button invokes pdffiles.click() event
  3. This displays a file picker, whose results are returned to ‘pdffile’
  4. pdffile’s onchange event is triggered, passing itself & the name of a target id, to the function myFunction()
  5. This gets the filename element from this input field ( it could include, a filename only , the  true path or a false path)  & updates the field accordingly
@model BootStrapApp.Models.Document
@{
   ViewBag.Title = “Create”;
   Layout = “~/Views/Shared/_Layout.cshtml”;
}
<h2>Create</h2>
<script>
   function myFunction(source, target) {
       var x = $(source).val();
       var z = x.split(“\\”).pop();
       $(target).val(z);
   }
   
   $(function () {
       $.noConflict();
       $(“#fromDate”).datepicker({
           dateFormat: “dd/mm/yy”,
           onClose: function (selDte) {
               $(“#toDate”).datepicker(“option”, “minDate”, selDte);
           }
       });
       
       $(“#toDate”).datepicker({
           dateFormat: “dd/mm/yy”,
           onClose: function (selDte) {
               $(“#fromDate”).datepicker(“option”, “maxDate”, selDte);
           }
       });
   });
</script>
<form>
<fieldset>
   <legend>Document</legend>
   <div>
       <label>
           @Html.LabelFor(model => model.sourceURL)</label>
       <div>
           : @Html.TextBoxFor(model => model.sourceURL, new { @class = “form-control”,placeholder =”First field name” })
           @Html.ValidationMessageFor(model => model.sourceURL)
       </div>
   </div>
   <div>
       <label>
           @Html.LabelFor(model => model.anInteger)</label>
       <div>
           : @Html.TextBoxFor(model => model.anInteger, new { @class = “text-right”, type=”number”,placeholder =”0″})
           @Html.ValidationMessageFor(model => model.anInteger)
       </div>
   </div>
   <div>
       <label>
           @Html.LabelFor(model => model.toDate)</label>
       <div>
           : @Html.TextBoxFor(model => model.toDate, new { @class = “form-control” })
           @Html.ValidationMessageFor(model => model.toDate)
       </div>
   </div>
   <div>
       <label>
           @Html.LabelFor(model => model.fromDate)</label>
       <div>
           : @Html.TextBoxFor(model => model.fromDate)
           @Html.ValidationMessageFor(model => model.fromDate)
       </div>
   </div>
   
   <div>
 
       <label>
           Choose A File
       </label>
       <div>
           : <input type=”text” id=”subfile”>
           <a onclick=”$(‘#pdffile’).click();”>Browse</a>
       </div>
   </div>
</fieldset>
<div>
   <label for=”inputEmail”>
       Email:</label>
   <div>
       : <input type=”email” id=”inputEmail” placeholder=”Email”>
   </div>
</div>
<div>
   <label for=”inputPassword”>
       Password:</label>
   <div>
       : <input type=”password” id=”inputPassword” placeholder=”Password”>
   </div>
</div></div>
<div>
   <label for=”confirmPassword”>
       Confirm Password:</label>
   <div>
      : <input type=”password” id=”confirmPassword” placeholder=”Confirm Password”>
   </div>
</div>
<div>
   <label for=”firstName”>
       First Name:</label>
   <div>
       : <input type=”text” id=”firstName” placeholder=”First Name”>
   </div>
</div>
<div>
   <label for=”lastName”>
       Last Name:</label>
   <div>
       : <input type=”text” id=”lastName” placeholder=”Last Name”>
   </div>
</div>
<div>
   <label for=”phoneNumber”>
       Phone:</label>
   <div>
       <input type=”tel” id=”phoneNumber” placeholder=”Phone Number”>
   </div>
</div>
<div>
   <label>
       Date of Birth:</label>
   <div>
       <select>
           <option>Date</option>
       </select>
   </div>
   <label>
   </label>
   <div>
       <select>
           <option>Month</option>
       </select>
   </div>
   <label>
   </label>
   <div>
       <select>
           <option>Year</option>
       </select>
   </div>
<div>
   <label>
       Gender:</label>
   <div>
       <label>
           <input type=”radio” name=”genderRadios” value=”male”>
           Male
       </label>
   </div>
   <div>
       <label>
           <input type=”radio” name=”genderRadios” value=”female”>
           Female
       </label>
   </div>
</div>
<div>
   <div>
       <label>
           <input type=”checkbox” value=”news”>
           Send me latest news and updates.
       </label>
   </div>
</div>
<div>
   <div>
       <label>
           <input type=”checkbox” value=”agree”>
           I agree to the <a href=”#”>Terms and Conditions</a>.
       </label>
   </div>
</div>
<br>
<div>
   <div>
       <input type=”submit” value=”Submit”>
       <input type=”reset” value=”Reset”>
   </div>
</div>
     <input type=”file” name=”file” style=”visibility: hidden” id=”pdffile” onchange=”myFunction(this,’#subfile’);” />
</form>

C# MVC reports / navigation screen populated from webconfig with JQuery calendar validation & radio button.

Set the RepNams & urls as values in Web.Config. Use tilda ~ to separate the values.  Its a nice generic separator, because it is not commonly used in English words & phrases.

<appSettings>

<add key=”RepNams” value=”Rep1~Rep2~Rep3~Rep4″ />

<add key=”RepLnk” value=”http://www.google.co.uk~http://WWW.BBC.co.UK~http://www.netvibes.com~http://www.icloud.com&#8221; />

<add key=”RepNamsR” value=”Records up for disposal~Employee records opened~Employee records moved~Employee records deleted” />

<add key=”RepLnkR” value=”http://www.LindaScannell.com~http://WWW.BBC.co.UK~http://www.netvibes.com~http://WWW.BBC.co.UK&#8221; />

<add key=”RepActions” value=”All~Create~Read~Update~Delete” />

</appSettings>

Build a simple Model, to help pass data from the query string to the View, & to allow for easier expansion of validation:

public class Rep

{

[DisplayName("User ID")]

public string UserId { get; set; }

[DisplayName("Employee Id")]

public string EmployeeId { get; set; }

[DataType(DataType.Date)]

[DisplayName("Date from ")]

public DateTime FromDate { get; set; }

[DataType(DataType.Date)]

[DisplayName("Date until")]

public DateTime ToDate { get; set; }

[DisplayName("Actions to Rep on")]

public string[] RepActions { get; set; }

}

Build a controller, with an index capable of taking input parameters

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using System.Net;

using System.Configuration;

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity;

using Microsoft.Practices.EnterpriseLibrary.Security;

using Microsoft.Practices.Unity;

using myMVC_app.Common;

using myMVC_app.Models;

namespace myMVC_app.Controllers

{

[Authorise("RepsAccess")]

public class RepsController : Controller

{

//

// Index takes multiple parms

public ActionResult Index(string userid=””,string empno=””, string from=””,string to =””)

{

// Load data from web.config & parse into arrays

String RepNamStr = ConfigurationManager.AppSettings["RepNams"];

String RepLnkStr = ConfigurationManager.AppSettings["RepLnk"];

String RepActionsStr = ConfigurationManager.AppSettings["RepActions"];

// Build links for left hand array

string[] RepNamArr = RepNamString.Split(‘~’);

string[] RepLnkArr = RepLnkString.Split(‘~’);

string[] RepActionsArr = RepActionsStr.Split(‘~’);

int arrlen = RepLnkArr.Length;

ViewBag.RepNamArr= RepNamArr;

ViewBag.RepLnkArr = RepLnkArr;

ViewBag.RepActionsArr = RepActionsArr;

ViewBag.Warning = “”;

// Build links for right hand array

String RepNamStringR = ConfigurationManager.AppSettings["RepNamsR"];

String RepLnkStringR = ConfigurationManager.AppSettings["RepLnkR"];

string[] RepNamArrR = RepNamStringR.Split(‘~’);

string[] RepLnkArrR = RepLnkStringR.Split(‘~’);

int arrlenR = RepLnkArr.Length;

ViewBag.RepNamArrR = RepNamArrR;

ViewBag.RepLnkArrR = RepLnkArrR;

Rep myRep = new Rep();

myRep.RepActions = RepActionsArr;

DateTime tempdate;

if (userid != “”)

{

myRep.UserId = userid;

}

if (empno != “”)

{

myRep.EmployeeId = empno;

}

// validate & set from date

if (from == “”)

{

myRep.FromDate = DateTime.Today.AddYears(-1).Date;

}

else

{

if (DateTime.TryParse(from, out tempdate))

{

myRep.FromDate = tempdate;

}

else

{

myRep.FromDate = DateTime.Today.AddYears(-1).Date;

ViewBag.Warning = “A date has been reset to the default”;

}

}

// validate & set to date

if (to == “”)

{

myRep.ToDate = DateTime.Today.Date;

}

else

{

if (DateTime.TryParse(to, out tempdate))

{

myRep.ToDate = tempdate;

}

else

{

myRep.ToDate = DateTime.Today.Date;

ViewBag.Warning = “A date has been reset to the default”;

}

}

return View(myRep);

}

}

}

Create the view  JQuery data validation displays calendar boxes, whose maximum & minimum values are dynamically set according to what has been chosen already:

The ReportAction is a radio button populated from the ReportActions array with a foreach loop

@foreach(var option in Model.ReportActions)

{

@Html.RadioButton(“ReportAction”, option)

@Html.Label(option)

}

( Prefer radio buttons over dropdowns, as it’s a 1 click operation

There is a left list & a right list of report URLS,  The left list is built with an unorderlist ( UL) whose Li elements are populated with an @for loop.   It shows a report name & passes a report URL to the LaunchReport function…

div id=”ReportsLeft”>

<ul>

@for (int i = 0; i < @ViewBag.ReportNameArray.Length; i++)

{

<li><a  onclick=”LaunchReport(‘@ViewBag.ReportLinksArray[i]‘)”>@ViewBag.ReportNameArray[i]</a></li>

}

</ul>

</div>

LaunchReport uses Jquery to get the field values, including the RadioButton

var userID = $(‘#UserId’).val();

.

.

.

var RadioButton = $(“input[name='RadioButtonName']:checked”).val();

@model myMVC_app.Models.Report

@{Html.EnableClientValidation();}

@{

ViewBag.Title = “Reports Menu”;

Layout = “~/Views/Shared/_Layout.cshtml”;

}

<script>

$(function () {

//  Use Jquery ui date picker to provide date ranges that dynamically adjust the max & //minimum available

$(“#FromDate”).datepicker({

dateFormat: “dd/mm/yy”,

onClose: function (selectedDate) {

$(“#ToDate”).datepicker(“option”, “minDate”, selectedDate);

}

});

$(“#ToDate”).datepicker({

dateFormat: “dd/mm/yy”,

onClose: function (selectedDate) {

$(“#FromDate”).datepicker(“option”, “maxDate”, selectedDate);

}

});

});

</script>

<script type=”text/javascript”>

//  Take the url, & add the available parameters to it..

function LaunchReport(reporturl) {

var userID = $(‘#UserId’).val();

var emp = $(‘#EmployeeId’).val();

var from = $(‘#FromDate’).val();

var to = $(‘#ToDate’).val();

var reportaction = $(“input[name='ReportAction']:checked”).val();

var url = reporturl;

if (reportaction != “”) {

url += “&reportaction=” + reportaction.toString();

}

if (userID != “”) {

url += “&userid=” + userID.toString();

}

if (emp != “”) {

url += “&empno=” + emp.toString();

}

if (from != “”) {

url += “&from=” + from.toString();

}

if (to != “”) {

url += “&to=” + to.toString();

}

window.open(url);

}

</script>

<div id=”headpanel”>

<h3>

Reports Menu</h3>

</div>

<div id=”reportdisplaypanel”>

<div id=”reportdisplaycontent”>

<div id=”SearchParm”>

<div id=”ParmList”>

<h3>

Please enter report parameters to use

</h3>

<div>

<div>

@Html.LabelFor(model => model.UserId)

</div>

<div>

@Html.TextBoxFor(model => model.UserId)

@Html.ValidationMessageFor(model => model.UserId)

</div>

</div>

<div>

<div>

@Html.LabelFor(model => model.EmployeeId)

</div>

<div>

@Html.TextBoxFor(model => model.EmployeeId)

@Html.ValidationMessageFor(model => model.EmployeeId)

</div>

</div>

<div>

<div>

@Html.LabelFor(model => model.FromDate)

</div>

<div>

@Html.EditorFor(model => model.FromDate)

@Html.ValidationMessageFor(model => model.FromDate)

</div>

</div>

<div>

<div>

@Html.LabelFor(model => model.ToDate)

</div>

<div>

@Html.EditorFor(model => model.ToDate)

@Html.ValidationMessageFor(model => model.ToDate)

</div>

</div>

<div>

<div>

@Html.LabelFor(model => model.ReportActions)

</div>

<div>

@foreach(var option in Model.ReportActions) {

@Html.RadioButton(“ReportAction”, option)

@Html.Label(option)

}

@Html.ValidationMessageFor(model => model.ReportActions)

</div>

</div>

<div>

@Html.ValidationSummary(true)

@ViewBag.Warning

</div>

</div>

</div>

<h3>

Then click a report below</h3>

<p>

</p>

<div id=”ReportsLeft”>

<ul>

@for (int i = 0; i < @ViewBag.ReportNameArray.Length; i++)

{

<li><a  onclick=”LaunchReport(‘@ViewBag.ReportLinksArray[i]‘)”>@ViewBag.ReportNameArray[i]</a></li>

}

</ul>

</div>

<div id=”ReportsRight”>

<ul>

@for (int i = 0; i < @ViewBag.ReportNameArrayR.Length; i++)

{

<li><a onclick=”LaunchReport(‘@ViewBag.ReportLinksArrayR[i]‘)”>@ViewBag.ReportNameArrayR[i]</a></li>

}

</ul>

</div>

</div>

</div>

WEB API Part 2 invoking csv

Extending the web api with the  results of Mikes additional post  http://www.asp.net/web-api/overview/formats-and-model-binding/media-formatters.  ( I had to make some tweaks to get it to work for me  :

Visual studio objected to the definition of  CanReadType &  WriteToStream

I had to use ” public override bool CanReadType”  and “public void WriteToStream(”  instead

In the Index HTML I added the following code

A button in the 2nd div:     <input type=”button” value=”Search csv” onclick=”dumpCSV();” />

& the following Javascript functions :

// dumpCSV to invoke the service call to return the API call as CSV

function dumpCSV() {
var id = $(‘#prodId’).val();
$.ajax({
type: “GET”,
url: “api/products/” + id,
dataType: “text”,
success: function (data) {
successCSV(data);
},
error: function (data) {
errorXML(data);
}});}

successCSV(data) parse the contents of the csv string into variable

function successCSV(data) {

var dataArray = data.split(‘,’);
var Name = dataArray[1];
var Category = dataArray[2];
var Price = dataArray[3];
Name = ReplaceAll(Name,”Name”);
Category = ReplaceAll(Category,”Category”);
Price = ReplaceAll(Price,”Price”);

$(‘#product’).text(“From CSV “+Name + ” (” + Category + “) ” + Price);

}

ReplaceAll is a reusable JavaScript function to strip out all instances of unwanted characters from a string

function ReplaceAll(s,customstring) {
// Gets rid of all instances of unwanted values(“,\,:,{,}) and a customstring) from a string
if (customstring != “”)
{
while (s.indexOf(customstring) != -1) s = s.replace(customstring, ”);
}
// Add new chars to Rep
var Rep = new Array(‘”‘,”\\”,”:”,”{“,”}”);
for (i = 0; i < Rep.length; i++)
{
while (s.indexOf(Rep[i]) != -1) s = s.replace(Rep[i], ”);
}

return s;
}

Mike Wasson’s original article:

Internet Media Types

A media type, also called a MIME type, identifies the format of a piece of data. In HTTP, media types describe the format of the message body. A media type consists of two strings, a type and a subtype. For example:

  • text/html
  • image/png
  • application/json

When an HTTP message contains an entity-body, the Content-Type header specifies the format of the message body. This tells the receiver how to parse the contents of the message body.

For example, if an HTTP response contains a PNG image, the response might have the following headers.

HTTP/1.1 200 OK
Content-Length: 95267
Content-Type: image/png

When the client sends a request message, it can include an Accept header. The Accept header tells the server which media type(s) the client wants from the server. For example:

Accept: text/html,application/xhtml+xml,application/xml

This header tells the server that the client wants either HTML, XHTML, or XML.

In Web API, the media type determines how Web API serializes and deserializes the HTTP message body. There is built-in support for XML, JSON, and form-urlencoded data, and you can support additional media types by writing a media formatter.

To create a media formatter, derive from one of these classes:

  • MediaTypeFormatter. This class uses asynchronous read and write methods.
  • BufferedMediaTypeFormatter. This class derives from MediaTypeFormatter but wraps the asynchronous read/write methods inside sychronous methods.

Deriving from BufferedMediaTypeFormatter is simpler, because there is no asynchronous code, but it also means the calling thread can block during I/O.

Creating a Media Formatter

The following example shows a media type formatter that can serialize a Product object to a comma-separated values (CSV) format. This example uses the Product type defined in the tutorial Creating a Web API that Supports CRUD Operations. Here is the definition of the Product object:

namespace ProductStore.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }
}

To implement a CSV formatter, define a class that derives from BufferedMediaTypeFormater:

namespace ProductStore.Formatters
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Net.Http.Formatting;
    using System.Net.Http.Headers;
    using ProductStore.Models;

    public class ProductCsvFormatter : BufferedMediaTypeFormatter 
    {
    }
}

In the constructor, add the media types that the formatter supports. In this example, the formatter supports a single media type, “text/csv”:

public ProductCsvFormatter()
{
    // Add the supported media type.
    SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv"));
}

Override the CanWriteType method to indicate which types the formatter can serialize:

public override bool CanWriteType(System.Type type)
{
    if (type == typeof(Product))
    {
        return true;
    }
    else
    {
        Type enumerableType = typeof(IEnumerable<Product>);
        return enumerableType.IsAssignableFrom(type);
    }
}

In this example, the formatter can serialize single Product objects as well as collections of Product objects.

Similarly, override the CanReadType method to indicate which types the formatter can deserialize. In this example, the formatter does not support deserialization, so the method simply returns false.

protected override bool CanReadType(Type type)
{
    return false;
}

Finally, override the WriteToStream method. This method serializes a type by writing it to a stream. If your formatter supports deserialization, also override the ReadFromStream method.

public override void WriteToStream(
    Type type, object value, Stream stream, HttpContentHeaders contentHeaders)
{
    using (var writer = new StreamWriter(stream))
    {

        var products = value as IEnumerable<Product>;
        if (products != null)
        {
            foreach (var product in products)
            {
                WriteItem(product, writer);
            }
        }
        else
        {
            var singleProduct = value as Product;
            if (singleProduct == null)
            {
                throw new InvalidOperationException("Cannot serialize type");
            }
            WriteItem(singleProduct, writer);
        }
    }
    stream.Close();
}

// Helper methods for serializing Products to CSV format. 
private void WriteItem(Product product, StreamWriter writer)
{
    writer.WriteLine("{0},{1},{2},{3}", Escape(product.Id),
        Escape(product.Name), Escape(product.Category), Escape(product.Price));
}

static char[] _specialChars = new char[] { ',', '\n', '\r', '"' };

private string Escape(object o)
{
    if (o == null)
    {
        return "";
    }
    string field = o.ToString();
    if (field.IndexOfAny(_specialChars) != -1)
    {
        return String.Format("\"{0}\"", field.Replace("\"", "\"\""));
    }
    else return field;
}

Adding the Media Formatter

To add a media type formatter to the Web API pipeline, use the Formatters property on the HttpConfigurationobject.

public static void ConfigureApis(HttpConfiguration config)
{
    config.Formatters.Add(new ProductCsvFormatter()); 
}

For ASP.NET hosting, add this function to the Global.asax file and call it from the Application_Start method.

protected void Application_Start()
{
    ConfigureApis(GlobalConfiguration.Configuration);

    // ...
}

Now if a client specifies “text/csv” in the Accept header, the server will return the data in CSV format.

The following example uses HttpClient to get the CSV data and write it to a file:

HttpClient client = new HttpClient();

// Add the Accept header
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/csv"));

// Get the result and write it to a file.
// (Port 9000 is just an example port number.)
string result = client.GetStringAsync("http://localhost:9000/api/product/").Result;
System.IO.File.WriteAllText("products.csv", result);

End Mike Wasson's original article:

.net web api 2 process json & XML simultaneously

.net web api 2 .  This is an extension to Mike Wasson’s excellent post “Getting Started with ASP.NET Web API 2 (C#) ”  to illustrate processing with XML as well as JSON .   My changes are purely in the index.html, to add

  • a div called XMLProducts
  • a button to invoke findXML()
  • findXML() function
  • SuccessXML() function
  • SuccessXMLList() function
  • errorXML() function
  • additional code to document .ready  to call $.ajax function

Mikes Original post : Getting Started with ASP.NET Web API 2 (C#)

  By         Mike Wasson|January 21, 2012

HTTP is not just for serving up web pages. It is also a powerful platform for     building APIs that expose services and data. HTTP is simple, flexible, and ubiquitous. Almost any platform     that you can think of has an HTTP library, so HTTP services can reach a broad     range of clients, including browsers, mobile devices, and traditional desktop     applications.

ASP.NET Web API is a framework for building web APIs on top of the .NET Framework. In this tutorial, you will use ASP.NET Web API to create a web API that returns a list of products. The front-end web page uses jQuery to display the results.

Download the completed project.

Requirements

This tutorial uses Visual Studio 2013.

Create a Web API Project

Start Visual Studio and select New Project from the     Start page. Or, from the File menu, select     New and then Project.

In the Templates pane, select Installed Templates         and expand the Visual C# node. Under Visual C#,         select Web. In the list of project templates, select         ASP.NET Web Application. Name the project “ProductsApp” and         click OK.

In the New ASP.NET Project dialog, select the Empty template. Under “Add folders and core references for”, check Web API. Click OK.

You can also create a Web API project using the “Web API” template. The Web API template uses ASP.NET MVC to provide API help pages. I’m using the Empty template for this tutorial because I want to show Web API without MVC. In general, you don’t need to know ASP.NET MVC to use Web API.

Adding a Model

A model is an object that represents the data in your application.                 ASP.NET Web API can automatically serialize your model to JSON, XML, or some                 other format, and then write the serialized data into the body of the HTTP                 response message. As long as a client can read the serialization format, it can                 deserialize the object. Most clients can parse either XML or JSON. Moreover, the                 client can indicate which format it wants by setting the Accept header in the                 HTTP request message.

Let’s start by creating a simple model that represents a product.

If Solution Explorer is not already visible, click the View menu                 and select Solution Explorer. In Solution Explorer, right-click                 the Models folder. From the context menu, select Add                 then select Class.

Name the class “Product”. Add the following properties to the                     Product class.

namespace ProductsApp.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }
}

                Adding a Controller

In Web API, a controller is an object that handles HTTP requests. We’ll add a controller that can return either a list of products or a single product specified by ID.

Note  If you have used ASP.NET MVC, you are already                 familiar with controllers. Web API controllers are similar to MVC controllers, but inherit the ApiController class instead of                the                Controller class.

In Solution Explorer, right-click the the Controllers folder. Select Add     and then select Controller.

In the Add Scaffold dialog, select Web API Controller – Empty. Click Add.

In the Add Controller dialog, name the controller “ProductsController”. Click Add.

The scaffolding creates a file named ProductsController.cs in the Controllers folder.

You don’t need to put your contollers into                 a folder named Controllers. The folder name is just a                 convenient way to organize your source files.

If this file is not open already, double-click the file to open it. Replace the code in this file with the following:

using ProductsApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web.Http;

namespace ProductsApp.Controllers
{
    public class ProductsController : ApiController
    {
        Product[] products = new Product[] 
        { 
            new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 }, 
            new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M }, 
            new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M } 
        };

        public IEnumerable<Product> GetAllProducts()
        {
            return products;
        }

        public IHttpActionResult GetProduct(int id)
        {
            var product = products.FirstOrDefault((p) => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }
            return Ok(product);
        }
}

To keep the example simple, products are stored in a fixed array inside the     controller class. Of course, in a real application, you would query a database     or use some other external data source.

The controller defines two methods that return products:

  • The GetAllProducts method returns the entire list of products as an     IEnumerable<Product> type.
  • The  GetProductById method looks up a single product by its                     ID.

That’s it! You have a working web API.  Each method on the controller corresponds to one or more URIs:

Controller Method URI
GetAllProducts /api/products
GetProductById /api/products/id

For the GetProductByID method, the id in the URI is a placeholder. For example, to get the product with ID of 5, the URI is api/products/5.

For more information about how Web API routes HTTP requests to controller methods, see Routing in ASP.NET Web API.

Calling the Web API with Javascript and jQuery

In this section, we’ll add an HTML page that uses AJAX to call the web API. We’ll use jQuery to make the AJAX calls and also to update the page with the results.

In Solution Explorer, right-click the project and select Add, then select New Item.

In the Add New Item dialog, select the Web node under Visual C#, and then select the HTML Page item. Name the page “index.html”.

Replace everything in this file with the following:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>Product App</title>
</head>
<body>

  <div>
    <h2>All Products</h2>
    <ul id="products" />
  </div>
  <div>
    <h2>Search by ID</h2>
    <input type="text" id="prodId" size="5" />
    <input type="button" value="Search" onclick="find();" />
    <p id="product" />
  </div>

  <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js"></script>
  <script>
    var uri = 'api/products';

    $(document).ready(function () {
      // Send an AJAX request
      $.getJSON(uri)
          .done(function (data) {
            // On success, 'data' contains a list of products.
            $.each(data, function (key, item) {
              // Add a list item for the product.
              $('<li>', { text: formatItem(item) }).appendTo($('#products'));
            });
          });
    });

    function formatItem(item) {
      return item.Name + ': $' + item.Price;
    }

    function find() {
      var id = $('#prodId').val();
      $.getJSON(uri + '/' + id)
          .done(function (data) {
            $('#product').text(formatItem(data));
          })
          .fail(function (jqXHR, textStatus, err) {
            $('#product').text('Error: ' + err);
          });
    }
  </script>
</body>
</html>

There are several ways to get jQuery. In this example, I used the Microsoft Ajax CDN. You can also download it from http://jquery.com/, and the ASP.NET “Web API” project template includes jQuery as well.

Getting a List of Products

To get a list of products, send an HTTP GET request to “/api/products”.

The jQuery getJSON function sends an AJAX request. For response contains array of JSON objects. The done function specifies a callback that is called if the request succeeds. In the callback, we update the DOM with the product information.

$(document).ready(function () {
    // Send an AJAX request
    $.getJSON(apiUrl)
        .done(function (data) {
            // On success, 'data' contains a list of products.
            $.each(data, function (key, item) {
                // Add a list item for the product.
                $('<li>', { text: formatItem(item) }).appendTo($('#products'));
            });
        });
});

Getting a Product By ID

To get a product by ID, send an HTTP GET  request to “/api/products/id“, where id is the product ID.

function find() {
    var id = $('#prodId').val();
    $.getJSON(apiUrl + '/' + id)
        .done(function (data) {
            $('#product').text(formatItem(data));
        })
        .fail(function (jqXHR, textStatus, err) {
            $('#product').text('Error: ' + err);
        });
}

We still call getJSON to send the AJAX request, but this time we put the ID in the request URI. The response from this request is a JSON representation of a single product.

Running the Application

Press F5 to start debugging the application. The web page should look like the following:

To get a product by ID, enter the ID and click Search:

If you enter an invalid ID, the server returns an HTTP error:

                Using F12 to View the HTTP Request and Response

When you are working with an HTTP service, it can be very useful to see the HTTP                 request and request messages. You can do this by using the F12 developer tools                 in Internet Explorer 9. From Internet Explorer 9, press F12 to                 open the tools. Click the Network tab and press Start                 Capturing. Now go back to the web page and press F5 to                 reload the web page. Internet Explorer will capture the HTTP traffic between the                 browser and the web server. The summary view shows all the network traffic for a                 page:

Locate the entry for the relative URI “api/products/”. Select this entry and                 click Go to detailed view. In the detail view, there are tabs                 to view the request and response headers and bodies. For example, if you click                 the Request headers tab, you can see that the client requested                 “application/json” in the                 Accept header.

My Addition   to index .html  Code to render the list & find code as a XML too

<! DOCTYPEhtml>

<htmlxmlns=”http://www.w3.org/1999/xhtml”&gt;

<head>

<title>Product App</title>

</head>

<body>

<div>

<h2>All Products</h2>

<ulid=”products”/>

<ulid=”XMLproducts”/>

</div>

<div>

<h2>Search by ID</h2>

<inputtype=”text”id=”prodId”size=”5″/>

<inputtype=”button”value=”Search”onclick=”find();/>

<inputtype=”button”value=”Search XML”onclick=”findXML();/>

<pid=”product”/>

</div>

<scriptsrc=”http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js”></script>

<script>

var uri = ‘api/products’;

$(document).ready(

function () {

// Send an AJAX request

$.getJSON(uri)

.done(

function (data) {

// On success, ‘data’ contains a list of products.

$.each(data,

function (key, item) {

// Add a list item for the product.

$(

‘<li>’, { text: formatItem(item) }).appendTo($(‘#products’));

});

});

//   Additional code for XML Processing

$.ajax({

type:“GET”,

url:“api/products,

dataType:“xml”,

success: function (data) {successXMLList(data);},

error:function (data) {errorXML(data);}

});

});

function formatItem(item) {

return item.Name + ‘: $’ + item.Price;

}

function find() {

var id = $(‘#prodId’).val();

$.getJSON(uri +

‘/’ + id)

.done(

function (data) {

$(

‘#product’).text(formatItem(data));

})

.fail(

function (jqXHR, textStatus, err) {

$(

‘#product’).text(‘Error: ‘ + err);

});

}

function findXML() {

var id = $(‘#prodId’).val();

$.ajax({

type:“GET”,

url:“api/products/”+id,

dataType:“xml”,

success:function (data) {successXML(data);},

error:function (data) {errorXML(data);}

});

}

function successXMLList(data) {

var Name = “”;

var Category = “”;

var Price = “”;

$(data).find(

‘Product’).each(function () {

Name = $(this).find(‘Name’).text();

Category = $(this).find(‘Category’).text();

Price = $(this).find(‘Price’).text();

$(‘<li>’, { text: Name +“-“+Category+“-“+Price }).appendTo($(‘#XMLproducts’));

});

}

function successXML(data) {

var Name = “”;

var Category = “”;

var Price = “”;

$(data).find(

‘Product’).each(function () {

Name = $(this).find(‘Name’).text();

Category = $(this).find(‘Category’).text();

Price = $(this).find(‘Price’).text();

});

$(‘#product’).text(Name + ” (“ + Category + “) “ + Price);

}

function errorXML(data)

{

var msg = data.statusText

$(

‘#product’).text(‘Error: ‘ + msg);

}

</script>

</body>

</html>

Follow

Get every new post delivered to your Inbox.