I will have this code reviewed by back-end devs tomorrow as part of an interview process tomorrow and would like comments on how to improve A) structure B) performance. For structure, it's too late to switch from the "get all" approach as this would involve rewriting the entire front end. Mainly I am wondering if I should move a bunch of the logic into a data service project and if so, do I build a console app to host it or how would that work? Also open to removing the custom routes and letting the HTTP verbs handle what they're supposed to if it's important and doable in a short time.
public class FileSystemController : ApiController
{
private int _currentId;
private readonly string _folderName = ConfigurationManager.AppSettings["FileSystemFolder"];
private readonly string _basePath = HttpContext.Current.Server.MapPath(@"~\" + ConfigurationManager.AppSettings["FileSystemFolder"]);
// GET: api/FileSystem
public IEnumerable<FileSystemNode> Get()
{
List<FileSystemNode> nodes = new List<FileSystemNode>();
var baseNode = AddFolderContents(_basePath, _folderName, _folderName, null);
nodes.Add(baseNode);
return nodes.ToArray();
}
private FileSystemNode AddFolderContents(string serverPath, string publicPath, string name, int? parentId)
{
var node = new FileSystemNode
{
Id = _currentId,
ParentId = parentId,
Name = name,
Path = publicPath,
Files = new List<FileObject>(),
Children = new List<FileSystemNode>()
};
_currentId++;
DirectoryInfo dInfo = new DirectoryInfo(serverPath);
foreach (FileInfo file in dInfo.GetFiles())
{
node.Files.Add(new FileObject
{
Name = file.Name,
Size = String.Format("{0:n0}", file.Length),
ParentId = node.Id,
Path = publicPath
});
}
foreach (DirectoryInfo folder in dInfo.GetDirectories())
{
var childServerPath = serverPath + @"\" + folder.Name;
node.Children.Add(AddFolderContents(childServerPath, publicPath + @"\" + folder.Name, folder.Name, node.Id));
}
return node;
}
[Route("api/FileSystem/Upload")]
[HttpPost]
public string Upload()
{
try
{
var request = HttpContext.Current.Request;
var fileName = request.Headers["filename"];
var filePath = request.Headers["path"];
var fullPath = HttpContext.Current.Server.MapPath(@"~\" + filePath + @"\" + fileName);
using (var fs = new FileStream(fullPath, FileMode.Create))
{
request.InputStream.CopyTo(fs);
}
return "File " + fileName + " uploaded successfully";
}
catch (Exception e)
{
Console.WriteLine(e);
return "File upload failed";
}
}
[Route("api/FileSystem/Delete")]
[HttpPost]
public string Delete()
{
try
{
var request = HttpContext.Current.Request;
var fileName = request.Headers["filename"];
var filePath = request.Headers["path"];
var fullPath = HttpContext.Current.Server.MapPath(@"~\" + filePath + @"\" + fileName);
File.Delete(fullPath);
return "File " + fileName + " deleted successfully";
}
catch (Exception e)
{
Console.WriteLine(e);
return "File deletion failed";
}
}
}
Here are the front-end calls to upload and delete. I imagine I could just hit "api/FileSystem" URL with the proper verbs (POST and DELETE) and get the same results? (Note I've already had the front end review, so while this may not be the ideal way to do things, I'm really only asking about making it compliant with the API)
self.onUploadClicked = function() {
var fileInput = $('#uploadFileName');
if (fileInput[0].files[0]) {
var file = fileInput[0].files[0];
var xhr = new XMLHttpRequest();
xhr.open("POST", "/api/filesystem/Upload", false);
xhr.setRequestHeader("filename", file.name);
xhr.setRequestHeader("path", this.Path);
showSpinner('Uploading...');
xhr.send(file);
hideSpinner();
self.onRefreshClicked(xhr.responseText);
} else {
alert("Please select a file to upload");
}
};
self.onDeleteClicked = function() {
if (confirm('Are you sure you want to permanently delete ' + this.Name + '?')) {
var xhr = new XMLHttpRequest();
xhr.open("POST", "/api/filesystem/Delete", false);
xhr.setRequestHeader("filename", this.Name);
xhr.setRequestHeader("path", this.Path);
showSpinner('Deleting...');
xhr.send();
hideSpinner();
self.onRefreshClicked(xhr.responseText);
};
};