RSS

Category Archives: MVC

ASP .NET Health Monitor and Custom Errors

ASP .NET Health Monitor and Custom Errors

Monitoring is an important part of the project life, ASP .NET provides features that makes it easy to monitor your web app while it is in production, it is called ASP .NET Health Monitoring.

Enabling Health Monitor

create a new ASP .NET MVC 3 Web Application and edit the Web.Config file, add the following between the <system.web> tags

  <!-- Health Monitoring -->
    <healthMonitoring enabled="true">
      <providers>
        <clear />
        <!-- Email WebEvents Provider -->
        <add name="EmailWebEventProvider" type="System.Web.Management.SimpleMailWebEventProvider" from="logger@mydomain.com" to="MY_USERNAME@gmail.com" bodyHeader="My Monitor" buffer="false" />
      </providers>
      <eventMappings>
        <clear />
        <add name="All Errors" type="System.Web.Management.WebBaseErrorEvent" startEventCode="0" endEventCode="2147483647" />
      </eventMappings>
      <rules>
        <clear />
        <add name="All Errors Default" eventName="All Errors" provider="EmailWebEventProvider" profile="Default" minInstances="1" maxLimit="1000" minInterval="23:59:00" />
      </rules>
    </healthMonitoring>

here we have enabled health monitoring with an Email Alert for any error that occurs but we will only be notified once a day for each specific error, as we have set minInterval=”23:59:00″.

to complete the configuration go ahead and set the “from”, “to”, and “header” properties in the above code, for more details on the health monitor properties go here.

GMAIL SMTP Server

at this point it will use the default local host smtp server, if you dont have an smtp server running you can set it up to connect to any available server, here we are using gmail smtp server as i have a gmail account (500 email/day limit)

add the following after the <system.web> tags

<!--SMTP -->
  <system.net>
    <mailSettings>
      <smtp from="noreply@mydomain.com" deliveryMethod="Network">
        <network defaultCredentials="false" enableSsl="true" host="smtp.gmail.com" password="MY_PASSWORD" port="587" userName="MY_USERNAME@gmail.com" />
      </smtp>
    </mailSettings>
  </system.net>

enter your email and password in the “password” and “userName” fields.

ERROR DEMO

to trigger an error we will add the following code to the “HomeController”




Function Index() As ActionResult
ViewData("Message") = "Welcome to ASP.NET MVC!"

Throw New ArgumentException("ERROR FROM HomeController")

Return View()
End Function

open the site and you will get an error, and an email notification.

Handling Errors

to suppress these errors and show our own custom page we have to do some extra work, in our global.asax file we add a global error handler, then we create views for each of the different types of errors you want to have a custom page for.

open Global.asax and add the following code


Private Sub MvcApplication_Error(sender As Object, e As System.EventArgs) Handles Me.Error

Dim routeData = New RouteData

routeData.Values.Add("controller", "Home")

Dim lastEx = Server.GetLastError

If TypeOf (lastEx) Is HttpException Then

Dim ex As HttpException = lastEx

Select Case ex.GetHttpCode

Case 404

routeData.Values.Add("action", "Error404")

Case 500

routeData.Values.Add("action", "Error500")

Case Else

routeData.Values.Add("action", "ErrorGeneric")

End Select

Else 'not an Http Ex.

routeData.Values.Add("action", "ErrorGeneric")

End If

routeData.Values.Add("exception", lastEx)

Server.ClearError()

Dim errController As IController = New ErrorController

errController.Execute(New RequestContext(New HttpContextWrapper(Context), routeData))

End Sub

in the above code we handle the Me.Error global event, then we choose which view we want to show based on the HttpCode, and Exception type, we also put our Exception in routeData (optional) so it will be available to us in our View, clear the error that tells ASP .NET that we handled it, and then return one of the Views from the ErrorController, let’s create the ErrorController

ErrorController

create a new controller, name it “ErrorController”, and add the following code


Public Function Error404() As ActionResult
Response.StatusCode = 404

Dim ex As Exception = Nothing

If RouteData.Values.ContainsKey("exception") Then

ex = RouteData.Values("exception")

End If
Return View(ex)

End Function

Public Function Error500() As ActionResult
Response.StatusCode = 500

