0 Comments

What is a Hotlinking?

Hotlinking is using images links directly from one site to another without permission from the owner of the website.

Hotlinking images have a side effect on your site, because it increases the server load and using your server bandwidth.

Prevent Hotlinking

Now let us use the power of ASP.NET Core 1.0 to protect our applications from hotlinking. We need sort of handler that listen to all incoming requests and checks whether the files are requested from our application or not, this may done easily with the middlewares, so we can plug a new middleware to perform those checks and integrate it with the other middlewares that our application may need in the same pipeline.

Let us build our middleware that named HotlinkingPreventionMiddleware as the following:

public class HotlinkingPreventionMiddleware
{
private readonly string _wwwrootFolder;
private readonly RequestDelegate _next;

public HotlinkingPreventionMiddleware(RequestDelegate next, IHostingEnvironment env)
{
_wwwrootFolder = env.WebRootPath;
_next = next;
}

public async Task Invoke(HttpContext context)
{
var applicationUrl = $"{context.Request.Scheme}://{context.Request.Host.Value}";
var headersDictionary = context.Request.Headers;
var urlReferrer = headersDictionary[HeaderNames.Referer].ToString();

if(!string.IsNullOrEmpty(urlReferrer) && !urlReferrer.StartsWith(applicationUrl))
{
var unauthorizedImagePath = Path.Combine(_wwwrootFolder,"Images/Unauthorized.png");

await context.Response.SendFileAsync(unauthorizedImagePath);
}

await _next(context);
}
}

The above middleware simply looking for the UrlReferrer to identify the origin of the request, while ASP.NET Core 1.0 doesn't have it directly in the Request object like what we have seen in the previous versions of ASP.NET, so we can get its value from the Headers object. So if the request is not belong to application host we can simply redirect the user to unauthorized page, but it our implementation above I sent unauthorized image, so the visitors of his/her page will notice that he/she steal or use use an image from other website without the owner permission.

Last but not least I wrote a very straightforward extension like what we have seen in almost ASP.NET repositories to allow us to use our middleware via IApplicationBuilder.

public static class BuilderExtensions
{
public static IApplicationBuilder UseHotlinkingPreventionMiddleware(this IApplicationBuilder app)
{
return app.UseMiddleware<HotlinkingPreventionMiddleware>();
}
}

Finally we can use it in the Configure method as the following:

app.UseHotlinkingPreventionMiddleware();
For more information you can play with the source code that is hosted in my GitHub repo.

0 Comments

I already wrote few blog posts about the localization in ASP.NET Core, it will be nice to use the localization with another ASP.NET Core components.

Today I'm gonna talk about localization & routing hand on hand, ASP.NET Core already have a middleware for routing the requests to application logic, for more details you can look to the routing repository.

As we know from my post about Localization Extensibility in ASP.NET Core 1.0 that localization is shipped with four request culture providers, and I remembered that I post an issue for support DomainRequestCultureProvider also there's another issue to Get segment from mapped route for CustomRequestCultureProvider with that I realize that there's a need for such RequestCultureProvider to get the culture information from the route data.

Almost of us know that we can easily get data from the routing whenever we have  HttpContext object, using the method GetRouteValue, so with that let us create our provider.

Easily we can take the source code for the QueryStringRequestCultureProvider and modify it little bit to accomplish our goal.

public class RouteDataRequestCultureProvider : RequestCultureProvider
{
public string RouteDataKey { get; set; } = "culture";

public string UIRouteDataKey { get; set; } = "ui-culture";

public override Task<ProviderCultureResult> DetermineProviderCultureResult(HttpContext httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}

var culture = null;
var uiCulture = null;

if (!string.IsNullOrWhiteSpace(RouteDataKey))
{
culture = httpContext.GetRouteValue(RouteDataKey);
}

if (!string.IsNullOrWhiteSpace(UIRouteDataKey))
{
uiCulture = httpContext.GetRouteValue(UIRouteDataKey);
}

if (culture == null && uiCulture == null)
{
return Task.FromResult((ProviderCultureResult)null);
}

if (culture != null && uiCulture == null)
{
uiCulture = culture;
}

if (culture == null && uiCulture != null)
{
culture = uiCulture;
}

var providerResultCulture = new ProviderCultureResult(culture, uiCulture);

return Task.FromResult(providerResultCulture);
}
}

