A blog about my life, development and projects

Preparing for a .Net Web Api interview

This post follow on one I did earlier this year named "Great Developers, Interviews and Projects".
Yesterday an old colleague of mine sent me a WhatsApp message just before going into and interview for a position relating to Api development specifically on .Net Web Api and asked me what he should know and how to prepare.

Normally Web Api or any api development isn't a position on it's own and generally forms part of a general .Net web developer role, but I guess it can happen that someone is just looking for an Api developer.

My response to this question was to look at the following links as a guideline on questions that could be asked during such an interview:

There are many more sites out there that could help you prepare not only for an Api related interview but anything from Mvc to general .Net development, they are just a Google/Bing search away.

Most important thing I do during and interview is to try and find out how much you know, and if you just remembered answers to questions. I normally ask more than what I interview for, such as design principles in .Net, clean code principles, database questions. General syntax questions of c#. The most difficult thing to do in an interview is to determine how much someone knows and if it's just theory they parrot back.

During an interview it's good to give someone a test, and I think in general this should be expected in any technical interview. The test can either be a physical test where you could expect to write code, or even write code on a white board or paper without the use of internet or visual studio, I prefer the latter, because it shows me that when you are in front of clients pressed to come up with a design, you have the knowledge to demonstrate your plan and structure your thoughts.

In the previous post I mentioned that if you have code to show, or actively contributing to an open source project or even your own personal GitHub project, to me at least, this counts a lot, because you have something tangible to show and in some cases can be more worth than a test during an interview.

The main point is to be well prepared for any interview. Go over the theory relating to the subject matter and ensure you can apply it. Don't stress, it's OK to say you don't know, or you need time to think about it.

Upload multiple files with progress using JavaScript and .Net MVC

Over the past few years web technologies have progressed significantly. With the introduction of HTM5 and ECMAScript 5/6 (JavaScript) we have seen quite a few interesting new features being introduces such as web workers, web sockets, XMLHttpRequest2, Geolocation and the file api.
Todays post is primarily regarding XMLHttpRequest2 and the file api.

I have spent quite a bit of time trying to find good examples of how to implement asynchronous file uploads in javascript and being able to accept it using C# and MVC.
There are numerous examples explaining how to achieve the client side uploads using the file api and then making a XMLHttpRequest, but there are very few examples on how to properly receive those files on the MVC controller side.

One of the best examples of async file uploads is the Blueimp file upload control: https://github.com/blueimp/jQuery-File-Upload

Even though this is a wonderful control with a lot of features, I needed something that is very light weight, reusable and that can integrate into any .Net C# project without having to create a custom handler, it must use any MVC controller action.
Almost all examples for MVC involves create a custom handler, and to me this just feels wrong.

One thing that I also needed was for the control to be a able to provide me with the form variable that you normally would have with a form post.
The control should also not only be async but actually have a form post as a fallback for older browser.
I also needed the ability to dynamically set the allowed file types whenever I use the control.

With the criteria above in mind I set out to build just that and chose to create a Knockout.js model. Yes, knockout is not dead, and for something like this it’s perfect because it gives you model binding while being able to implement it on a single view without major scaffolding. I also used bootstrap for the styling and drop container as it comes with the standard MVC template and it has a progress bar control.

The full solution can be downloaded from GitHub: https://github.com/TechnoDezi/MVCMultipleFileUpload

Step 1 – Client side code

The first part of this control is the Knockout model, this will handle the drag & drop events, as well as all the upload progress events inside a single model that can be bound to any html control. The model is implemented as a single javascript file in order to keep the size down and to share it is also easier. The single model will also take care of tracking multiple files at once and be able to receive feedback after it’s uploaded.

