Browser back button triggers previous post action in .NET webform application - solved

Recently while working on one of my .NET application I was stuck in a strange problem. I created a button and its click event to insert some entry in database and at the end of this action I used below statement to redirect the page for next step.

Response.Write("<script>top.location="next step page url";</script>");


Note: I used this method to redirect the page because I was doing it in an iframe and wanted to redirect parent page after this step is completed and next step is started.

This was working perfectly fine but after moving to next step when I clicked browser back button same action was again fired and I got two similar entries in database. This was strange because I never exacted post event to be actually fired on browser back button because browser used to show a message when there is any post action is required (like we tap browser refresh button after any post action).

After some hit and trial I found that the statement above (Response.Write) is causing the issue. This was because the post action was still in progress because I used a trick to redirect instead of simple Response.Redirect.

To complete the post event and redirect properly to resolve the page postback issue on browser back button I used below approach in place of above like of statement:

Session["next"] = true
Response.Write("<script>top.location=top.location;</script>");


On parent page I checked if this session exists then redirect to next step

if(Session["next"] != null && Convert.ToBoolean(Session["next"]))
{
     Response.Redirect("next step page url");
}


After this approach when I clicked browser back button there was not issue of postback.

So the conclusion was that Response.Write was actual reason of the issue because it was breaking the flow without completing the post action.

Download Visual Studio 2017 ISO (offline installer) + Web Installer

This is a simple guide to download offline installer or ISO file for Visual Studio 2017.

As you all new Microsoft always provides web installer file for all new releases of Visual studio and on 7th March Microsoft has launched Visual Studio 2017 and this time also they haven't provided offline installer or ISO file of the setup so we are here to explain how you can obtain offline installer or ISO file from this web installer EXE.

There are 7 simple steps for doing this:

1. Download web installer exe from this link

2. After download put this file in any folder like C:\VS 2017.

3. Open command prompt with admin access and move to your visual studio eve folder using command cd c:\CS 2017

4. Now run another command in command prompt vs_setup.exe /layout (vs_setup.exe is your file name so you can write what ever is your file name)

5. This will open a windows to ask location on your drive where you would like to download the visual studio offline / ISO installer.

6. Now it will download your visual studio and after it is completed you can run setup where you have selected to download it and that too without internet connection.


Visual studio 2017 will be launched on 7th March 2017

Finally Microsoft is going to launch Visual studio 15 (Visual studio 2017) on 7th March.

This version of Visual Studio will be launched at an event which will be organised on 7th and 8th March. This will also be a celebration event for 20th Anniversary of Visual Studio.

Day 1 (7th March 2017) of the event will include Keynote and demos by Julia Liuson, Brian Harry, Miguel de Icaza, and Scott Hanselman which can also be viewed on live streaming. They will show new things in Visual Studio, .NET, Xamarin, Azure etc.

Day 2 (8th March 2017) will be live interactive training day to teach how to be more productive using the all new Visual Studio 2017.
If you are interested in this event then you can register on this link https://launch.visualstudio.com

Apart from this live event there will be launch event by the Visual Studio community all around the world where developers can meet and collaborate in their local area.

You can even share a short story, video clip or image of your visual studio story. Share your story on Instagram, Twiter or Facebook with #MyVSStory

Encryption and Decryption of content using RijndaelManaged Class in C#

If you want to encrypt and decrypt data in your .NET application then one of the most simplest and safest way is using RijndaelManaged class.

It is predecessor of Aes but still most of the new users us RijndaelManaged class because it is easy to plug and use.

