Create A Sample C# Corner 👨‍🎓Flair With ASP.NET MVC

We will see how to create a sample C# Corner User Flair in ASP.NET MVC application using HtmlAgilityPack. We will also add a provision to save this flair as an image using a third-party JavaScript library html2canvas.

In this post, we will see how to create a sample C# Corner User Flair in ASP.NET MVC application using HtmlAgilityPack. We will also add a provision to save this flair as an image using a third-party JavaScript library, html2canvas.

Introduction

C# Corner itself provides the user flair. This is my humble attempt to scrape the data from C# Corner user profile page using HtmlAgilityPack in ASP.NET MVC application. We collected the user data like Name, Rank, Reputation, Reads from the corresponding span ids. We also got all the image URLs from the site (profile page) and only took the image used for the author's image. We will create a sample user flair using this data. Will also provide an option to save this flair as an image.

The HtmlAgilityPack, commonly known as HAP is an HTML parser written in C# to read/write DOM (Document Object Model) and supports plain XPATH or XSLT. You can get more details on HAP from this documentationHtml2canvas is mainly used for converting the HTML content into images/pdf. Please refer to this site to get more details of html2canvas library.

Dependencies for this project -

  • Visual Studio 2015 or higher (we are using .NET framework 4.5)
  • Install HtmlAgilityPack using NuGet
  • Valid CDN link for html2canvas library

Create an MVC application in Visual Studio

We can choose the ASP.NET 4.5.2 Templates and MVC template. Click the OK button to create the project.

 

In the MVC project, we have Controllers, Models, and Views folders. We need to create a “UserInfo” class inside the Models folder.

UserInfo.cs
  1. namespace CsharpCornerFlairMVC.Models  
  2. {  
  3.     public class UserInfo  
  4.     {  
  5.         public string UserId { getset; }  
  6.         public string UserName { getset; }  
  7.         public string UserRank { getset; }  
  8.         public string UserReputation { getset; }  
  9.         public string UserRead { getset; }  
  10.         public string ImageURL { getset; }  
  11.         public bool InvalidUser { getset; }  
  12.         public bool PageInit { getset; }  
  13.     }  
  14. }  

Please note that we have additionally added “InvalidUser” and “PageInit” properties along with other user information properties in this class. These are used for controlling the visibility of some page contents in Razor Views. We will discuss about these details later in this post.

We need the C# Corner site logo for creating a flair. We also need an animated GIF file to show the process. We can add these images to the “Content” folder.
 
 

Now, modify the “_Layout.cshtml” Razor View file inside the Views -> Shared folder.

_Layout.cshtml
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4.     <meta charset="utf-8" />  
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  6.     <title>C# Corner Sample Flair</title>  
  7.     <link href="https://csharpcorner-mindcrackerinc.netdna-ssl.com/Images/McnIcon.ico" rel="shortcut icon" type="image/x-icon" />  
  8.     @Styles.Render("~/Content/css")  
  9.     @Scripts.Render("~/bundles/modernizr")  
  10. </head>  
  11. <body>  
  12.     <div class="navbar navbar-inverse navbar-fixed-top">  
  13.         <div class="container">  
  14.             <div class="navbar-header">  
  15.                 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">  
  16.                     <span class="icon-bar"></span>  
  17.                     <span class="icon-bar"></span>  
  18.                     <span class="icon-bar"></span>  
  19.                 </button>  
  20.                 @Html.ActionLink("C# Corner Sample Flair""Index""Home"new { area = "" }, new { @class = "navbar-brand" })  
  21.             </div>  
  22.             <div class="navbar-collapse collapse">  
  23.                 <ul class="nav navbar-nav">  
  24.                     <li>@Html.ActionLink("Home""Index""Home")</li>  
  25.                     <li>@Html.ActionLink("About""About""Home")</li>  
  26.                 </ul>  
  27.             </div>  
  28.         </div>  
  29.     </div>  
  30.     <div class="container body-content">  
  31.         @RenderBody()  
  32.         <hr />  
  33.         <footer>  
  34.             <p>  
  35.                 © 2019 - C# Corner Sample Flair     
  36.                 <a href="https://codewithsarath.com" target="_blank">By Sarath Lal</a>  
  37.             </p>  
  38.         </footer>  
  39.     </div>  
  40.   
  41.     @Scripts.Render("~/bundles/jquery")  
  42.     @Scripts.Render("~/bundles/bootstrap")  
  43.     @RenderSection("scripts", required: false)  
  44. </body>  
  45. </html>  