The code is straightforward nothing fancy except we getting the route data values from the HttpContext as I mentioned before.

We're done!! after that we can you use our provider like the following:

public void Configure(IApplicationBuilder app, IStringLocalizer<startup> localizer)
{
var supportedCultures = new List<cultureinfo>
{
new CultureInfo("en-US"),
new CultureInfo("ar-SA")
};

var options = new RequestLocalizationOptions()
{
DefaultRequestCulture = new RequestCulture("en-US"),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
};

options.RequestCultureProviders.Insert(0, new RouteDataRequestCultureProvider());
app.UseRequestLocalization(options);
...
}

0 Comments

Razor pages are page-based programming model for ASP.NET Core. It's still a prototype in the early stages, hosted in GitHub in this repo, proving the concept of MVC View Pages which already have a long discussion here.

Razor Page came up to simplifying page-focused scenarios, which is very useful in some cases such as live.asp.net website, which has two pages if I'm not wrong, so instead of the MVC that we use and love, this can be simplified with this new model.

What are Razor Pages trying to do & What are not?

The razor pages are trying to:

  • Make dynamic html and forms easier in ASP.NET Core.
  • Reduce the number of folders and files in MVC structure that requires for page-focused scenarios.
  • Simplify the code for page-focused pattern.
  • Enable to return non html responses when necessary. 
  • Use MVC primitives as much as possible.
  • Simplify the migration to traditional MVC structure.

The razor pages are not trying to:

  • Create scripted page framework to compete with PHP .. etc.
  • Hide C# with Domain Specification Language (DSL) in razor or otherwise.
  • Create new primitives that only applicable for this model.

Real World Sample

Let us see some real code and where razor page may fit, I was thinking a lot for appropriate sample that I can demo, I remembered that Scott Hanselman has a great podcast called This Developer's Life, so I downloaded the source code for his repo, and starting to build the podcast again in ASP.NET Core with Razor Page model.

The code in my repo is straightforward, the structure is clean and simple, if we take a quick look to the image below we will notice that Models and Views folders are


same in what we have seen in MVC, there's no Controllers folder, because this model is page-focused so that is why we have Pages folder that contains both .razor and .cs which represent the code behind, this remind me the Web Forms where we have .aspx and .aspx.cs, but the idea is here like what's known before ASP.NET Web Pages.

Some may wonder why we have Views folder?!! the views folder may contains layout pages, partials .. etc. The main pages should be in the Pages folder till now.

Let us see some real code

@functions {
    
    public IEnumerable<Episode> RecentEpisodes {get; set;}
    
    public async Task OnGetAsync()
    {
        RecentEpisodes = GetRecentEpisodes();
    }
    
}

@{
    Layout = "~/Views/Shared/_SiteLayout.cshtml";
    ViewBag.Title = "Stories About Developers and Their Lives";
}

<div class= "container-wide" style="margin-bottom:36px">
    @foreach(var episode in RecentEpisodes){
    <div style="margin-right:22px;float:left;margin-bottom:22px;margin-top:36px" class="episode"> 
        <a href='/Show?slug=@episode.Slug' class="showlink"> 
            <img src="@Href("~/Images/" + episode.LeadImage)"
                alt="@episode.Title" width="320" height="220"/>
            <div class='title'> 
                <p>@episode.Title</p> 
                <div class='description'> 
                    <p>@episode.Summary</p> 
                </div> 
            </div> 
        </a> 
    </div> 
    <audio id="audio-@episode.Slug" preload="none" src="http://podtrac.com/pts/redirect.mp3/traffic.libsyn.com/devlife/@HtmlEncoder.Encode(episode.MediaFile)"></audio>
    }
</div>

the code above is default page which shows all the episodes of the show, if we notice almost the logic code should go inside @functions block and .cs file below, and the rest of the page is a normal view that contains razor syntax.

