0
\$\begingroup\$

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.

\$\endgroup\$
1
\$\begingroup\$

Just a couple suggestions...

It might be easier to setup sub-folders (dynamically) under Resources for each template that gets uploaded to store the associated files in. Then just tweak the links to point to that sub-folder. This way you don't have to serve up files manually and everything works as you expect with caching and compression and whatnot. Basically it's a static file on your server at that point.

For the parameters, I would use a single JSON object and return that to the client for consumption instead of setting up multiple header scripts like that. Then you have "templateModel.p1, templateModel.p2..." etc.

Also, if you haven't you should learn a client-side framework like Angular... this problem sounds like one that could leverage it to make rendering and binding and link adjustment MUCH simpler and more dynamic.

Lastly, performance for 30 users shouldn't be a concern. Unless the server that's running this is running 100 other things that are being pounded way harder I wouldn't worry. Fix performance later if it becomes an issue. And narrow the problem down to the exact choke point(s).

\$\endgroup\$
  • \$\begingroup\$ Thanks - as for the first suggestion - it sort of seems like I'd have to provide a full path to the actual folder on the server for this to work? So basically something like C:\WebApp\resources\Template1\css\custom.css? \$\endgroup\$ – Bartosz Sep 12 '18 at 21:12
  • \$\begingroup\$ If you have a site with a /resources relative path, and then something like /resources/images/logo.jpg or whatever, then it should be as simple as adding the folders (dynamically in code) on the server side when a template is first added. You would then reference them on your html page (template page) by referencing /resources/templateid/someImage.jpg or someCss.css or js or whatever. Does that help? \$\endgroup\$ – Chad Sep 12 '18 at 22:16
  • \$\begingroup\$ As far as creating the folders in code, you would use the server path, correct. So HostingEnvironment.MapPath("~/Resources") would be the root where you add all the templates, or perhaps resources/templates/id, to distinguish it from your other "core" static files. \$\endgroup\$ – Chad Sep 12 '18 at 22:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.