Dim ex As Exception = Nothing

If RouteData.Values.ContainsKey("exception") Then

ex = RouteData.Values("exception")

End If
Return View(ex)

End Function

Public Function ErrorGeneric() As ActionResult
Dim ex As Exception = Nothing

If RouteData.Values.ContainsKey("exception") Then

ex = RouteData.Values("exception")

End If
Return View(ex)

End Function

here we have 3 views, one for 404 (Not Found), another for 500 server error, and a default generic error handler. we inspect if we passed in the exception in routeData and pass it to our view as the view model type.

right click on the “Shared” folder and create the following 3 views

Error404.vbhtml

@ModelType Exception
@Code

ViewData("Title") = "Error 404 : Page not found"

End Code
@ViewData("Title")
My Custom Error Page

@Model.Message

@Model.StackTrace

Error500.vbhtml

save as above

ErrorGeneric.vbhtml

save as above

now if we rebuild and open the site again we will see the following screen

Screenshot
at this point your site has been setup to email you when errors occur in your app, and if they do the visitor will see a nice custom error page, with this in your app you can handle more complex scenarios like having a 404 page that inspects the Exception passed in and tries to figure out what might the visitor was trying to find in our site and give them some suggestions.

you can download the full sample project here

i hope you like this post and for my next post i will be looking at creating a whole site using ASP .NET localization features.

Advertisements
 
Leave a comment

Posted by on 22, Feb 2012 in .NET, ASP.NET, MVC, VB.NET

 

Adding Social Buttons, Twitter, facebook, Google +1, to ASP .NET MVC

an easy way to add social buttons to your site is having a nice and portable ASP .NET MVC HTML Helper.
according to the documentation for Tweet button, facebook Like button, and Google +1 button, listed below is the code to create these social buttons and embed them into any site.

Required Scripts

include the following script into your _layout page.


$(function () {

    //Google +1
    $.getScript("http://apis.google.com/js/plusone.js", null, true);

    //Twitter
    $.getScript("http://platform.twitter.com/widgets.js", null, true);

    //Facebook
    $.getScript("http://connect.facebook.net/en_US/all.js#xfbml=1",  function(){
       
        $('body').append('<div id="fb-root"></div>'); 

        FB.init({ status: true, cookie: true, xfbml: true }); 
   
    }, true);
});

and the following is the HTML Helpers for each Social Button.

Tweet Button

    <Extension()>
    Function Social_Twitter(htmlHelper As HtmlHelper, title As String, Optional url As String = "") As MvcHtmlString
       
        Dim social_link As New TagBuilder("a")

        social_link.Attributes.Add("href", "https://twitter.com/share")
        social_link.Attributes.Add("class", "twitter-share-button")
        social_link.Attributes.Add("data-via", "MY-TWITTER-HANDLE")
        social_link.Attributes.Add("data-count", "horizontal")
        social_link.Attributes.Add("data-text", title)
        social_link.SetInnerText("Tweet")

        If Not String.IsNullOrEmpty(url) Then

            social_link.Attributes.Add("data-url", url)

        End If

        Return New MvcHtmlString(social_link.ToString(TagRenderMode.Normal))
    End Function

facebook Like


 <Extension()>
    Function Social_Facebook(htmlHelper As HtmlHelper, title As String, Optional url As String = "") As MvcHtmlString

        Dim str = New StringBuilder

        Dim social_link As New TagBuilder("div")
        social_link.Attributes.Add("class", "fb-like")
        social_link.Attributes.Add("data-send", "false")
        social_link.Attributes.Add("data-layout", "button_count")
        social_link.Attributes.Add("data-show-faces", "false")
        social_link.Attributes.Add("data-font", "arial")

        If Not String.IsNullOrEmpty(url) Then

            social_link.Attributes.Add("data-href", url)

        End If

        str.Append(social_link.ToString(TagRenderMode.Normal))

        Return New MvcHtmlString(str.ToString)
    End Function

Google +1 Button