function FileUploadViewModel(uploadUrl, dropBoxID, defaultFileImg, supportedExtentions, dataHeaderObj) {
var self = this;
self.uploadUrl = uploadUrl;
self.dropBoxID = dropBoxID;
self.defaultFileImg = defaultFileImg;
self.supportedExtentions = supportedExtentions;
self.dataHeaderObj = dataHeaderObj;
self.showResults = ko.observable(false);
self.showFileSelect = ko.observable(true);
self.showSubmit = ko.observable(true);
self.fileObj = function (fileName, fileSize, uploadPercentage, messages, showMessages) {
this.fileName = fileName;
this.fileSize = fileSize;
this.imgSrc = ko.observable("");
this.uploadPercentage = ko.observable(uploadPercentage);
this.messages = ko.observable(messages);
this.showMessages = ko.observable(showMessages);
}
self.fileList = ko.observableArray([]);
self.init = function () {
// Check if FileAPI and XmlHttpRequest.upload are supported, so that we can hide the old style input method
if (window.File && window.FileReader && window.FileList && window.Blob && new XMLHttpRequest().upload) {
self.showFileSelect(false);
self.showSubmit(false);
var dropbox = document.getElementById(self.dropBoxID);
// init event handlers
dropbox.addEventListener("dragenter", self.dragEnter, false);
dropbox.addEventListener("dragexit", self.dragExit, false);
dropbox.addEventListener("dragleave", self.dragExit, false);
dropbox.addEventListener("dragover", self.dragOver, false);
dropbox.addEventListener("drop", self.drop, false);
}
}
self.dragEnter = function (evt) {
evt.stopPropagation();
evt.preventDefault();
}
self.dragExit = function (evt) {
evt.stopPropagation();
evt.preventDefault();
$("#" + self.dropBoxID).removeClass("active-dropzone");
}
self.dragOver = function (evt) {
evt.stopPropagation();
evt.preventDefault();
$("#" + self.dropBoxID).addClass("active-dropzone");
}
self.drop = function (evt) {
evt.stopPropagation();
evt.preventDefault();
$("#" + self.dropBoxID).removeClass("active-dropzone");
var files = evt.dataTransfer.files;
var count = files.length;
self.fileList.removeAll();
// Only call the handler if a file was dropped
if (count > 0) {
self.showResults(true);
self.handleFiles(files);
}
else {
self.showResults(false);
}
}
self.handleFiles = function (files) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
var re = /(?:\.([^.]+))?$/;
var extention = re.exec(file.name)[1];
var fileName = file.name;
if (fileName.length > 100) {
fileName = fileName.substring(0, 100);
fileName = fileName + "...";
}
var size = file.size / 1024;
size = Math.round(size * Math.pow(10, 2)) / Math.pow(10, 2);
var fileModel = new self.fileObj(fileName, size + "Kb", "0%", "", false);
self.fileList.push(fileModel);
if ($.inArray(extention, self.supportedExtentions) > -1) {
self.HandleFilePreview(file, fileModel);
this.UploadFile(file, fileModel);
}
else {
var message = "File type not valid for file " + file.name + ".";
fileModel.messages(message);
fileModel.showMessages(true);
}
}
}
self.HandleFilePreview = function (file, fileModel) {
if (file.type.match('^image/')) {
var reader = new FileReader();
// init the reader event handlers
reader.onloadend = function (evt) {
fileModel.imgSrc(evt.target.result);
};
// begin the read operation
reader.readAsDataURL(file);
}
else {
fileModel.imgSrc(self.defaultFileImg);
}
}
self.UploadFile = function (file, fileModel) {
fileModel.uploadPercentage("0%");
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", function (evt) {
if (evt.lengthComputable) {
var percentageUploaded = parseInt(100 - (evt.loaded / evt.total * 100));
fileModel.uploadPercentage(percentageUploaded + "%");
}
}, false);
// File uploaded
xhr.addEventListener("load", function () {
fileModel.uploadPercentage("100%");
}, false);
// file received/failed
xhr.onreadystatechange = function (e) {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
console.log(xhr);
fileModel.messages(xhr.responseText);
fileModel.showMessages(true);
}
}
};
xhr.open("POST", self.uploadUrl, true);
// Set appropriate headers
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.setRequestHeader("X-File-Name", file.name);
xhr.setRequestHeader("X-File-Size", file.size);
xhr.setRequestHeader("X-File-Type", file.type);
if (self.dataHeaderObj != null && self.dataHeaderObj != "")
{
xhr.setRequestHeader("X-File-Data", self.dataHeaderObj);
}
// Send the file
xhr.send(file);
}
//Load View Model
self.init();
}

Step 2 – The html and model binding

On the html side the model can be bound to any html allowing you to style the upload exactly as you wish. When binding the Knockout model to the html you can also serialize you MVC model to a json object and send it along with every upload.
 