First we have to generate an encryption key using a password ans salt. Password and salt could be any string (use base64 string to make your encryption stronger) which we have to convert to bytes array (byte[]) before using them for generating encryption key. Below is the complete example of generating key and encryption and decryption

        public static byte[] GenerateEncryptionKey(string password, string saltString)
        {
            byte[] salt = Convert.FromBase64String(saltString);
            var keyGen = new Rfc2898DeriveBytes(password, salt, 7845);
            return keyGen.GetBytes(32);
        }

        public static string Encrypt(object data, string password, string saltString)
        {
            try
            {
                using (var aes = new RijndaelManaged())
                {
                    //get some bytes
                    aes.Key = GenerateEncryptionKey(password, saltString);
                    aes.BlockSize = 256;
                    aes.Padding = PaddingMode.PKCS7;

                    //generate an IV
                    aes.GenerateIV();

                    //get the bytes for our message
                    var plainBytes = Encoding.UTF8.GetBytes(data.ToString());

                    //start up the encryption
                    using (var ms = new MemoryStream())
                    using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        //write the bytes to the cryptostream
                        cs.Write(plainBytes, 0, plainBytes.Length);
                        cs.FlushFinalBlock();

                        //get message bytes
                        var msgBytes = ms.ToArray();

                        //create a new array big enough for the both of 'em
                        var cypherBytes = new byte[aes.IV.Length + msgBytes.Length];

                        //return the string with the iv as the first 32 bytes. will need this when decrypting
                        System.Buffer.BlockCopy(aes.IV, 0, cypherBytes, 0, aes.IV.Length);
                        System.Buffer.BlockCopy(msgBytes, 0, cypherBytes, aes.IV.Length, msgBytes.Length);

                        //now convert it to base64 string
                        var cypherText = System.Convert.ToBase64String(cypherBytes).TrimEnd(new char[] { '=' }).Replace('+', '-').Replace('/', '_');
                        //var cypherText = Convert.ToBase64String(cypherBytes);

                        //return cypher text
                        return cypherText;
                    }
                }
            }
            catch (Exception)
            {
                return null;
            }
        }

        public static string Decrypt(object cypherData, string password, string saltString)
        {
            try
            {
                using (var aes = new RijndaelManaged())
                {
                    //get some bytes
                    aes.Key = GenerateEncryptionKey(password, saltString);
                    aes.BlockSize = 256;
                    aes.Padding = PaddingMode.PKCS7;

                    //get the bytes for our message
                    string cypherStr = cypherData.ToString();
                    string incoming = cypherStr.Replace('_', '/').Replace('-', '+');
                    switch (cypherStr.Length % 4)
                    {
                        case 2: incoming += "=="; break;
                        case 3: incoming += "="; break;
                    }
                    var cypherBytes = Convert.FromBase64String(incoming);
                    //var cypherBytes = Convert.FromBase64String(cypherData.ToString());
                    var iv = new byte[aes.IV.Length];
                    var msgBytes = new byte[cypherBytes.Length - iv.Length];

                    //we use the first 32 bytes of the cypherdata for the IV
                    System.Buffer.BlockCopy(cypherBytes, 0, iv, 0, iv.Length);
                    System.Buffer.BlockCopy(cypherBytes, iv.Length, msgBytes, 0, msgBytes.Length);

                    //set the IV for the instance
                    aes.IV = iv;

                    //start up the decryption
                    using (var ms = new MemoryStream())
                    using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        //write the bytes to the cryptostream
                        cs.Write(msgBytes, 0, msgBytes.Length);
                        cs.FlushFinalBlock();

                        //the plain text has been decrypted.
                        var plainText = System.Text.Encoding.UTF8.GetString(ms.ToArray());

                        //return plain text
                        return plainText;
                    }
                }
            }
            catch (Exception)
            {

                return null;
            }
        } 

Sample Code - Android share action with chooser to display system default share chooser dialog

This post is for helping those you are looking to implement share feature in there android application and ending up with default ShareActionProvider which is too basic and doesn't look good at all.

Solution to this problem is "chooser". below is the sample of how you can share any data with ACTION_SEND intent by setting text and subject and at the end call start activity with createChooser.


            Intent sendIntent = new Intent();
            sendIntent.setAction(Intent.ACTION_SEND);

            String text = "";
            String url = "";
            sendIntent.putExtra(Intent.EXTRA_SUBJECT, text);
            sendIntent.putExtra(Intent.EXTRA_TEXT, text + " \n\n" + url);
            sendIntent.setType("text/plain");
            context.startActivity(Intent.createChooser(sendIntent, "Share"));