<Extension()>
    Function Social_GooglePlusOne(htmlHelper As HtmlHelper, title As String, Optional url As String = "") As MvcHtmlString
       
        Dim social_link As New TagBuilder("div")

        social_link.Attributes.Add("class", "g-plusone")
        social_link.Attributes.Add("data-size", "medium")
        'social_link.Attributes.Add("data-size", "small")

        If Not String.IsNullOrEmpty(url) Then

            social_link.Attributes.Add("data-href", url)

        End If

        Return New MvcHtmlString(social_link.ToString(TagRenderMode.Normal))
    End Function

you can also combine them all, and generate them with one line using the following

All Social Buttons Helper

<Extension()>
    Function Social_AllButtons(htmlHelper As HtmlHelper, title As String, url As String) As MvcHtmlString
     
        Dim str = New StringBuilder

        Dim ul As New TagBuilder("ul")
        ul.AddCssClass("social")

        'Google
        Dim li3 As New TagBuilder("li")
        li3.InnerHtml = htmlHelper.Social_GooglePlusOne(title, url).ToHtmlString
        li3.AddCssClass("social-google")
        ul.InnerHtml += li3.ToString

        'Twitter
        Dim li2 As New TagBuilder("li")
        li2.InnerHtml = htmlHelper.Social_Twitter(title, url).ToHtmlString
        li2.AddCssClass("social-twitter")
        ul.InnerHtml += li2.ToString

        'facebook
        Dim li1 As New TagBuilder("li")
        li1.InnerHtml = htmlHelper.Social_Facebook(title, url).ToHtmlString
        li1.AddCssClass("social-facebook")
        ul.InnerHtml += li1.ToString
        str.Append(ul.ToString(TagRenderMode.Normal))

        Return New MvcHtmlString(str.ToString)
    End Function

now you can embed social buttons into any page, and if you do not set the URL, they get the current page URL by default.

you can also expand on these methods to add the required JavaScript source to run these buttons from their respective owners if it’s not included in the page, this way; you can really separate all the dependencies into a totally portable utilities library.

 
6 Comments

Posted by on 18, Nov 2011 in .NET, ASP.NET, MVC, VB.NET

 

Generating Captcha images – ASP .NET MVC

Wikipedia describes Captcha images as

a type of challenge-response test used in computing as an attempt to ensure that the response is generated by a person

there are a few simple plug-ins that you can use in your ASP .NET MVC App, like reCaptcha, but to have a fully flexible and integrated solution you have to create you own, in this article we will define our implementation to handle Captcha images.

start by creating an empty MVC 3 Internet application.

go to the HomeController.vb class and add the following Action method.

HomeController.vb

GetCaptcha Action

