I need advise whether the approach I took is the optimal one (not sure whether this is the right 'stackExchange' site).
I have an MVC application, which is supposed to be able to render an HTML page uploaded by a user of the app (this is a 'preview your data' feature). These HTML files are like templates into which I inject some (XML) data from the MVC app.
The input uploaded by the user is a zip package which contains HTML files, CSS files, images, some javascript etc. This is stored in the 'Resources' repository in my application. The input package is uploaded from time to time (rarely) by a single user, but it will be 'launched for data preview' by multiple users multiple times a day.
Aspect 1:
When the page is supposed to be generated, it hits the method below:
public ActionResult Generate(int previewResourceId)
{
var provider = new TemplateProvider(this.settings, previewResourceId);
//finds the proper uploaded resource in repository
string originalHtml = provider.ProvideTemplateHtml(deployer);
//returns the content of the index.html
string updatedHtml = this.Engine.ProvidePreviewPage(originalHtml);
//injects the data into the page
return this.Content(updatedHtml, "text/html", Encoding.UTF8);
}
When the zip template is uploaded by the user, it is extracted to a folder. When the page is generated, the some XML data is injected to the content of the index.html and it is rendered.
Question 1:
Is it fine to return this.Content() with a manipulated html string, or are there better approaches?
Aspect 2:
Now, the user's page will have references to the images, css, and scripts provided as relative paths, e.g. <script src=\"js/events.js\"></script>
. Since I am not 'hosting' the page, and just rendering it, the relative paths will not work - therefore I have created an API endpoint which provides a referenced content.
When the users page template zip is uploaded, upon extracting I do some manipulation of the links and simply replace the srcs and hrefs with something like this:
<script src=\"/MyPage/GetReferencedContent?rid=69&fn=js/events.js\"></script>
The action is as follows:
public ActionResult GetReferencedContent(int rid, string fn)
{
TemplateProvider provider = new TemplateProvider(this.settings, templateResourceId);
string filePath = Path.Combine(provider.GetTemplateRootFolder().FullName, referencedFileName);
if (!System.IO.File.Exists(filePath))
{
return this.HttpNotFound($"Resource not found on the server: {fn}");
//bonus question - is the HttpNotFound the proper error in this case, or should it be BadRequest?
}
return new FileStreamResult(fileStream: System.IO.File.Open(filePath, FileMode.Open), contentType: MimeMapping.GetMimeMapping(filePath));
}
Question 2:
Is it fine to return the referenced files like that? It will result in tens or even hundreds of Get requests for each time the page is rendered, and it will each time read a file from disk, I am a bit concerned with performance...
The page will be accessed by probably not more than 30 users a day, and I expect that 'at peak hours' it will be rendered once every two-three minutes. It's an internal app with a very limited number of users.
Aspect 3:
Since the user's page is not really 'hosted', but simply rendered, I don't see any better way to pass parameters&data to it, other than injecting them to the index.html page prior to rendering.
So, what I am currently doing is I am injecting xml strings into the 'head' section of the index.html as 'script' elements with a certain IDs, like so:
<head>
<script id="productId" type="text/plain">AaaaCh0o0!</script>
<script id="pageData" type="text/plain">[500kb - 1mb long xml string]</script>
And, wherever the users page needs data, their page is expected to call a javascript function loadPageData()
. I inject the body of that function when the template is first uploaded, so that it looks as follows:
function loadPageData() {
return $('#pageData').text();
}
Question 3: Is there any better way of passing the parameters? And is there any significant overhead in injecting large amount of data string to the page header?
It seems to work all OK, I am just concerned about performance (as I said, it is an internal app for limited number of users) and whether there is simpler way. Any feedback to the design greatly appreciated.