There are many advantages of using chooser
  • Even if you has selected any default application of this action your application will share chooser dialog so that user can select any other application without going to settings
  • You have option to set title for your share action chooser dialog
  • If no matching application found (I don't think if this will happen to anyone in real world) then android displays a system message.

Send FCM data message & notification to Android, iOS & Web in PHP

In our previous article we learnt how to send FCM data message notification from your server to FCM server using C#. Now for those you want to use PHP instead of C# to send FCM notification we have prepared a fairly simple class.

You just need to follow below four steps and you are done.
  1. Create an object of fcm class
  2. Call setIds method (passing array of registration ids) to set registration ids of your devices
  3. Call setData method to set your notification title, message and id.
  4. Call send method
class fcm
{
    private $url = "https://fcm.googleapis.com/fcm/send";
    private $ids = array();
    private $data = array();

    public function setIds($ids)
    {
        $this->ids = $ids;
    }

    public function send()
    {
        if (empty($this->ids)) {

            return "no id";
        }

        $post = array(
            'registration_ids'  => $this->ids,
            'data'              => $this->data,
        );
        $headers = array(
            'Authorization: key=' . GOOGLE_API_KEY,
            'Content-Type: application/json'
        );
     
        $ch = curl_init();

        curl_setopt( $ch, CURLOPT_URL, $this->url);
        curl_setopt( $ch, CURLOPT_POST, true);
        curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode($post));

        // Disabling SSL Certificate support temporarly
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

        $result = curl_exec($ch);
        if (curl_errno($ch)) {
            $error = curl_error($ch);
            return $error;
        }

        curl_close($ch);

        $obj = json_decode($result, true);

        return "sent on: " . $obj['success'] . " devices";
    }

    public function setData($title, $msg, $id)
    {
        $this->data = array("title" => $$title,
                            "msg" => $msg,
                            "id" => $id);
    }
}

Send FCM data message & notification to Android, iOS & Web in C#

In my previous article I have explained about types of notification messages in FCM. We have already learnt how to send GCM notification using C#. Now because FCM is replacing GCM so here we are with new sample of working with FCM in C#.


public class NotificationManager
{
    private class NotificationMessage
    {
        public string Title;
        public string Message;
        public long ItemId;
    }

    public NotificationManager()
    {
        //
        // TODO: Add constructor logic here
        //
    }

    public string SendNotification(List<string> deviceRegIds, string message, string title, long id)
    {

        string SERVER_API_KEY = "";
        var SENDER_ID = "";
        string regIds = string.Join("\",\"", deviceRegIds);

        NotificationMessage nm = new NotificationMessage();
        nm.Title = title;
        nm.Message = message;
        nm.ItemId = id;

        var value = new JavaScriptSerializer().Serialize(nm);

        WebRequest tRequest;
        tRequest = WebRequest.Create("https://fcm.googleapis.com/fcm/send");
        tRequest.Method = "post";
        tRequest.ContentType = "application/json";
        tRequest.Headers.Add(string.Format("Authorization: key={0}", SERVER_API_KEY));

        tRequest.Headers.Add(string.Format("Sender: id={0}", SENDER_ID));

        string postData = "{\"collapse_key\":\"score_update\",\"time_to_live\":108,\"delay_while_idle\":true,\"data\": { \"message\" : " + value + ",\"time\": " + "\"" + System.DateTime.Now.ToString() + "\"},\"registration_ids\":[\"" + regIds + "\"]}";

        Byte[] byteArray = Encoding.UTF8.GetBytes(postData);
        tRequest.ContentLength = byteArray.Length;

        Stream dataStream = tRequest.GetRequestStream();
        dataStream.Write(byteArray, 0, byteArray.Length);
        dataStream.Close();

        WebResponse tResponse = tRequest.GetResponse();

        dataStream = tResponse.GetResponseStream();

        StreamReader tReader = new StreamReader(dataStream);

        String sResponseFromServer = tReader.ReadToEnd();

        tReader.Close();
        dataStream.Close();
        tResponse.Close();
        return sResponseFromServer;
    }
}


You can see that only difference between GCM and FCM code is the request url where we have to post our data and yes this is the only difference between GCM and FCM except another notification message.

Above code is data notification message which is also called payload message. FCM provide simple notification message also which we don't need to handle in our code in client app or we can say we can not handle because FCM will automatically catches it display on our app's behalf. Upon clicking on this message it will open your app.

To send this kind of notification we simply need to replace data part of our message json (see string variable postData) with new notification json which should be like below:

{
    "to" : "yourclientregistrationid...",
    "notification" : {
      "body" : "notification body",
      "title" : "notification title",
      "icon" : "displayicon"
    }
}

Send notification on Android, iOS and Web using FCM

Few month before Google has launched its new cloud messaging platform Firebase Cloud messaging which will replace its current cloud messaging system Google Cloud Messaging. Gogle has not yet deprecated GCM but it has stopped support on this platform and suggested all developers to use FCM from now onward.

Although google has provided interface to send FCM notification from its console itself but there are some limitations. Actually google has divided notification system in two types:

  1. Notification message
  2. Data messages

