Returning Generated CSS from an MVC5 or Web API 2 Controller Action

Returning generated CSS from an MVC5 or Web API 2 controller action

You could use BundleTransformer to compile your LESS server side.

It can depend on how you want to serve the file. If you know all the tenants then just add add a bundle url for each tenant application to the bundle config.

 var themeStyles = new CustomStyleBundle("~bundles/theme/tenant").Include("~/Content/theme.less");
themeStyles.Builder = new ThemeBuilder();
BundleTable.Bundles.Add(themeStyles);

If you don't and the tenants are flexible as was the case in our situation then add the following controller action for your themes.

    [Route("bundles/theme/{id}")]
public ContentResult Theme(string id)
{
var tenantThemePath = string.Format("~/bundles/theme/{0}", id);

// Check that bundle has not already been added.
if (BundleTable.Bundles.All(x => x.Path != tenantThemePath))
{
var themeStyles = new CustomStyleBundle(tenantThemePath ).Include("~/Content/theme.less");

themeStyles.Builder = new ThemeBuilder();

BundleTable.Bundles.Add(themeStyles);
}

var context = new BundleContext(HttpContext, BundleTable.Bundles, institutionPath);

var response = BundleTable.Bundles.GetBundleFor(tenantThemePath).GenerateBundleResponse(context);

Response.Cache.SetCacheability(response.Cacheability);

return Content(response.Content, response.ContentType);
}

The ThemeBuilder implementation for BundleTransformer

public class ThemeBuilder : IBundleBuilder
{
public string BuildBundleContent(Bundle bundle, BundleContext context, IEnumerable<BundleFile> files)
{
var lessTranslator = bundle.Transforms.OfType<StyleTransformer>()
.Where(x => x != null)
.Select(x => x.Translators.OfType<LessTranslator>().FirstOrDefault())
.FirstOrDefault();

if (lessTranslator == null)
{
return string.Empty;
}

lessTranslator.GlobalVariables = GetThemeVariables();

return string.Empty;
}

private string GetThemeVariables()
{
// Simplified for brevity
// This will be translated to less variables by the BundleTransformer
// themeColour should correspond to a variable name in your less file.
return string.Format("themeColour={0}", themeColour);
}

}

You will need away of getting the theme colours out we stashed those variables in HttpContext stores so that we could pull them out using an extension method in the GetThemeVariables method.

I hope this helps.

UPDATE
I've expanded on my original answer and created a more reusable way of including themes.

Demo site here: http://bundletransformer-theme-builder.azurewebsites.net/

GitHub repo here: https://github.com/benembery/bundle-transformer-theme-builder

Dynamically generated Javascript, CSS in ASP.NET MVC

The best solution so far I found for that is the following:

Dynamic Javascript and CSS in ASP.NET MVC using Razor Views

You just create views: CurrentUserOverrides.css.cshtml, ContactViewModel.js.cshtml. This views will contain single HTML block (<script> or <style>), so IntelliSense works fine. Then you create controller that renders that view, trims the root tag and return content with appropriate content type.

Dynamic CSS for ASP.NET MVC?

I'd love to be able to run my CSS through Razor

What stops you?

public class CssViewResult : PartialViewResult
{
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.ContentType = "text/css";
base.ExecuteResult(context);
}
}

public class HomeController : Controller
{
public ActionResult Index()
{
return new CssViewResult();
}
}

and in ~/Views/Home/Index.cshtml:

@{
var color = "White";
if (DateTime.Now.Hour > 18 || DateTime.Now.Hour < 8)
{
color = "Black";
}
}
.foo {
color: @color;
}

Now all that's left is to include it:

<link href="@Url.Action("index")" rel="stylesheet" type="text/css" />

You can also make the template strongly typed to a view model, write loops, ifs, inclusions, ...

What is the standard or fast way of delivering dynamic stylesheets in ASP.NET / MVC5?