Function GetCaptcha() As ActionResult
        'Captcha Image Size  Width - Height
        Dim width As Integer = 75
        Dim height As Integer = 20

        'Captcha String
        Dim fontFamily = "Tahoma"
        ' -  Generate Random
        Dim randomsize As Integer = 5
        Dim randomstringbuilder As New StringBuilder()
        Dim random As New Random(DateTime.Now.Millisecond)
        Dim ch As Char
        For i As Integer = 0 To randomsize - 1
            ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)))
            randomstringbuilder.Append(ch)
        Next
        Dim captchaString = randomstringbuilder.ToString

        ' Create a new 32-bit bitmap image.
        Dim bitmap As New Drawing.Bitmap(width, height, Drawing.Imaging.PixelFormat.Format32bppArgb)

        ' Create a graphics object for drawing.
        Dim g As Drawing.Graphics = Drawing.Graphics.FromImage(bitmap)
        g.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias
        Dim rect As New Drawing.Rectangle(0, 0, width, height)

        ' Fill in the background.
        Dim hatchBrush As New Drawing.Drawing2D.HatchBrush(Drawing.Drawing2D.HatchStyle.Wave, Drawing.Color.LightGray, Drawing.Color.White)
        g.FillRectangle(hatchBrush, rect)

        ' Set up the text font.
        Dim size As Drawing.SizeF
        Dim fontSize As Single = rect.Height + 1
        Dim font As Drawing.Font
        Dim format As New Drawing.StringFormat()
        format.Alignment = Drawing.StringAlignment.Center
        format.LineAlignment = Drawing.StringAlignment.Center

        ' Adjust the font size until the text fits within the image.
        Do
            fontSize -= 1
            font = New Drawing.Font(fontFamily, fontSize, Drawing.FontStyle.Bold)
            size = g.MeasureString(captchaString, font, New Drawing.SizeF(width, height), format)
        Loop While size.Width > rect.Width

        ' Create a path using the text and warp it randomly.
        Dim path As New Drawing.Drawing2D.GraphicsPath()
        path.AddString(captchaString, font.FontFamily, CInt(font.Style), font.Size, rect, format)
        Dim v As Single = 4.0F
        Dim points As Drawing.PointF() = {New Drawing.PointF(random.[Next](rect.Width) / v, random.[Next](rect.Height) / v), New Drawing.PointF(rect.Width - random.[Next](rect.Width) / v, random.[Next](rect.Height) / v), New Drawing.PointF(random.[Next](rect.Width) / v, rect.Height - random.[Next](rect.Height) / v), New Drawing.PointF(rect.Width - random.[Next](rect.Width) / v, rect.Height - random.[Next](rect.Height) / v)}
        Dim matrix As New Drawing.Drawing2D.Matrix()
        matrix.Translate(0.0F, 0.0F)
        path.Warp(points, rect, matrix, Drawing.Drawing2D.WarpMode.Perspective, 0.0F)

        ' Draw the text.
        hatchBrush = New Drawing.Drawing2D.HatchBrush(Drawing.Drawing2D.HatchStyle.DashedUpwardDiagonal, Drawing.Color.DarkGray, Drawing.Color.Black)
        g.FillPath(hatchBrush, path)

        ' Add some random noise.
        Dim m As Integer = Math.Max(rect.Width, rect.Height)
        For i As Integer = 0 To CInt(Math.Truncate(rect.Width * rect.Height / 30.0F)) - 1
            Dim x As Integer = random.[Next](rect.Width)
            Dim y As Integer = random.[Next](rect.Height)
            Dim w As Integer = random.[Next](m \ 50)
            Dim h As Integer = random.[Next](m \ 50)
            g.FillEllipse(hatchBrush, x, y, w, h)
        Next

        ' Clean up.
        font.Dispose()
        hatchBrush.Dispose()
        g.Dispose()

        Dim captchaImageResult As FileContentResult = Nothing
        Using mystream As New System.IO.MemoryStream()

            bitmap.Save(mystream, System.Drawing.Imaging.ImageFormat.Jpeg)
            captchaImageResult = MyBase.File(mystream.GetBuffer, "image/jpeg")

        End Using
        bitmap.Dispose()

        'Save the Captcha Hash in the Session
        Session("botdeflector") = Convert.ToBase64String(System.Security.Cryptography.MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(captchaString)))

        'Return the Captcha Image
        Return captchaImageResult

    End Function

what is mostly happening here is the generation of the image, pasting in the Text that you want for the Captcha image, adding some noise, and in the end we save the hash for the generated text, this is what we will use later to verify that the user submitted the correct string of text, the most important part is saving the hash to the session state.

change the index page a little, adding a new form, an image tag with its source set as the captcha image, and a submit button.

Index.vbhtml

@Code
    ViewData("Title") = "Home Page"
End Code
<h2>@ViewData("Message")</h2>

@Using Html.BeginForm

    @<div>
        <img src='@Url.Action("GetCaptcha")' alt='Captcha' />
        <input type="text" size="10" name="CaptchaAnswer" autocomplete="off" />
        <br />
        <input type="submit" value="Submit"/>
    </div>

End Using


adding a new POST only index Action method

HomeController.vb

Index Action (POST)


      <HttpPost()>
      Function index(CaptchaAnswer As String) As ActionResult

        Dim captchaHash = Session("botdeflector")

        If captchaHash = Convert.ToBase64String(System.Security.Cryptography.MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(UCase(CaptchaAnswer)))) Then

            'Valid
            ViewData("Message") = "YOU ARE A PERSON :>"

        Else
            'Invalid
            ViewData("Message") = "YOU ARE A ROBOT!"

        End If

        Return View()

     End Function

the validation begins with converting the Captcha Answer the user supplied to it’s hash equivalent, then we get our saved hash from the current user session, compare them and return the result.

here is the complete project if you want it. and you can also use the following helper functions for random generation of strings or numbers