This is a master layout page which will be rendered with all the other Views. We can modify the important Razor View file “Index.cshtml”.

Index.cshtml
  1. @{  
  2.     ViewBag.Title = "Home Page";  
  3.   
  4. }  
  5. @model CsharpCornerFlairMVC.Models.UserInfo  
  6.   
  7. @using (Html.BeginForm("Index""Home", FormMethod.Post))  
  8. {  
  9.     <div style="padding-top:30px;">  
  10.         @Html.Label("C# Corner User Id")  
  11.     </div>  
  12.     <div>  
  13.         @Html.TextBoxFor(m => m.UserId)  
  14.         <i onclick="resetValues()" style="color:forestgreen; cursor:pointer;" class="glyphicon glyphicon-repeat" title="Reset Values"></i>  
  15.     </div>  
  16.     <div style="padding-top:20px;">  
  17.         <input class="btn-lg btn-info" type="submit" id="btnSubmit" value="Process Flair" onclick="processFlair()" name="Submit" />  
  18.     </div>  
  19.   
  20.     <div id="process" style="display:none">  
  21.         <img src="~/Content/process.gif" alt="Process" />  
  22.     </div>  
  23.   
  24.     @Html.HiddenFor(model => model.UserId)  
  25. }  
  26.   
  27. @if (Model != null && !Model.InvalidUser && !Model.PageInit)  
  28. {  
  29.     <div id="flair" style="padding-top:30px; width:550px; column-count: 4;">  
  30.         <div style="float: left;">  
  31.             <img src="~/Content/SiteLogo.png" style="width:30px; height:22px;" alt="C# Corner" />  
  32.         </div>  
  33.         <div style="padding-left: 30px;  width:250px;">  
  34.             @Model.UserName  
  35.         </div>  
  36.         <div style="padding-left: 30px; width:250px; cursor:pointer">  
  37.             <i class="greendot"></i> <text title="Current Rank">@Model.UserRank</text> <i class="orangedot"></i> <text title="Total Reputation">@Model.UserReputation</text> <i class="bluedot"></i> <text title="Total Reads">@Model.UserRead</text>  
  38.         </div>  
  39.         <div style="float: right;">  
  40.             <img src="@Model.ImageURL" alt="NA"  
  41.                  style="border-radius: 50%; width:50px; height:50px;">  
  42.         </div>  
  43.     </div>  
  44.   
  45.     <div id="saveflair">  
  46.         <div style="padding-top:20px;">  
  47.             <input class="btn-success btn-sm" type="button" onclick="saveAsImage()" value="Save as Image!" />  
  48.         </div>  
  49.   
  50.         <div style="padding:10px">  
  51.             <img id="txtScreenshot" src="">  
  52.         </div>  
  53.     </div>  
  54. }  
  55. else  
  56. {  
  57.     if (Model != null && Model.InvalidUser && !Model.PageInit)  
  58.     {  
  59.         <div id="invaliduser" style="padding-top:30px;">  
  60.             <i>Invalid User Id</i>  
  61.         </div>  
  62.     }  
  63. }  
  64.   
  65. @section scripts {  
  66.   
  67.     <script src="//cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>  
  68.   
  69.     <script>  
  70.   
  71.         function resetValues() {  
  72.             document.getElementById("UserId").value = '';  
  73.   
  74.             var invalidUser = @Html.Raw(Json.Encode(Model.InvalidUser));  
  75.   
  76.             if (invalidUser) {  
  77.                 document.getElementById("invaliduser").style.display = 'none';  
  78.             }  
  79.             else {  
  80.                 document.getElementById("saveflair").style.display = 'none';  
  81.                 document.getElementById("flair").style.display = 'none';  
  82.             }  
  83.         }  
  84.   
  85.         function processFlair() {  
  86.             document.getElementById("process").style.display = 'block';  
  87.   
  88.             var invalidUser = @Html.Raw(Json.Encode(Model.InvalidUser));  
  89.   
  90.             if (invalidUser) {  
  91.                 document.getElementById("invaliduser").style.display = 'none';  
  92.             }  
  93.             else {  
  94.                 document.getElementById("saveflair").style.display = 'none';  
  95.                 document.getElementById("flair").style.display = 'none';  
  96.             }  
  97.         }  
  98.   
  99.         function saveAsImage() {  
  100.             html2canvas(document.getElementById("flair"),  
  101.                 {  
  102.                     useCORS: true,  
  103.                     onrendered: function (canvas) {  
  104.                         var screenshot = canvas.toDataURL("image/png");  
  105.                         document.getElementById("txtScreenshot").setAttribute("src", screenshot);  
  106.                     }  
  107.                 });  
  108.         }  
  109.     </script>  
  110.   
  111. }  