Your planned approach sounds fine. As far as your specific questions go:

  1. Is dotless the fastest way to compile less?

    "Fastest" is relative and debatable. The only way to know would be to run this and any available alternatives on your production machine using some sort of benchmarking. Even then, outside factors such as how much load the server is getting, how many requests are being handled simultaneously, etc. can affect those benchmarks. For example, maybe one solution is "faster" handling an ideal scenario, but has a large overhead that causes it to run much slower than another solution when the server is actually being taxed. Overall, it's impossible for anyone to give you any sort of definite answer to this, and really, it's probably too early to even be that concerned about the question. If it becomes a problem in production, then you can start investigating alternatives.

  2. How long will the databasecall + compiling take?

    Also completely impossible for anyone to give a definitive answer to. There's way too many variables involved in that relatively simple question. What database are you using? What version? What are the specs of the server it's running on. What else is running on that server? How have you configured the database server? What kind of query are you running? How many tables are involved? What's the size of the resultset being returned? What type of network infrastructure is in place? What's your latency? How capable is your network infrastructure at handling load? There's probably more questions I could ask if I though long enough and that's just about the database call portion. I don't expect answers to those questions; I'm merely trying to point out 1) there's no way anyone can answer that for you and 2) you're going to have to do a lot of research to come up with those answers yourself.

  3. Is this the way other application do this?

    This is highly speculative. First, it assumes this is somewhat common, when it's probably anything but. In my 20-some-odd years of doing web development, I've yet to encounter a scenario where I needed a dynamic stylesheet. Granted, for a large part of those years stylesheets didn't even exist or at least weren't heavily used yet and just because I haven't had a need doesn't mean there's not still a perfectly valid business-case for this. I understand the desire to want to find an accepted pattern or best practice to follow, but the sample set here is probably so small that no such thing exists. Trust your gut. Build things in a way that makes sense. Then test, refine and refactor. That's really the best advice I can give you.

  4. I'm looking if there's a way to cache the css request - maybe check if there has been a change since the last time the css was compiled.

    This one is pretty easy. Just make sure to set the appropriate response headers before returning your response. Expires is really your go-to here. If the stylesheet virtually never changes for the user, then you can set a far future Expires header and the client's browser should cache it requiring all this infrastructure to not have to do its thing again for a while. If the change-ability is variable (any time the user updates a setting, they need a new version, and this can happen at a whim), then you can still use a far-future Expires header and employ a cache-busting querystring param that will force the browser to get a fresh copy. A good choice might be adding the last modified date for the settings when rendering the link for the stylesheet. If the user hasn't modified anything, then the date won't change and the original cached version will be used. But if the date has changed, it will look like a new URL to the browser, and it will be fetched fresh.

Return HTML from ASP.NET Web API

ASP.NET Core. Approach 1

If your Controller extends ControllerBase or Controller you can use Content(...) method:

[HttpGet]
public ContentResult Index()
{
return base.Content("<div>Hello</div>", "text/html");
}

ASP.NET Core. Approach 2

If you choose not to extend from Controller classes, you can create new ContentResult:

[HttpGet]
public ContentResult Index()
{
return new ContentResult
{
ContentType = "text/html",
Content = "<div>Hello World</div>"
};
}

Legacy ASP.NET MVC Web API

Return string content with media type text/html:

public HttpResponseMessage Get()
{
var response = new HttpResponseMessage();
response.Content = new StringContent("<div>Hello World</div>");
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
return response;
}

HTML button calling an MVC Controller and Action method

No need to use a form at all unless you want to post to the action. An input button (not submit) will do the trick.

  <input type="button"
value="Go Somewhere Else"
onclick="location.href='<%: Url.Action("Action", "Controller") %>'" />

Difference between MVC 5 Project and Web Api Project

Basically, a Web API controller is an MVC controller, which uses HttpMessageResponse as the base type of its response, instead of ActionResponse. They are the same in most other respects. The main difference between the project types is that the MVC Application project type adds web specific things like default CSS, JavaScript files and other resources needed for a web site, which are not needed for an API.

MVC is used for creating web sites. In this case Controllers usually return a View (i.e. HTML response) to browser requests. Web APIs on the other hand are usually made to be consumed by other applications. If you want to allow other applications to access your data / functionality, you can create a Web API to facilitate this access. For example, Facebook has an API in order to allow App developers to access information about users using the App. Web APIs don't have to be for public consumption. You can also create an API to support your own applications. For example, we created a Web API to support the AJAX functionality of our MVC web site.

Microsoft changed the way they present the different templates. Now instead of using different templates for different project types, they encourage developers to mix ASP.NET technologies inside the same project as needed. Microsoft calls this vNext.

UPDATE: For ASP.NET Core, Web API has been integrated into the MVC 6 project type and the ApiController class is consolidated into the Controller class. Further details at: https://wildermuth.com/2016/05/10/Writing-API-Controllers-in-ASP-NET-MVC-6

How to return raw string with ApiController?

You could have your Web Api action return an HttpResponseMessage for which you have full control over the Content. In your case you might use a StringContent and specify the correct content type:

public HttpResponseMessage Get()
{
return new HttpResponseMessage()
{
Content = new StringContent(
"<strong>test</strong>",
Encoding.UTF8,
"text/html"
)
};
}

or

public IHttpActionResult Get()
{
return base.ResponseMessage(new HttpResponseMessage()
{
Content = new StringContent(
"<strong>test</strong>",
Encoding.UTF8,
"text/html"
)
});
}

Can an ASP.NET MVC controller return an Image?

Use the base controllers File method.

public ActionResult Image(string id)
{
var dir = Server.MapPath("/Images");
var path = Path.Combine(dir, id + ".jpg"); //validate the path for security or use other means to generate the path.
return base.File(path, "image/jpeg");
}

As a note, this seems to be fairly efficient. I did a test where I requested the image through the controller (http://localhost/MyController/Image/MyImage) and through the direct URL (http://localhost/Images/MyImage.jpg) and the results were:

  • MVC: 7.6 milliseconds per photo
  • Direct: 6.7 milliseconds per photo

Note: this is the average time of a request. The average was calculated by making thousands of requests on the local machine, so the totals should not include network latency or bandwidth issues.



Related Topics



Leave a reply



Submit