@{
ViewBag.Title = "Upload File";
}
@section scripts
{
<script src="~/Scripts/knockout-3.4.0.js"></script>
<script src="~/Scripts/FileUploadViewModel.js"></script>
<script type="text/javascript">
$(function () {
//var data = @(Html.Raw(Json.Encode(this.Model)));
//var jsonModel = JSON.stringify(data);
var jsonModel = null;
ko.applyBindings(new FileUploadViewModel(
"@Url.Action("UploadFilePost", "Home")",
"dropbox",
"@Url.Content("~/Content/images/file_type_icons_flat_-13.png")",
["jpg", "png"],
jsonModel), document.getElementById("fileUploadContainer"));
});
</script>
}
<div id="fileUploadContainer">
@using (Html.BeginForm("UploadFilePost", "Home", FormMethod.Post, new { @class = "form-horizontal", role = "form", enctype = "multipart/form-data" }))
{
<br />
<div id="dropbox" dropzone="copy f:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet f:application/vnd.ms-excel" class="hero-unit">
<h2 id="droplabel">Drop zone - Drag & Drop your files here</h2>
<p id="dnd-notes">Only jpg and png file types are supported. Once you drop your files in the dropzone, the upload will start.</p>
<input data-bind="visible: showFileSelect" id="fileSelect" type="file" name="fileSelect" />
<p><button data-bind="visible: showSubmit" type="submit" class="btn btn-primary btn-large">Upload</button></p>
</div>
<table class="table table-striped" data-bind="visible: showResults">
<thead>
<tr>
<th></th>
<th>File name</th>
<th>File size</th>
<th>Upload Progress</th>
<th>Message</th>
</tr>
</thead>
<tbody data-bind="foreach: fileList">
<tr>
<td>
<img style="max-height: 80px" data-bind="attr: { src: imgSrc }" alt="preview will display here" />
</td>
<td data-bind="text: fileName"></td>
<td data-bind="text: fileSize"></td>
<td>
<div class="progress progress-info progress-striped">
<div class="progress-bar" data-bind="style: { width: uploadPercentage }"></div>
</div>
</td>
<td>
<div data-bind="visible: showMessages, html: messages">
</div>
</td>
</tr>
</tbody>
</table>
}
</div>

Step 3 – Server side MVC controller

On the server side a couple of things is to note. Firstly there is an upload helper class that can retrieve the file content from the request for either an async post or a normal form post. This helper will build up a model that contains the file content, the file name, size as well as the json model or form fields serialized to json string that can be easily desterilized back into a C# model class. Once the file is retrieved it can be saved to disc, opened, or pushed to a database.

 
Controller:
[HttpPost]
public async Task<string> UploadFilePost()
{
FileUploadHelper handler = new FileUploadHelper();
//Retriev the file from the request
UploadedFile fileObj = handler.GetFileFromRequest(this.Request);
//Write file to disc
System.IO.File.WriteAllBytes(Path.Combine(Server.MapPath("~/App_Data/"), Guid.NewGuid() + Path.GetExtension(fileObj.Filename)), fileObj.Contents);
return "File uploaded";
}
 
File upload handler:
using MVCMultipleFileUpload.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MVCMultipleFileUpload.Helpers
{
public class FileUploadHelper
{
public UploadedFile GetFileFromRequest(HttpRequestBase Request)
{
string filename = null;
string fileType = null;
string fileData = null;
byte[] fileContents = null;
if (Request.Files.Count > 0)
{ //we are uploading the old way
var file = Request.Files[0];
fileContents = new byte[file.ContentLength];
file.InputStream.Read(fileContents, 0, file.ContentLength);
fileType = file.ContentType;
filename = file.FileName;
IDictionary<string, object> dict = new Dictionary<string, object>();
foreach (string key in Request.Form.Keys)
{
dict.Add(key, Request.Form.GetValues(key).FirstOrDefault());
}
dynamic dobj = dict.ToExpando();
fileData = Newtonsoft.Json.JsonConvert.SerializeObject(dobj);
}
else if (Request.ContentLength > 0)
{
// Using FileAPI the content is in Request.InputStream!!!!
fileContents = new byte[Request.ContentLength];
Request.InputStream.Read(fileContents, 0, Request.ContentLength);
filename = Request.Headers["X-File-Name"];
fileType = Request.Headers["X-File-Type"];
fileData = Request.Headers["X-File-Data"];
}
return new UploadedFile()
{
Filename = filename,
ContentType = fileType,
FileSize = fileContents != null ? fileContents.Length : 0,
Contents = fileContents,
FileData = fileData
};
}
}
}
 