I have put all the client-side logic inside this Razor file. As I mentioned earlier, I have used the InvalidUser” and “PageInit” properties to hide/show some div tags.

  1. if (Model != null && Model.InvalidUser && !Model.PageInit)  
  2.    {  
  3.        <div id="invaliduser" style="padding-top:30px;">  
  4.            <i>Invalid User Id</i>  
  5.        </div>  
  6.    }  

Please note, I have also used @Html.Raw to get the server Model values in the client-side code.

 
  1. function resetValues() {  
  2.             document.getElementById("UserId").value = '';  
  3.   
  4.             var invalidUser = @Html.Raw(Json.Encode(Model.InvalidUser));  
  5.   
  6.             if (invalidUser) {  
  7.                 document.getElementById("invaliduser").style.display = 'none';  
  8.             }  
  9.             else {  
  10.                 document.getElementById("saveflair").style.display = 'none';  
  11.                 document.getElementById("flair").style.display = 'none';  
  12.             }  
  13.         }  

I have used a very simple logic to save the HTML to image. (Here, we are saving it as a PNG file).

 
  1. function saveAsImage() {  
  2.            html2canvas(document.getElementById("flair"),  
  3.                {  
  4.                    useCORS: true,  
  5.                    onrendered: function (canvas) {  
  6.                        var screenshot = canvas.toDataURL("image/png");  
  7.                        document.getElementById("txtScreenshot").setAttribute("src", screenshot);  
  8.                    }  
  9.                });  
  10.        }  

I have used three CSS classes to show the green, orange, and blue dots on flair. We can add these CSS classes in the Site.css file inside Content folder.

Site.css
  1. .greendot {  
  2.     height10px;  
  3.     width10px;  
  4.     background-colorgreen;  
  5.     border-radius: 50%;  
  6.     display: inline-block;  
  7. }  
  8.   
  9. .orangedot {  
  10.     height10px;  
  11.     width10px;  
  12.     background-color: orange;  
  13.     border-radius: 50%;  
  14.     display: inline-block;  
  15. }  
  16.   
  17. .bluedot {  
  18.     height10px;  
  19.     width10px;  
  20.     background-colorblue;  
  21.     border-radius: 50%;  
  22.     display: inline-block;  
  23. }  

Let us add logic to scrape the user data from the user profile page. We will add these codes in our HomeController class file.

Before adding these codes, we must install the HtmlAgilityPack NuGet package to our project.
 
 
 
We can modify the HomeController class file with the below code.
 