namespace ThisDevelopersLife.Pages
{
    public class Index : Page
    {
        public IEnumerable<Episode> GetRecentEpisodes()
        {
            return ShowDatabase.Recent(10);
        }
    }
}

The coolest part in Razor Page is the notion of model binding, and if you have a deep look you will find a method called OnGetAsync also there's OnPostAsync which they handle the GET and POST actions.

The Razor Pages have the simplicity of the ASP.NET Web Page to implement page-focus scenarios and brings almost the goodness that ASP.NET MVC have such as model binding, model validation, html helpers, tag helpers and much more.

2 Comments

The ASP.NET Core came up with many middlewares that handle the exception nicely instead of YSOD, almost of them are available in Diagnostics Repository.

If we are looking to the previous versions of ASP.NET, we will notice that ASP.NET has a good way to create a configurable and customizable error pages via Web.Config as the following:

<configuration>
  <system.web>
    <compilation debug="true" />
    <customErrors mode="On" defaultRedirect="DefaultErrorPage.aspx">
      <error statusCode="401" redirect="Http401ErrorPage.aspx"/>
      <error statusCode="403" redirect="Http403ErrorPage.aspx"/>
      <error statusCode="404" redirect="Http404ErrorPage.aspx"/>
      <error statusCode="500" redirect="Http500ErrorPage.aspx"/>
    </customErrors>
  </system.web>
</configuration>

With that I decide to write a small blog post describing how can we achieve that in ASP.NET Core.

First of all we need to define the error pages in a file (JSON in our case) as the following:

"ErrorPages": {
  "401": "/Error/Http401ErrorPage",
  "403": "/Error/Http403ErrorPage",
  "404": "/Error/Http404ErrorPage",
  "500": "/Error/Http500ErrorPage"
}

after that we can fetch the values of status codes & paths easily with the help of Configuration APIs that provided by ASP.NET Core itself.

public IConfigurationRoot Configuration { get; }

internal static IDictionary<int, string> ErrorPages { get; } = new Dictionary<int, string>();

The Configuration property will hold the configuration data and the ErrorPages will contains the details of the error pages.

Then we need to read the data from the JSON file and write them into the declared properties.

var builder = new ConfigurationBuilder()
    .SetBasePath(env.ContentRootPath)
    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
    .AddEnvironmentVariables();

Configuration = builder.Build();

foreach (var c in Configuration.GetSection("ErrorPages").GetChildren())
{
    var key = Convert.ToInt32(c.Key);
    if (!ErrorPages.Keys.Contains(key))
    {
        ErrorPages.Add(key, c.Value);
    }
}

Now it's time to create a piece of middleware that handle the HTTP errors and map them into the predefined paths.

public class CustomErrorPagesMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    public CustomErrorPagesMiddleware(ILoggerFactory loggerFactory, RequestDelegate next)
    {
        _next = next;
        _logger = loggerFactory.CreateLogger<CustomErrorPagesMiddleware>();
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(0, ex, "An unhandled exception has occurred while executing the request");

            if (context.Response.HasStarted)
            {
                _logger.LogWarning("The response has already started, the error page middleware will not be executed.");
                throw;
            }
            try
            {
                context.Response.Clear();
                context.Response.StatusCode = 500;
                return;
            }
            catch (Exception ex2)
            {
                _logger.LogError(0, ex2, "An exception was thrown attempting to display the error page.");
            }
            throw;
        }
        finally
        {
            var statusCode = context.Response.StatusCode;

            if (Startup.ErrorPages.Keys.Contains(statusCode))
            {
                context.Request.Path = Startup.ErrorPages[statusCode];
                await _next(context);
            }
        }
    }
}

The code is straightforward it reads the StatusCode from the Response object and looking for it in the predefined codes and if there's a match it changes the Path of the Request object to the one in the JSON file. 

Last but not least we need write an extension to the IApplicationBuilder interface

public static class BuilderExtensions
{
    public static IApplicationBuilder UseCustomErrorPages(this IApplicationBuilder app)
    {
        return app.UseMiddleware<CustomErrorPagesMiddleware>();
    }
}