Model class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MVCMultipleFileUpload.Models
{
public class UploadedFile
{
public int FileSize { get; set; }
public string Filename { get; set; }
public string ContentType { get; set; }
public byte[] Contents { get; set; }
public string FileData { get; set; }
}
}
 

Give credit where credit is due: Many thanks to Valerio Gheri who gave me a running start: https://github.com/vgheri/HTML5Drag-DropExample

Azure media services–converting videos for uploading

This post is about preparing videos for uploading to Azure media service and not so much about media services itself.

Since December I have been working on a solution that involves Azure media services. To my surprise it’s actually a lot easier to work with than I imagined, the biggest thing for me was to wrap my head around the different combinations and how tokens work with DRM content.

For Azure media services I mainly followed a code example created by Mingfei https://github.com/AzureMediaServicesSamples/Dynamic-Encryption-with-PlayReady. There are a lot of documentation regarding Azure media services so I’m not going to go in depth on it.

A big thing for me and also something I found very little info on was how to prepare my videos for upload. Azure media services accepts a lot of different file formats when uploading a video, but none supported the files I had.

The videos I was going to receive would have been in a RAW uncompressed AVI file format. These files are way too big to upload directly to Azure, and on my 4Mbps ADSL line it will take forever. Sure I can just use a video converter and batch convert them all to .MP4, but I need more control over the file conversion and be able to upload the file as soon as it’s converted along with some meta data to my own database.

After a thorough search I cam across a .Net wrapper for the FFMPEG library created by NReco http://www.nrecosite.com/video_converter_net.aspx
This library had everything I needed to start an application that can convert my videos and upload them to Azure as well as load the assets in my own database.

I wrote a little tool that looks something like this:

This allows me to load a folder full of raw files, add names/descriptions and tags for the files so that I can find them later on.

When you convert and upload the tool will convert the video and add it to the upload queue. While uploading a file it will start converting the next file in line and so on. Multi threading the convert/upload process saves a lot of time. If I had to 1st wait for a tool to convert all the files and upload them to Azure it would have been very painful.

To encode your own video files using NReco you can use the following code:

FFMpegConverter ffMpeg = new FFMpegConverter();

ffMpeg.ConvertProgress += new EventHandler<ConvertProgressEventArgs>(delegate (Object o, ConvertProgressEventArgs eP)
{
    double percentage = ((eP.Processed.TotalMilliseconds / eP.TotalDuration.TotalMilliseconds) * 100);
});

//Start conversion
ffMpeg.ConvertMedia(SourcePath, null, DestinationPath, Format.mp4, new ConvertSettings()
{
    AudioCodec = "libmp3lame",
    VideoCodec = "h264",
    VideoFrameSize = "1280x720"
});

After FFMpeg have finished converting the video to MP4, the file can then be uploaded to Azure media services as per the example by Mingfei.

Error when installing a Windows Service using InstallUtil

During the last week I was asked to write a windows service, something I have done quite a few times in my career, but this time when I tried to install my service on a test PC it broke with the error “system.invalidoperationexception: unable to get installer types”. The funning thing is that it is working on a test server.

The solution to this problem turned out to be quite simple:

When running InstallUtil.exe it has to be run from the .Net folder matching your machines setup, for instance if you have a 64Bit machine you have to run InstallUtil.exe from the 64Bit .Net folder “C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe” and for a 32Bit machine you can use the normal .Net folder “C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe”

The reason this was working on a test server was because the test server is running on a 32Bit virtual machine. 
Normally when I create windows services they go directly to a dev/test environment, I usually don’t bother running them on my local machine or a test PC.

Hope that this post help’s someone in the future Smile

Web API Help Pages

Follow my previous post about Web API here is a little bit more on Help Pages and an interesting thing that isn’t mentioned anywhere.

For a complete guide on how to enable help pages you can read the post on: http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/creating-api-help-pages

One thing that I have noticed with Web API is that when you are using HttpResponseMessage as your return type there is no documntation generated for it.
In order to get the documentation you will have to add an attribute that tells web API what type of model it can expect as part of your return type.

Just add the attribute: [ResponseType(typeof(Your_Class))] and your documentation will now reflect the return type sent along with your Http Response.