HomeController.cs
  1. using CsharpCornerFlairMVC.Models;  
  2. using HtmlAgilityPack;  
  3. using System;  
  4. using System.Collections.Generic;  
  5. using System.IO;  
  6. using System.Net;  
  7. using System.Text;  
  8. using System.Web.Mvc;  
  9.   
  10. namespace CsharpCornerFlairMVC.Controllers  
  11. {  
  12.     public class HomeController : Controller  
  13.     {  
  14.         public ActionResult Index()  
  15.         {  
  16.             UserInfo userInfo = new UserInfo();  
  17.             userInfo.PageInit = true;  
  18.             return View(userInfo);  
  19.         }  
  20.   
  21.         [HttpPost]  
  22.         public ActionResult Index(UserInfo userInfo)  
  23.         {  
  24.             if (userInfo != null && !string.IsNullOrEmpty(userInfo.UserId))  
  25.             {  
  26.                 userInfo.PageInit = false;  
  27.                 if (String.IsNullOrEmpty(userInfo.UserId))  
  28.                 {  
  29.                     return View(userInfo);  
  30.                 }  
  31.   
  32.                 string urlAddress = "https://www.c-sharpcorner.com/members/" + userInfo.UserId;  
  33.   
  34.                 try  
  35.                 {  
  36.                     HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlAddress);  
  37.                     HttpWebResponse response = (HttpWebResponse)request.GetResponse();  
  38.                     string strData = "";  
  39.   
  40.                     if (response.StatusCode == HttpStatusCode.OK)  
  41.                     {  
  42.                         Stream receiveStream = response.GetResponseStream();  
  43.                         StreamReader readStream = null;  
  44.   
  45.                         if (response.CharacterSet == null)  
  46.                         {  
  47.                             readStream = new StreamReader(receiveStream);  
  48.                         }  
  49.                         else  
  50.                         {  
  51.                             readStream = new StreamReader(receiveStream, Encoding.GetEncoding(response.CharacterSet));  
  52.                         }  
  53.   
  54.                         strData = readStream.ReadToEnd();  
  55.   
  56.                         response.Close();  
  57.                         readStream.Close();  
  58.                     }  
  59.   
  60.                     string htmlToParse = strData;  
  61.   
  62.                     HtmlDocument htmlDocument = new HtmlDocument();  
  63.                     htmlDocument.LoadHtml(htmlToParse);  
  64.   
  65.                     var nodeName = htmlDocument.DocumentNode.SelectSingleNode("//title");  
  66.                     if (nodeName != null)  
  67.                     {  
  68.                         userInfo.UserName = nodeName.InnerText.Trim();  
  69.                     }  
  70.                     else  
  71.                     {  
  72.                         userInfo.UserName = "Invalid";  
  73.                     }  
  74.   
  75.                     var nodeRank = htmlDocument.DocumentNode.SelectSingleNode("//span[@id='ctl00_ContentMain_AuthorProfile1_LabelRank']");  
  76.                     if (nodeRank != null)  
  77.                     {  
  78.                         userInfo.UserRank = nodeRank.InnerText;  
  79.                     }  
  80.                     else  
  81.                     {  
  82.                         var nodeUserType = htmlDocument.DocumentNode.SelectSingleNode("//span[@id='spanUserType']");  
  83.                         if (nodeUserType != null)  
  84.                         {  
  85.                             userInfo.UserRank = nodeUserType.InnerText.ToLower();  
  86.                         }  
  87.                         else  
  88.                         {  
  89.                             userInfo.UserRank = "NA";  
  90.                         }  
  91.                     }  
  92.   
  93.                     var nodeReputation = htmlDocument.DocumentNode.SelectSingleNode("//span[@id='ctl00_ContentMain_AuthorProfile1_AuthorPoints']");  
  94.                     if (nodeReputation != null)  
  95.                     {  
  96.                         userInfo.UserReputation = nodeReputation.InnerText;  
  97.                     }  
  98.                     else  
  99.                     {  
  100.                         userInfo.UserReputation = "NA";  
  101.                     }  
  102.   
  103.                     var nodeRead = htmlDocument.DocumentNode.SelectSingleNode("//span[@id='ctl00_ContentMain_AuthorProfile1_TotalReadCount']");  
  104.                     if (nodeRead != null)  
  105.                     {  
  106.                         userInfo.UserRead = nodeRead.InnerText;  
  107.                     }  
  108.                     else  
  109.                     {  
  110.                         userInfo.UserRead = "NA";  
  111.                     }  
  112.   
  113.                     List<string> imgURLs = new List<string>();  
  114.                     foreach (HtmlNode node in htmlDocument.DocumentNode.SelectNodes("//img"))  
  115.                     {  
  116.                         var imgURL = node.Attributes["src"].Value;  
  117.   
  118.                         if (imgURL.Contains("AuthorImage"))  
  119.                         {  
  120.                             imgURLs.Add(imgURL);  
  121.                         }  
  122.   
  123.                     }  
  124.                     if (imgURLs.Count == 1)  
  125.                     {  
  126.                         userInfo.ImageURL = imgURLs[0];  
  127.                     }  
  128.                     else  
  129.                     {  
  130.                         userInfo.ImageURL = "https://csharpcorner-mindcrackerinc.netdna-ssl.com/UploadFile/AuthorImage/DefaultAuthorImage.jpg";  
  131.                     }  
  132.   
  133.                 }  
  134.                 catch (Exception)  
  135.                 {  
  136.                     userInfo.InvalidUser = true;  
  137.                     return View(userInfo);  
  138.                 }  
  139.                 userInfo.InvalidUser = false;  
  140.                 return View(userInfo);  
  141.             }  
  142.             else  
  143.             {  
  144.                 userInfo.InvalidUser = true;  
  145.                 return View(userInfo);  
  146.             }  
  147.   
  148.         }  
  149.   
  150.         public ActionResult About()  
  151.         {  
  152.             return View();  
  153.         }  
  154.   
  155.     }  
  156. }  