Notification Message is simple message which contains a text which will be displayed in the notification area of the device along with some other fields like notification title and display icon. We don't need to handle them on client app. FCM will catch and display it automatically. This type of message can be sent directly from FCM Console. Just select your project and go to notification tab.

A notification message will look like this:


{
    "to" : "yourclientregistrationid...",
    "notification" : {
      "body" : "notification body",
      "title" : "notification title",
      "icon" : "displayicon"
    }
}


Data Messages are more powerful where we can send any data we want to send in json format. Maximum payload for data messaged could be 4KB. This type of message need to be send via your own code.

A data message will look similar to this:


{
    "to" : "yourclientregistrationid...",
    "data" : {
      "field1" : "your content",
      "field2" : "your content",
      "field3" : "your content"
    }
}


You can see the notification is replaced by data and unlike notification there can be any number of field with any name which you can handle in your client app code.

In next Article we will discuss how to send data notification in C# and PHP

Adding Share Action on Android Application ActionBar (appcompat v7)

Adding share action on android application action bar is simple and straight forward.

There are mainly 3 steps that you have to follow:

1. Add Share Action Item in your menu

To add share action item in your menu simply add below item in your menu layout:

    <item
        android:id="@+id/menu_item_share"
        app:showAsAction="ifRoom"
        android:title="Share"
        app:actionProviderClass="android.support.v7.widget.ShareActionProvider" />

And don't forget to add res-auto schema on your menu. Your final menu layout should contain below details:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <item
        android:id="@+id/menu_item_share"
        app:showAsAction="ifRoom"
        android:title="Share"
        app:actionProviderClass="android.support.v7.widget.ShareActionProvider" />
</menu>

You can set showAsAction to any other value like always, never, withText etc

2. Create ShareActionProvider in your activity

No you have to create ShareActionProvider in your activity where you want to show and handle action share event.

For that first declare ShareActionProvider object

    private ShareActionProvider mShareActionProvider;

override onCreateOptionMenu in your activity and add below code:

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate menu resource file.
        getMenuInflater().inflate(R.menu.menu_main, menu);

        // Locate MenuItem with ShareActionProvider
        MenuItem item = menu.findItem(R.id.menu_item_share);

        // Fetch and store ShareActionProvider
        mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item);

        setShareIntent();
        // Return true to display menu
        return true;
    }

Here menu_main should be your menu xml file name and menu_item_share is the id of share item added in the menu.

You many notice that we have called a method setShareIntent at the end just before returning. This is not an inbuilt method. We will define this in next step.

3. Set Share Intent

Here we will set the intent of the share action so that we can control what will be shared if user proceeds to go further with sharing.

    private void setShareIntent() {
        if (mShareActionProvider != null) {

            Intent myShareIntent = new Intent(Intent.ACTION_SEND);
            myShareIntent.setType("text/plain");

            myShareIntent.putExtra(Intent.EXTRA_SUBJECT, "");
            myShareIntent.putExtra(Intent.EXTRA_TEXT, "");

            mShareActionProvider.setShareIntent(myShareIntent);
        }
    }

You can specify different share intent type like in our case we have set text/plain because we are sharing plain text. Similarly you can set other type like view, images etc.

After that put extras based on based on your share intent type. For text/plain we have set subject and text.

Note: Whenever in your activity life cycle if your share content (subject and text in our case) is changed just call this method setShareIntent, no need to create ShareActionProvider again.

Deployment Failed: Unexpected Exception - AWS EBS deployment error

Recently I was trying to deploy my .NET project on AWS Elastic Beanstalk (EBS) using AWS Toolkit for Visual Studio.

Before this deployment attempt this same project was published on same EBS where I was just updating the project after making some changes. But unfortunately I was getting an error message (Deployment Failed: Unexpected Exception) every time I try to deploy the project on my AWS EBS instance and deployment gets failed hence EBS redeployed my previous deployed code again.

I searched for many days but none of the solution helped me resolve my issue and I started inspecting the changes I made between current and previous deployment.

After removing all the change I made in my aspx and cs paged I tried redeployment and it was really frustrating that I got same error (Deployment Failed: Unexpected Exception) and EBS again reverted my code to previous deployment.

Suddenly one thing strike in my mind that recently I updated AWS SDK in my project and after checked further I noticed that during updation process I mistakenly added an AWSRegion appSetting twice in my project web.config file.

I just removed that extra appSetting and keeping all the changes I made and tried redeployment, Voylla it was deployed successfully this time.

So this silly mistake cost my 2-3 days but all well that ends well. So never do such silly mistakes like me :)