Generate Random Number Function

 Private Function generateRandomNumber(ByVal size As Integer) As String

        Dim builder As New StringBuilder()
        Dim random As New Random(DateTime.Now.Millisecond)
        Dim ch As Char
        For i As Integer = 0 To size - 1
            ch = random.Next(9).ToString
            builder.Append(ch)
        Next
        Return builder.ToString()

    End Function

Generate Random String Function

   Private Function generateRandomString(ByVal size As Integer) As String

        Dim builder As New StringBuilder()
        Dim random As New Random(DateTime.Now.Millisecond)
        Dim ch As Char
        For i As Integer = 0 To size - 1
            ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)))
            builder.Append(ch)
        Next
        Return builder.ToString()

    End Function

 
Leave a comment

Posted by on 3, Nov 2011 in .NET, ASP.NET, MVC, VB.NET

 

Tags: ,

SEO Friendly Slug URLs Extension Method

Here is a quick Extension Method that you can use to generate a friendly string for use in a URL.

Function ToFriendlyUrl(txt As String, Optional maxLength As Integer = 45) As String
If String.IsNullOrWhiteSpace(txt) Then

Return String.Empty
Else
Dim str As String = RemoveAccent(txt).ToLower()

str = RegularExpressions.Regex.Replace(str, "[^a-z0-9\s-]", String.Empty)
str = RegularExpressions.Regex.Replace(str, "\s+", " ").Trim()
str = str.Substring(0, If(str.Length str = RegularExpressions.Regex.Replace(str, "\s", "-")

Return str

End If

End Function

we use Regular expressions to only allow letters and dashes, so now you can create SEO Friendly URL for your site just by using this extension method on any URL String.

 
Leave a comment

Posted by on 10, Oct 2011 in .NET, ASP.NET, MVC, VB.NET

 

Tags: , , ,

Change Shared or Views folder location in an MVC 3 site

if you want to change the location of the shared folder in your MVC 3 App, then this is a must read.

when you create a new MVC 3 Web Application Project, a default folder structure is created for you that looks something like this

01

to change the shared folder location you must do the following steps

Move the shared folder to the Root

just drag the shared folder out of the views folder to the root of your app, like so

02

Copy the web.config

in order for the shared folder to work outside the views folder, it has to have a special web.config file within it. just copy the one inside the views folder and paste it into the shared folder.

03

Edit the _ViewStart.vbhtml page

now that the _layout page has changed its locations, we have to modify the _ViewStart.vbhtml page located in the Views folder, and edit the following

04

remove the “/Views” so now it directs to the _layout page located in the new shared folder location.

05

Code a custom view engine

if you tried to browse the site now you will get an error like this one

06

that is happening because MVC is using the default locations for the partial views, so its still looking for the _LogOnPartial.vbhtml in its old location, but you moved that file to a new location in the root of your app.

to change the default file location for the whole site we need to create a new class that inherits from RazorViewEngine


Public Class MyCustomViewEngine
    Inherits RazorViewEngine

    Sub New()

        MasterLocationFormats = New String() {"~/Shared/{0}.vbhtml"}

        ViewLocationFormats = New String() {"~/Views/{1}/{0}.vbhtml", "~/Shared/{0}.vbhtml"}

        PartialViewLocationFormats = New String() {"~/Views/{1}/{0}.vbhtml", "~/Shared/{0}.vbhtml"}

    End Sub

End Class

 

here we set new locations for the Master, Views, and PartialViews

Replace the default view engine and test

all we have to do now is plugin this new custom engine, go to the Global.asax file and add the following to the Application_Start()


  Sub Application_Start()
        AreaRegistration.RegisterAllAreas()

        ViewEngines.Engines.Clear()
        ViewEngines.Engines.Add(New MyCustomViewEngine)

        RegisterGlobalFilters(GlobalFilters.Filters)
        RegisterRoutes(RouteTable.Routes)
    End Sub

now if you run the app you will notice that it all works with the shared folder new location.

Summary

we managed to change the shared folder location and replace the default razor view engine with our own implementation that changes the locations of the master, views, and partial views of the app.

 
2 Comments

Posted by on 1, Aug 2011 in .NET, ASP.NET, MVC, VB.NET

 

Tags: , ,

Google Static Maps HTML Helper

one of the features that i like the most in ASP .NET MVC is HTML Helpers, i use them all the time, and i also use Google Maps very often too, so its natural to join them and create a simple HTML Helper Extension Method that you can use immediately in your applications.

Google Maps provides an API for generating map images from parametrized URLs called Google Static Maps API, so today we will create an Extension Method to the HtmlHelper class that you can use to easily embed a Google Static Map into any View Page.

we will start with some text from the API docs.

Google Static Maps API Query string parameters

Name requirement Description
center required
if markers not present
defines the center of the map
zoom required
if markers not present
Zoom levels
between 0 (the lowest zoom level) to 21+ (down to individual buildings)
size required defines the rectangular dimensions of the map image
format optional defines the format of the resulting image (png8,png,png32,gif,jpg,jpg-baseline)
maptype optional defines the type of map to construct (roadmap,satellite,terrain,hybrid)
language optional defines the language to use for display of labels on map tiles
markers optional define one or more markers to attach to the image at specified locations, more info
path optional defines a single path of two or more connected points to overlay on the image at specified locations, more info
visible optional specifies one or more locations that should remain visible on the map
style optional defines a custom style to alter the presentation of a specific feature (road, park, etc.) of the map, more info
sensor required specifies whether the application requesting the static map is using a sensor to determine the user’s location, more info

create a new MVC 3 Web application and add the following module into your Models folder

Google Static Map Extension Method (VB .NET)


Public Module HtmlHelpers

    <Runtime.CompilerServices.Extension()>
    Function GoogleStaticMap(helper As HtmlHelper,
                             title As String,
                             latitude As Decimal,
                             longitude As Decimal,
                             width As Integer, height As Integer,
                             Optional zoom As Integer = 15,
                             Optional language As String = "en") As MvcHtmlString

        'Google Static Map API - Base URL
        Dim googleApiUrl = "http://maps.googleapis.com/maps/api/staticmap?sensor=false"

        'a string builder for quicker text manipulation
        Dim url = New System.Text.StringBuilder

        'Setup URL with all required query strings

        'Base Url
        url.Insert(0, googleApiUrl)

        'Language
        url.Append("&language=")
        url.Append(language)

        'Zoom
        url.Append("&zoom=")
        url.Append(zoom.ToString)

        'Size
        url.Append("&size=")
        url.Append(width.ToString)
        url.Append("x")
        url.Append(height.ToString)

        'Markers "latitude,longitude"
        url.Append("&markers=")
        url.Append(latitude.ToString)
        url.Append(",")
        url.Append(longitude.ToString)

        'Create an <img src="URL" alr="title" />
        Dim imgTag = New TagBuilder("img")

        imgTag.MergeAttribute("src", url.ToString)
        imgTag.MergeAttribute("style", "border:1px solid black;")
        imgTag.MergeAttribute("alt", title)

        'Return the Encoded HTML
        Return MvcHtmlString.Create(imgTag.ToString(TagRenderMode.SelfClosing))

    End Function

End Module

Explain The Code

using a string builder we construct the final parameterized URL, notice that the markers parameter is of format “latitude,longitude”, then a TagBuilder is used to create our IMG tag with the attributes required to show a Google Static Map, as IMG tags are self-closing we select the self-closing TagRenderMode and return the final encoded html with MvcHtmlString.Create method.

that’s it, we create the URL, and then create an IMG tag with that URL as source.

How to use it

in your Views/Home folder, open the Index.vbhtml page and use the following code.


@Code
    ViewData("Title") = "Index"
End Code

<h2>Google Static Maps - Example</h2>

@Html.GoogleStaticMap("My First Map", 12.250151, 64.33717, 550, 500)

Enhancements

  • Geocoding feature
  • show multiple location markers
  • use map styles to change the appearance of the map
  • use marker styles to change the appearance of the marker

Summary

there are just too many features available in Google Maps API, we talked here about a quick and portable implementation that can quickly and easily fit into any project with a “Contact Us” page that needs a map, I hope that this can help you start your own Advanced Google Maps HTML Helper.

 
1 Comment

Posted by on 23, Jul 2011 in .NET, ASP.NET, MVC, VB.NET

 

Tags: , , , , ,