We are getting the C# Corner user id from Razor View and passing this value to “Index” method in the Controller. After that, we passed the user id along with “https://www.c-sharpcorner.com/members/" URL (For e.g.: “https://www.c-sharpcorner.com/members/sarath-lal7") and got the page content and stored into a string variable.

  1. string urlAddress = "https://www.c-sharpcorner.com/members/" + userInfo.UserId;  
  2.   
  3.                 try  
  4.                 {  
  5.                     HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlAddress);  
  6.                     HttpWebResponse response = (HttpWebResponse)request.GetResponse();  
  7.                     string strData = "";  
  8.   
  9.                     if (response.StatusCode == HttpStatusCode.OK)  
  10.                     {  
  11.                         Stream receiveStream = response.GetResponseStream();  
  12.                         StreamReader readStream = null;  
  13.   
  14.                         if (response.CharacterSet == null)  
  15.                         {  
  16.                             readStream = new StreamReader(receiveStream);  
  17.                         }  
  18.                         else  
  19.                         {  
  20.                             readStream = new StreamReader(receiveStream, Encoding.GetEncoding(response.CharacterSet));  
  21.                         }  
  22.   
  23.                         strData = readStream.ReadToEnd();  
  24.   
  25.                         response.Close();  
  26.                         readStream.Close();  
  27.                     }  

We can get the user's name using the below code.

  1. var nodeName = htmlDocument.DocumentNode.SelectSingleNode("//title");  
  2.                     if (nodeName != null)  
  3.                     {  
  4.                         userInfo.UserName = nodeName.InnerText.Trim();  
  5.                     }  
  6.                     else  
  7.                     {  
  8.                         userInfo.UserName = "Invalid";  
  9.                     }  

Please note that currently the username is the user profile page title.

We can get the User Rank using this code block.
  1. var nodeRank = htmlDocument.DocumentNode.SelectSingleNode("//span[@id='ctl00_ContentMain_AuthorProfile1_LabelRank']");  
  2.                     if (nodeRank != null)  
  3.                     {  
  4.                         userInfo.UserRank = nodeRank.InnerText;  
  5.                     }  
  6.                     else  
  7.                     {  
  8.                         var nodeUserType = htmlDocument.DocumentNode.SelectSingleNode("//span[@id='spanUserType']");  
  9.                         if (nodeUserType != null)  
  10.                         {  
  11.                             userInfo.UserRank = nodeUserType.InnerText.ToLower();  
  12.                         }  
  13.                         else  
  14.                         {  
  15.                             userInfo.UserRank = "NA";  
  16.                         }  
  17.                     }  

User's rank is stored inside the span id “ctl00_ContentMain_AuthorProfile1_LabelRank”.

