0 Comments

Almost web developers remembered that ASP.NET support chart controls since ASP.NET 3.5 as separate package that you can download it easily, after awhile they become built-in in ASP.NET 4, for more details you can refer to ScottGu's blog post here.

In this blog post I'll explain how can we develop some of the common charting controls that our applications & websites many need with the help of the tag helpers and morris.js.

Morris.js is an open source javascript library that powers the graphs. It has a very simple APIs for drawing line, bar, area and donut charts.

1. Line Charts

2. Area Charts

3. Bar Charts

4. Donut Charts


For more information about morris.js, please visit this link.

Now let us dig into chart controls and how I Create them. First of all we need to define the chart types using the enumeration

public enum ChartType
{
Area,
Bar,
Donut,
Line
}

After that we 'll create ChartTagHelper that calling morris.js APIs behind the scene.

public class ChartTagHelper: TagHelper
{
private IList<PropertyInfo> Properties => Source.First().GetType().GetProperties().ToList();

private IList<string> PropertyNames => Properties.Select(p => p.Name).ToList();

[HtmlAttributeName("id")]
public string Id { get; set; }

[HtmlAttributeName("type")]
public ChartType Type { get; set; }

[HtmlAttributeName("labels")]
public string Labels { get; set; }

[HtmlAttributeName("source")]
public IEnumerable<object> Source { get; set; }

public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.Attributes.SetAttribute("id", Id);
output.TagName = "div";

if (Source == null)
{
return;
}

if (Type == ChartType.Donut)
{
output.PostElement.AppendHtml($@"
<script>
Morris.{Type}({{
element: '{Id}',
data: [
{ConstructDonutChartData()}
]
}});
</script>");
}
else
{
var xKey = PropertyNames.First();
var yKeys = PropertyNames.Skip(1).ToList();

output.PostElement.AppendHtml($@"
<script>
Morris.{Type}({{
element: '{Id}',
data: [
{ConstructChartData()}
],
xkey: '{xKey}',
ykeys: [{String.Join(",",yKeys.Select(p=> "'" + p + "'"))}],
labels: [{String.Join(",",Labels.Split(',').Select(l => "'" + l + "'"))}]
}});
</script>");
}
}

private string ConstructDonutChartData()
{
var labelProperty = Source.First().GetType().GetProperty("label");
var valueProperty = Source.First().GetType().GetProperty("value");

return String.Join(",", Source.Select(s =>
$"{{label: '{labelProperty.GetValue(s)}', value: {valueProperty.GetValue(s)}}}"));
}

private string ConstructChartData()
{
var xKey = PropertyNames.First();
var xProperty = Properties.First();
var yProperties = Properties.Skip(1);

return String.Join(",",
Source.Select(s => $"{{{xKey}:'{xProperty.GetValue(s)}', " + String.Join(",", yProperties.Select(p => p.Name + ":" + p.GetValue(s))) + "}"
));
}
}

The code above is simply constructs the script that morris.js needs to call its APIs, the code using reflection to get the array properties and their values, I know it needs a lot of improvement, which i 'll try to do if I get a time :)

Last thing we can use our tag helper like this:

@{
var contribution = new []
{
new { year = "2016-1", i = 100, pr = 34, c = 12},
new { year = "2016-2", i = 75, pr = 11, c = 3},
new { year = "2016-3", i = 50, pr = 99, c = 7},
new { year = "2016-4", i = 75, pr = 56, c = 1},
new { year = "2016-5", i = 24, pr = 0, c = 54},
new { year = "2016-6", i = 75, pr = 31, c = 3},
new { year = "2016-7", i = 100, pr = 77, c = 44}
};
}

<chart id="bar-example" type="Bar" labels="Issues,Pull Requests,Comments" source="@contribution"></chart>

The same thing for other chart types except you need to change the ChartType property.

You can download the source code for this blog post from my ChartControls repository on GitHub.

2 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)
{
await _next(context);
var isImage = context.Response.ContentType.StartsWith("image/");

if (isImage)
{
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");
context.Response.Clear();
await context.Response.SendFileAsync(unauthorizedImagePath);
}
}
}
}

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.

2 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));
}

string culture = null;
string 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);
...
}

2 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.