and use it in the Configure method in the Startup class like this

app.UseCustomErrorPages();

Finally if you are interested you can check the source code which is hosted in the Github.

0 Comments
Minification is the process of removing unnecessary characters from the source code without changing its functionality.

In this post I will show you how we can optimize the JavaScript & CSS files using a custom minifiers tag helpers.

Now let us start with the CssMinifier which is nothing but a utility class that using online service https://cssminifier.com to minimize the CSS code an remove unnecessary characters.

CSS Minifier


using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;

namespace HelloMvc.TagHelpers
{
    public static class CssMinifier
{
    private const string URL_CSS_MINIFIER       = "https://cssminifier.com/raw";
    private const string POST_PAREMETER_NAME    = "input";

    public static async Task<String> MinifyCss(string inputCss)
    {
        List<KeyValuePair<String, String>> contentData = new List<KeyValuePair<String, String>>
        {
            new KeyValuePair<String, String>(POST_PAREMETER_NAME, inputCss)
        };

        using (HttpClient httpClient = new HttpClient())
        {
            using (FormUrlEncodedContent content = new FormUrlEncodedContent(contentData))
            {
                using (HttpResponseMessage response = await httpClient.PostAsync(URL_CSS_MINIFIER, content))
                {
                    response.EnsureSuccessStatusCode();
                    return await response.Content.ReadAsStringAsync();
                }
            }
        }
    }
}

}
Similarly JavaScriptMinifier which did the exact thing for the JavaScript content using https://javascript-minifier.com.

JavaScript Minifier


using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;

namespace HelloMvc.TagHelpers
{
    public static class JavaScriptMinifier
{
    private const string URL_JS_MINIFIER       = "https://javascript-minifier.com/raw";
    private const string POST_PAREMETER_NAME    = "input";

    public static async Task<String> MinifyJs(string inputJs)
    {
        List<KeyValuePair<String, String>> contentData = new List<KeyValuePair<String, String>>
        {
            new KeyValuePair<String, String>(POST_PAREMETER_NAME, inputJs)
        };

        using (HttpClient httpClient = new HttpClient())
        {
            using (FormUrlEncodedContent content = new FormUrlEncodedContent(contentData))
            {
                using (HttpResponseMessage response = await httpClient.PostAsync(URL_JS_MINIFIER, content))
                {
                    response.EnsureSuccessStatusCode();
                    return await response.Content.ReadAsStringAsync();
                }
            }
        }
    }
}
}
After we have the seen the core minification process, I will show you the code of both CssMinifierTagHelper and JavaScriptTagHelper that allow us to minifiy both inline styles and scripts as well as external files if it needs.

CSS Minifier TagHelper


using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.FileProviders;

namespace HelloMvc.TagHelpers
{
    [HtmlTargetElement("style")]
    [HtmlTargetElement("link", Attributes = MinifyAttributeName)]
    public class CssMinifierTagHelper: TagHelper
    {
        private const string MinifyAttributeName = "minify";
        private readonly IFileProvider _wwwroot;
        private readonly string _wwwrootFolder;
       
        public CssMinifierTagHelper(IHostingEnvironment env)        
        {
            _wwwroot = env.WebRootFileProvider;
            _wwwrootFolder = env.WebRootPath;
        }
       
        [HtmlAttributeName("rel")]
        public string Rel { get; set; }
       
        [HtmlAttributeName("href")]
        public string Href { get; set; }
       
        [HtmlAttributeName(MinifyAttributeName)]
        public bool? Minify {get; set;}
       
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            if (output.TagName == "style")
            {
                var content = output.GetChildContentAsync().Result.GetContent();
                var result = await CssMinifier.MinifyCss(content);
           
                output.Content.SetContent(result);
            }
           
            if (Rel == null || Href == null)
            {
                return;
            }