We can get the User Reputation using the following lines.
  1. var nodeReputation = htmlDocument.DocumentNode.SelectSingleNode("//span[@id='ctl00_ContentMain_AuthorProfile1_AuthorPoints']");  
  2.                     if (nodeReputation != null)  
  3.                     {  
  4.                         userInfo.UserReputation = nodeReputation.InnerText;  
  5.                     }  
  6.                     else  
  7.                     {  
  8.                         userInfo.UserReputation = "NA";  
  9.                     }  

Reputation is stored inside the span id “ctl00_ContentMain_AuthorProfile1_AuthorPoints”.

We can get the User Reads using the below code.
  1. var nodeRead = htmlDocument.DocumentNode.SelectSingleNode("//span[@id='ctl00_ContentMain_AuthorProfile1_TotalReadCount']");  
  2.                     if (nodeRead != null)  
  3.                     {  
  4.                         userInfo.UserRead = nodeRead.InnerText;  
  5.                     }  
  6.                     else  
  7.                     {  
  8.                         userInfo.UserRead = "NA";  
  9.                     }  

User Reads are stored inside the span id “ctl00_ContentMain_AuthorProfile1_TotalReadCount”.

For getting the author's image, we will use another method.
  1. List<string> imgURLs = new List<string>();  
  2.                     foreach (HtmlNode node in htmlDocument.DocumentNode.SelectNodes("//img"))  
  3.                     {  
  4.                         var imgURL = node.Attributes["src"].Value;  
  5.   
  6.                         if (imgURL.Contains("AuthorImage"))  
  7.                         {  
  8.                             imgURLs.Add(imgURL);  
  9.                         }  
  10.   
  11.                     }  
  12.                     if (imgURLs.Count == 1)  
  13.                     {  
  14.                         userInfo.ImageURL = imgURLs[0];  
  15.                     }  
  16.                     else  
  17.                     {  
  18.                         userInfo.ImageURL = "https://csharpcorner-mindcrackerinc.netdna-ssl.com/UploadFile/AuthorImage/DefaultAuthorImage.jpg";  
  19.                     }  

We are collecting all the image URLs from site and finding the image URL containing “AuthorImage”.

We can modify the “About.cshtml” file with the below code.

About.cshtml

  1. @{  
  2.    ViewBag.Title = "About";  
  3. }  
  4. <h2>@ViewBag.Title.</h2>  
  5. <p>We will create a sample C# Corner Flair in ASP.NET MVC application using <b>HtmlAgilityPack.</b><br />  
  6. We will also add a provision to save this flair as image using third party JavaScript library <b>html2canvas.</b></p>   

Well, we have completed all the coding part. Now, we can run the application and check the functionalities. If you do not know your user id, please click your user profile and get the value after “members”. Here, my user id is “sarath-lal7”.

 
 
 
You can enter the user id on the textbox and click the “Process Flair” button to create the flair.
 
Flair is created as shown below.
 
 
You can save the flair as an image by clicking the “Save as Image” button.
 
 

Just right-click the image file and save to your local folder easily.

If you enter a non-author user id for admins, you will not get the user rank usually. I have taken the user type from corresponding span id and showed that information instead of user rank.
 
Here, I have entered the user id of the founder of C# Corner, Mahesh Chand. You can notice that it is displayed as “admin” instead of rank.
 
 

You will not get the user rank usually for editors too. Here also, I take the user type and show it instead of user rank.

I have entered the user id of editor Mr. Pranav Mittal.
 
 
If you enter a guest user id (non-author and non-editor), you will get the default author image with the following details.
 
 

Challenges for this application in future

I have made this application based on the current span ids used on the C# Corner website. If the C# Corner team change these values in the future, that may affect the application. We will have to change our code accordingly.

 
 
 

Conclusion

In this post, we have seen how to scrape data from C# Corner profile page for a user and get details like name, user rank, user reputation and user image. We have created a user flair using this data. For that, we used HtmlAgilityPack. We have also provided an option to save the user flair as an image. For that, we used a third-party JavaScript library html2canvas for this.