• Enhancing RESTful WebAPI controllers with RPC style endpoints

    Enhancing RESTful WebAPI controllers with RPC style endpoints

    During the setup stage of the new project I’m working on, the decision was made to try and use RESTful WebAPI controllers that would support RPC style endpoints as well. I did a bit of research and found a nice post from Carolyn Van Slyck that explains how this can be achieved by creating a few different routing rules in the WebApiConfig file. I wasn’t however fully satisfied with this approach so I tried to do it in a different way.

    If you follow .net’s WebAPI conventions, you can simply write action methods that start with http verbs (GET, POST, PUT, DELETE) and everything will work out of the box with the default WebApiConfig setup. For example you can name your RESTful action methods something like GetAllProducts, GetProduct, PostProduct, etc… No extra action route attributes (such as RoutePrefix, Route, HttpGet/Post/Put/Delete) are needed for this approach. WebAPI will in this case expect the correct http verb.

    However, as soon as you add a custom action, it will start causing problems (you will start getting the infamous “Multiple actions were found that match the request” response). Say you add CustomGetEndpoint method – this will cause GetAllProducts and GetProduct to not work any more. Luckily by adding a few things we can make it work.

    The first step is to enable attribute routing in your WebApiConfig:

    The second step is to add the RoutePrefix to your controller and to add the Route and http verb attributes to all your custom RPC actions (they of course don’t have to start with “custom” but you can name them whatever you want):

    In this case ProductModel is very simple:

    Keep in mind that if you are using a BaseApiController class which inherits .net’s ApiController, ensure that all your methods are protected. If you make your BaseApiController methods public, this will mess up the routing and you will start getting the “Multiple actions were found that match the request” response (took me long enough to figure that one out!).

    That’s all folks, you can now enjoy both worlds at the same time. Enjoy!

  • Automatic WebAPI property casing serialization

    Automatic WebAPI property casing serialization

    If you work a lot with WebAPI’s and JavaScript and would like to follow the convention of lowerCamelCasing your JSON and UpperCamelCasing your .net model properties, you can do that by using Newtonsoft.Json CamelCasePropertyNamesContractResolver. This works for both directions “WebAPI -> client” and “client -> WebAPI” so that’s cool as well. You should simply set this up in your Global.asax and you’re good to go.

    Well that was an easy one. It was also my chance to try out the Gist GitHub ShortCode plugin. I think it works great, and it’s easier to use. I like it! And if you’re using WP, you should try it as well. Hopefully if won’t take me too long to migrate everything from the Code Colorer plugin that I’ve been using so far. :)

  • Secret Arcade Jam – FireWallCade

    Secret Arcade Jam – FireWallCade

    So, last weekend a Secret Arcade Jam was held, organized by Erik Svedäng for his else Heart.break() game. The goal was to create a mini-game that can be run on computer terminals inside the game. Erik created a simple Ruby-inspired programming language called Sprak which is used to code the mini-games. What you get inside the game is a terminal with an editor (it even has syntax-highlighting and everything!), compiler and runner to try your code out..

    This all sounded like a whole lot of awesomesauce to my friend Dalibor and me, so we decided to give it a go and try and make something. After two days of (haaard) work, we had our own mini game – *drumroll* FireWallCade. It’s a very simple game. You have good and bad “network packets” (green and red blocks) falling down from the top of the screen at increasing speed. Then there are two “ports” at the bottom which can be opened and closed by using left and right keys. The goal here is to block the bad packets and to let the good ones pass through. We even created a splash screen in ASCII, a menu and a GameOver screen. Here are a few screenshots..

    else Heart.break() shell

    ehb-screenshot-1

    code editor (very cool!)

    ehb-screenshot-2

    ASCIIIIIII splash

    ehb-screenshot-3

    about (SPACE handlers eveeerywheeereee)

    ehb-screenshot-4

    gameplay :)

    ehb-screenshot-5

    Well anyway, we won! :D People were able to vote on their favorite game and apparently we got over 40% of the votes. Thanks everyone! :)

    The prize for the first place was – your game ends up in the else Heart.break() itself. That means we now actually need to make it even better! Optimize it a bit, perhaps make it look a bit nicer as well, we’ll see.. I hope Eriks game ends up a good and interesting.

    Bye-bye-bye-bye-bye-byeeeeeee

    ehb-screenshot-6

  • Dynamically resolving function shared arguments in JavaScript

    Dynamically resolving function shared arguments in JavaScript

    Sometimes we have functions which expect the same arguments as other functions, all fine there. Sometimes these arguments are obtained/resolved asynchronously. If there are a lot of these functions that share the same resource, we can come up with lot of unnecessary boilerplate.

    Imagine having a function like resolveMetaData() which asynchronously obtains fresh data every time it’s called (to keep the code at a very simple level, for the purpose of this post I’ll be using setTimeout() instead of something a bit more complex like an AJAX call):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function resolveMetaData(callback) {
        // for the purpose of the demo we'll simply mock the metaData object    
        var metaData = { message: "Meta message", start: new Date() };

        // async business logic example
        setTimeout(function () {
            metaData.end = new Date();
            // after metaData is ready, resolve callback
            callback(metaData);
        }, 1000);
    }

    And two functions that require a new instance of metaData upon their execution:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function fnOne(data, metaData) {
        console.log(data);
        console.log(metaData);
    }

    function fnTwo(id, data, metaData) {
        console.log(id);
        console.log(data);
        console.log(metaData);
    }

    The simplest way to provide the latest metaData to these functions would be to use callbacks like this:

    1
    2
    3
    4
    5
    6
    7
    resolveMetaData(function(metaData) {
        fnOne("fnOne", metaData);
    });

    resolveMetaData(function(metaData) {
        fnOne(1, "fnTwo", metaData);
    });

    If you had to write a lot of functions similar to fnOne() and fnTwo() (ie. 10 or more) and all of them required the latest metaData, you would most probably be tempted to somehow reduce the code and get rid of the callback boilerplate. The first two ideas that came to my mind on how to resolve this were function overloads and/or having a base function that would handle metaData resolving. Since JS doesn’t really support overloading (in the same way as say C# does), having a base function to handle metaData resolving seems like a safe bet. The only question is – how do we call a function in JS with the parameters we got and resolve the shared parameters asynchronously?

    Fortunately Function.prototype.apply() comes to the rescue! It allows us to call a function with arguments as an array which is quite handy. Since functions in JS are objects, we can now create a base function which accepts the function object of the function we wish to call, and the args we have at that point. It then resolves metaData, appends it to the arguments array and calls the passed function with these arguments. This is how the base function would look like:

    1
    2
    3
    4
    5
    6
    function fnBase(fn, args) {
        resolveMetaData(function (metaData) {
            args.push(metaData);
            fn.apply(this, args);
        });
    }

    And this is the how we can now call fnOne() and fnTwo() through the fnBase():

    1
    2
    fnBase(fnOne, ["fnOne"]);
    fnBase(fnTwo, [1, "fnTwo"]);

    It would be possible to place metaData as a first argument in fnOne() and fnTwo() signatures but that would require additional args position handling in fnBase() so it is probably best to put metaData as the last argument.

    That’s it, hope it helps. Enjoy! :)

  • Returning value from fragment into parent activity on Android

    Returning value from fragment into parent activity on Android

    This post will show you how to return a value set in a dialog fragment back to the parent activity it was called from. Code sample provided here is from the Auto-WOL app I made not so long ago, so if you need the bigger picture, feel free to check TimePickerFragment and DeviceActivity classes over at github. This example uses TimePickerDialog. It also keeps track of the layoutId (where the layout it is bound to acts as a button in the UI) because in my app I have multiple buttons which can invoke the same TimePickerFragment so I needed to know which button invoked the dialog fragment because upon selecting the time the button caption gets updated.

    auto-wol-fragment-activity

    So, to make this whole thing work, we will need a fragment class which will extend DialogFragment and implement TimePickerDialog.OnTimeSetListener. This class will expose the OnTimePickedListener interface so it could later be consumed by the parent activity. This class will also be responsible for registering and calling the callback on time set event. The defined OnTimePickedListener interface has only one method signature – onTimePicked(), which will need to be implemented by the parent activity. Other than this, the class only has a few more overrides and that’s all.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    /**
     * This class is used so values from TimePickerFragment could be
     * returned back to the activity from which it was called.
     */

    public class TimePickerFragment extends DialogFragment implements TimePickerDialog.OnTimeSetListener {
        OnTimePickedListener mCallback;
        Integer mLayoutId = null;

        /**
         * An interface containing onTimePicked() method signature.
         * Container Activity must implement this interface.
         */

        public interface OnTimePickedListener {
            public void onTimePicked(int textId, int hour, int minute);
        }

        /* (non-Javadoc)
         * @see android.app.DialogFragment#onAttach(android.app.Activity)
         */

        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);

            try {
                mCallback = (OnTimePickedListener)activity;
            } catch (ClassCastException e) {
                throw new ClassCastException(activity.toString() + " must implement OnTimePickedListener.");
            }
        }

        /* (non-Javadoc)
         * @see android.app.DialogFragment#onCreateDialog(android.os.Bundle)
         */

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            mCallback = (OnTimePickedListener)getActivity();

            Bundle bundle = this.getArguments();
            mLayoutId = bundle.getInt("layoutId");
            int hour = bundle.getInt("hour");
            int minute = bundle.getInt("minute");

            // Create a new instance of TimePickerDialog and return it
            return new TimePickerDialog(getActivity(), this, hour, minute, DateFormat.is24HourFormat(getActivity()));
        }

        /* (non-Javadoc)
         * @see android.app.TimePickerDialog.OnTimeSetListener#onTimeSet(android.widget.TimePicker, int, int)
         */

        public void onTimeSet(TimePicker view, int hour, int minute) {
            if(mCallback != null)
            {
                mCallback.onTimePicked(mLayoutId, hour, minute);
            }
        }
    }

    The parent activity class implements the OnTimePickedListener defined in TimePickerFragment. This means we are requried to override the onTimePicked() method. This method is what gets executed after time has been picked in the dialog pop-up and “Done” confirmation button has been pressed. From here you’re back in your activity class and can do whatever you want with the obtained data.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class DeviceActivity extends BaseActivity implements OnTimePickedListener {    
        ...
       
        /**
         * On time picked event, converts hour and minutes values to milliseconds
         * milliseconds and sets a new value for the layout in the activity.
         * @param layoutId      QuietHoursFrom or QuietHoursTo layout Id.
         * @param hour          Hour value.
         * @param minute        Minutes value.
         */

        @Override
        public void onTimePicked(int layoutId, int hour, int minute) {
            Long timeInMillis = TimeUtil.getTimeInMilliseconds(hour, minute);
           
            // Here you can do whatever needed with value obtained from the fragment
        }
       
        ...
    }
  • Android EditText automatic MAC address formatting

    Android EditText automatic MAC address formatting

    I recently made a simple Android Wake-on-Lan app which includes an EditText MAC address input field. To make it easier for users to type in proper MAC’s (not having to type colons manually) I used TextWatcher to automatically format the input on text-changed event. This stackoverflow topic gave me a general idea on how to do it. What I wanted is for the application to insert a colon after every second character, remove the character preceding a colon if the user deletes the colon and to handle MAC address editing. For example if the user takes out a few characters from the middle of the string, the formatting should still be preserved and the cursor should stay in the right place and not go to the end or beginning of the string.

    The only callback I used was onTextChanged(). On each text-change event, users input gets stripped of all non-MAC characters (this means only numbers 0-9 and letters A-F are left), then the colons are added and after that (potential) character deletion and cursor positioning are handled. Here is the code I came up with in the end:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    private EditText mMacEdit = (EditText)findViewById(R.id.edit_mac);

    /**
     * Registers TextWatcher for MAC EditText field. Automatically adds colons,  
     * switches the MAC to upper case and handles the cursor position.
     */

    private void registerAfterMacTextChangedCallback() {
        mMacEdit.addTextChangedListener(new TextWatcher() {
            String mPreviousMac = null;

            /* (non-Javadoc)
             * Does nothing.
             * @see android.text.TextWatcher#afterTextChanged(android.text.Editable)
             */

            @Override
            public void afterTextChanged(Editable arg0) {
            }

            /* (non-Javadoc)
             * Does nothing.
             * @see android.text.TextWatcher#beforeTextChanged(java.lang.CharSequence, int, int, int)
             */

            @Override
            public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
            }

            /* (non-Javadoc)
             * Formats the MAC address and handles the cursor position.
             * @see android.text.TextWatcher#onTextChanged(java.lang.CharSequence, int, int, int)
             */

            @SuppressLint("DefaultLocale")
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                String enteredMac = mMacEdit.getText().toString().toUpperCase();
                String cleanMac = clearNonMacCharacters(enteredMac);
                String formattedMac = formatMacAddress(cleanMac);

                int selectionStart = mMacEdit.getSelectionStart();
                formattedMac = handleColonDeletion(enteredMac, formattedMac, selectionStart);
                int lengthDiff = formattedMac.length() - enteredMac.length();

                setMacEdit(cleanMac, formattedMac, selectionStart, lengthDiff);
            }

            /**
             * Strips all characters from a string except A-F and 0-9.
             * @param mac       User input string.
             * @return          String containing MAC-allowed characters.
             */

            private String clearNonMacCharacters(String mac) {
                return mac.toString().replaceAll("[^A-Fa-f0-9]", "");
            }

            /**
             * Adds a colon character to an unformatted MAC address after
             * every second character (strips full MAC trailing colon)
             * @param cleanMac      Unformatted MAC address.
             * @return              Properly formatted MAC address.
             */

            private String formatMacAddress(String cleanMac) {
                int grouppedCharacters = 0;
                String formattedMac = "";

                for (int i = 0; i < cleanMac.length(); ++i) {
                    formattedMac += cleanMac.charAt(i);
                    ++grouppedCharacters;

                    if (grouppedCharacters == 2) {
                        formattedMac += ":";
                        grouppedCharacters = 0;
                    }
                }

                // Removes trailing colon for complete MAC address
                if (cleanMac.length() == 12)
                    formattedMac = formattedMac.substring(0, formattedMac.length() - 1);

                return formattedMac;
            }

            /**
             * Upon users colon deletion, deletes MAC character preceding deleted colon as well.
             * @param enteredMac            User input MAC.
             * @param formattedMac          Formatted MAC address.
             * @param selectionStart        MAC EditText field cursor position.
             * @return                      Formatted MAC address.
             */

            private String handleColonDeletion(String enteredMac, String formattedMac, int selectionStart) {
                if (mPreviousMac != null && mPreviousMac.length() > 1) {
                    int previousColonCount = colonCount(mPreviousMac);
                    int currentColonCount = colonCount(enteredMac);

                    if (currentColonCount < previousColonCount) {
                        formattedMac = formattedMac.substring(0, selectionStart - 1) + formattedMac.substring(selectionStart);
                        String cleanMac = clearNonMacCharacters(formattedMac);
                        formattedMac = formatMacAddress(cleanMac);
                    }
                }
                return formattedMac;
            }

            /**
             * Gets MAC address current colon count.
             * @param formattedMac      Formatted MAC address.
             * @return                  Current number of colons in MAC address.
             */

            private int colonCount(String formattedMac) {
                return formattedMac.replaceAll("[^:]", "").length();
            }

            /**
             * Removes TextChange listener, sets MAC EditText field value,
             * sets new cursor position and re-initiates the listener.
             * @param cleanMac          Clean MAC address.
             * @param formattedMac      Formatted MAC address.
             * @param selectionStart    MAC EditText field cursor position.
             * @param lengthDiff        Formatted/Entered MAC number of characters difference.
             */

            private void setMacEdit(String cleanMac, String formattedMac, int selectionStart, int lengthDiff) {
                mMacEdit.removeTextChangedListener(this);
                if (cleanMac.length() <= 12) {
                    mMacEdit.setText(formattedMac);
                    mMacEdit.setSelection(selectionStart + lengthDiff);
                    mPreviousMac = formattedMac;
                } else {
                    mMacEdit.setText(mPreviousMac);
                    mMacEdit.setSelection(mPreviousMac.length());
                }
                mMacEdit.addTextChangedListener(this);
            }
        });
    }

    One important thing to note is the removal and re-adding of TextChangedListener in the setMacEdit() method. Without this part the code results in stack-overflow since every setText() triggers the TextChangedListener over and over again.

    Hope it helps, enjoy! :)

Back to top