            if (output.TagName == "link" && Rel == "stylesheet")
            {
                if (!string.IsNullOrEmpty(Href))
                {
                    if(Minify.HasValue && !Minify.Value)
                    {
                        return;
                    }
                   
                    var fileInfo = _wwwroot.GetFileInfo(Href);
                    var cssDirectory = Href.Substring(0,Href.IndexOf(fileInfo.Name)-1);
                    var minFileName = fileInfo.Name.Replace(".css", ".min.css");
                    var minFilePath = Path.Combine(_wwwrootFolder, cssDirectory, minFileName);
                   
                    if (Rel != null)
                    {
                        output.Attributes.SetAttribute("rel", "stylesheet");
                    }
                   
                    if (File.Exists(minFilePath))
                    {
                        if (Href != null)
                        {
                            output.Attributes.SetAttribute("href", Href.Replace(".css", ".min.css"));
                        }
                       
                        return;
                    }
                   
                    using (var readStream = fileInfo.CreateReadStream())
                    using (var reader = new StreamReader(readStream, Encoding.UTF8))
                    {
                        var content = await CssMinifier.MinifyCss(await reader.ReadToEndAsync());
                       
                        using(var writer = new StreamWriter(File.Create(minFilePath), Encoding.UTF8))
                        {
                            await writer.WriteAsync(content);
                        }
                    }
                   
                    if (Href != null)
                    {
                        output.Attributes.SetAttribute("href", Href.Replace(".css", ".min.css"));
                    }
                }
            }
        }
    }
}

JavaScript Minifier TagHelper


using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.FileProviders;

namespace HelloMvc.TagHelpers
{
    [HtmlTargetElement("script")]
    public class JavaScriptMinifierTagHelper: TagHelper
    {
        private const string MinifyAttributeName = "minify";
        private readonly IFileProvider _wwwroot;
        private readonly string _wwwrootFolder;
       
        public JavaScriptMinifierTagHelper(IHostingEnvironment env)        
        {
            _wwwroot = env.WebRootFileProvider;
            _wwwrootFolder = env.WebRootPath;
        }

        [HtmlAttributeName("src")]
        public string Src { get; set; }
       
        [HtmlAttributeName(MinifyAttributeName)]
        public bool? Minify {get; set;}
       
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {        
            if (output.TagName == "script")
            {
                if (Src == null)
                {
                    var content = output.GetChildContentAsync().Result.GetContent();
                    var result = await JavaScriptMinifier.MinifyJs(content);
           
                    output.Content.SetContent(result);
                }
                else
                {
                    if(!Minify.HasValue)
                    {
                        if (Src != null)
                        {
                            output.Attributes.SetAttribute("src", Src);
                        }
                       
                        return;
                    }
                   
                    var fileInfo = _wwwroot.GetFileInfo(Src);
                    var jsDirectory = Src.Substring(0,Src.IndexOf(fileInfo.Name)-1);
                    var minFileName = fileInfo.Name.Insert(fileInfo.Name.Length-3,".min");
                    var minFilePath = Path.Combine(_wwwrootFolder, jsDirectory, minFileName);
                    
                    if (File.Exists(minFilePath))
                    {
                        if (Src != null)
                        {
                            output.Attributes.SetAttribute("src", Src.Replace(".js", ".min.js"));
                        }
                       
                        return;
                    }
                   
                    using (var readStream = fileInfo.CreateReadStream())
                    using (var reader = new StreamReader(readStream, Encoding.UTF8))
                    {
                        var content = await JavaScriptMinifier.MinifyJs(await reader.ReadToEndAsync());
                       
                        using(var writer = new StreamWriter(File.Create(minFilePath), Encoding.UTF8))
                        {
                            await writer.WriteAsync(content);
                        }
                    }
                   
                    if (Src != null)
                    {
                        output.Attributes.SetAttribute("src", Src.Replace(".js", ".min.js"));
                    }
                }
            }
        }
    }
}
For sake of the demo I didn't implement kind of watchers to monitor if some of the external styles or scripts change to minify them again, but this is totally possibly with the newly File System APIs in ASP.NET Core.

Finally I hope this post give you some of thought to how to start with your own custom tag helper.

Hint: The entire source code is hosted in Github