Popular Posts
Enable SSL connection for Jsoup import org.jsoup.Connection; import org.jsoup.Jsoup; import javax.net.ssl.*; import java.io.IOException; import java.security.KeyManagement... Word break tag : <wbr/> (HTML5) The  HTML  <wbr>  tag  is  used  defines  a  potential  line  break  point  if  needed.  This  stands  for  Word  BReak. This  is  u... Build an OpenVPN server on android device Preparation An android device, in this case, Sony xperia Z is used Root permission required Linux Deploy for deploy i...
Blog Archive
Stats
ASP.NET MVC 5 Attribute Routing
Enable attribute route
Global.asax
public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        // Enable attribute route
        RouteTable.Routes.MapMvcAttributeRoutes();

        AreaRegistration.RegisterAllAreas();
        RouteConfig.RegisterRoutes(RouteTable.Routes);
    }
}
or
RouteConfig.cs
public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            
        // Enable attribute route
        routes.MapMvcAttributeRoutes();

        AreaRegistration.RegisterAllAreas();

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}
Direct route
public class BookController : Controller {

    // Map to 'http://prhythm.com.tw/Pet/10'
    [Route("Pet/{id}")]
    public ActionResult Pet(int? id) {
        return View();
    }

}
Optional paramter
public class BookController : Controller {

    // Optional parameter id
    // Map to 'http://prhythm.com.tw/Pet'
    //     or 'http://prhythm.com.tw/Pet/5'
    [Route("Pet/{id?}")]
    public ActionResult Pet(int? id) {
        return View();
    }

}
Default parameter value
public class BookController : Controller {
    
    // Default value of id
    // Map to 'http://prhythm.com.tw/Pet'
    //     or 'http://prhythm.com.tw/Pet/10'
    [Route("Pet/{id=10}")]
    public ActionResult Pet(int? id) {
        return View();
    }

}
Paramter constrains
public class BookController : Controller {
    
    // Paramters constrains
    [Route("Pet/{id:int}")]
    public ActionResult Pet(int? id) {
        return View();
    }

}
ConstraintDescriptionExample
alpha Matches uppercase or lowercase Latin alphabet characters (a-z, A-Z) {x:alpha}
bool Matches a Boolean value. {x:bool}
datetime Matches a DateTime value. {x:datetime}
decimal Matches a decimal value. {x:decimal}
double Matches a 64-bit floating-point value. {x:double}
float Matches a 32-bit floating-point value. {x:float}
guid Matches a GUID value. {x:guid}
int Matches a 32-bit integer value. {x:int}
length Matches a string with the specified length or within a specified range of lengths. {x:length(6)}
{x:length(1,20)}
long Matches a 64-bit integer value. {x:long}
max Matches an integer with a maximum value. {x:max(10)}
maxlength Matches a string with a maximum length. {x:maxlength(10)}
min Matches an integer with a minimum value. {x:min(10)}
minlength Matches a string with a minimum length. {x:minlength(10)}
range Matches an integer within a range of values. {x:range(10,50)}
regex Matches a regular expression. {x:regex(^\d{3}-\d{3}-\d{4}$)}
Route prefix
[RoutePrefix("Booking")]
public class BookController : Controller {

    // Map to 'http://prhythm.com.tw/Booking'
    //     or 'http://prhythm.com.tw/Booking/Pet'
    [Route]    
    [Route("Pet")]
    public ActionResult Pet() {
        return View();
    }

    // Map to 'http://prhythm.com.tw/Booking/Edit'
    [Route("Edit")
    public ActionResult Edit() {
        return View();
    }
}
Override route prefix
[RoutePrefix("Booking")]
public class BookController : Controller {

    // Override route prefix
    // Map to 'http://prhythm.com.tw/Pet'
    [Route("~/Pet")]
    public ActionResult Pet() {
        return View();
    }
}
Default route
[RoutePrefix("Booking")]
[Route("action=pet")]
public class BookController : Controller {

    // Map to 'http://prhythm.com.tw/Booking'
    //     or 'http://prhythm.com.tw/Booking/Pet'
    public ActionResult Pet() {
        return View();
    }

    // Map to 'http://prhythm.com.tw/Booking/Edit'
    public ActionResult Edit() {
        return View();
    }
}
Route area
[RouteArea("Backend")]
[RoutePrefix("News")]
public class NewsController : Controller {

    // Map to 'http://prhythm.com.tw/backend/news'
    public ActionResult Index() {
        return View();
    }

}
[RouteArea("Backend", AreaPrefix = "Admin")]
[RoutePrefix("News")]
public class NewsController : Controller {

    // Map to 'http://prhythm.com.tw/admin/news'
    public ActionResult Index() {
        return View();
    }

}

Reference http://sampathloku.blogspot.tw/2013/11/attribute-routing-with-aspnet-mvc-5.html

Reference http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx

Asynchronous and deferred JavaScript execution explained
  • Normal execution <script>
    This is the default behavior of the <script> element. Parsing of the HTML code pauses while the script is executing. For slow servers and heavy scripts this means that displaying the webpage will be delayed.
  • Deferred execution <script defer>
    Simply put: delaying script execution until the HTML parser has finished. A positive effect of this attribute is that the DOM will be available for your script. However, since not every browser supports defer yet, don’t rely on it!
  • Asynchronous execution <script async>
    Don’t care when the script will be available? Asynchronous is the best of both worlds: HTML parsing may continue and the script will be executed as soon as it’s ready. I’d recommend this for scripts such as Google Analytics.

Reference : http://peter.sh/experiments/asynchronous-and-deferred-javascript-execution-explained/

Reference : http://www.html5rocks.com/en/tutorials/speed/script-loading/

Integration config for spring & hibernate
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.prhythm.spring.web.controller"/>

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
        <property name="url" value="jdbc:hsqldb:mem:test"/>
        <property name="username" value="sa"/>
        <property name="password" value=""/>
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="packagesToScan" value="com.prhythm.spring.web.model"/>
        <!--<property name="annotatedClasses">-->
            <!--<list>-->
                <!--<value>com.prhythm.spring.web.model.Employee</value>-->
            <!--</list>-->
        <!--</property>-->
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.current_session_context_class">thread</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager"
          class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

</beans>
Wrong text encoding while Jsoup parse document

While page encoding is different with content type encoding declaration. Jsoup will get wrong text decode content. To avoid this problem, Assign a correct text encoding will be required.

Connection connection = Jsoup.connect(requestUrl)
        .data("type", "1")
        .data("searchKeyUID", searchKeyUID)
        .timeout(timeout)
        .method(Connection.Method.GET);

Connection.Response response = connection.execute();
Document document = Jsoup.parse(new ByteArrayInputStream(response.bodyAsBytes()), "UTF-8", requestUrl);
Group data on SQLite

Schema

CREATE TABLE invoice
(
    Id INTEGER PRIMARY KEY,
    code TEXT,
    dropped INTEGER,
    due_date INTEGER,
    paid INTEGER,
    due_date2 TEXT
);

Initial data

INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (1, 'IMSS', 0, 1407384129647, 0, '2014-08-07 04:02:09');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (2, 'Deep Discovery family', 0, 1407346684736, 0, '2014-08-06 17:38:04');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (3, 'TMMS​', 1, 1407375568242, 0, '2014-08-07 01:39:28');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (4, 'TMS family​', 0, 1407374437084, 1, '2014-08-07 01:20:37');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (5, 'SafeSync', 0, 1407358168347, 1, '2014-08-06 20:49:28');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (6, 'TMVMS​', 0, 1407350693881, 0, '2014-08-06 18:44:53');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (7, 'TMS family​', 0, 1407372590354, 0, '2014-08-07 00:49:50');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (8, 'IMSVA', 0, 1407344495046, 0, '2014-08-06 17:01:35');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (9, 'Other', 0, 1407351210774, 0, '2014-08-06 18:53:30');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (10, 'Deep Discovery family', 0, 1407372667906, 0, '2014-08-07 00:51:07');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (11, 'IMSVA', 1, 1407365934950, 1, '2014-08-06 22:58:54');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (12, 'PSP', 1, 1407382557767, 1, '2014-08-07 03:35:57');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (13, 'SecureCloud​', 0, 1407371609679, 0, '2014-08-07 00:33:29');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (14, 'NVWE​', 0, 1407383088966, 0, '2014-08-07 03:44:48');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (15, 'ESE', 1, 1407360299869, 1, '2014-08-06 21:24:59');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (16, 'IMSVA', 0, 1407377397832, 0, '2014-08-07 02:09:57');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (17, 'IWSVA​', 0, 1407366067348, 1, '2014-08-06 23:01:07');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (18, 'TMS family​', 1, 1407351067728, 0, '2014-08-06 18:51:07');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (19, 'OSCE​', 0, 1407345046895, 0, '2014-08-06 17:10:46');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (20, 'TIM​', 0, 1407363640638, 1, '2014-08-06 22:20:40');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (21, 'CSS​', 1, 1407330238874, 0, '2014-08-06 13:03:58');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (22, 'TMPS​', 1, 1407321708684, 1, '2014-08-06 10:41:48');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (23, 'TMS family​', 1, 1407319881608, 0, '2014-08-06 10:11:21');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (24, 'SafeSync', 1, 1407306547713, 0, '2014-08-06 06:29:07');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (25, 'ESDP', 0, 1407298566126, 0, '2014-08-06 04:16:06');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (26, 'CSS​', 0, 1407282477671, 0, '2014-08-05 23:47:57');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (27, 'TMMS​', 1, 1407240200509, 1, '2014-08-05 12:03:20');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (28, 'Other', 0, 1407218499996, 0, '2014-08-05 06:01:39');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (29, 'ESE', 0, 1407210588362, 0, '2014-08-05 03:49:48');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (30, 'CSS​', 0, 1407207304034, 0, '2014-08-05 02:55:04');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (31, 'TMVMS​', 1, 1407188482248, 0, '2014-08-04 21:41:22');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (32, 'SecureCloud​', 0, 1407155296508, 0, '2014-08-04 12:28:16');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (33, 'TMS family​', 1, 1407136374493, 0, '2014-08-04 07:12:54');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (34, 'ESE', 1, 1407121543307, 1, '2014-08-04 03:05:43');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (35, 'ScanMail', 0, 1407118018107, 0, '2014-08-04 02:06:58');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (36, 'ScanMail', 0, 1407088856291, 0, '2014-08-03 18:00:56');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (37, 'IMSS', 0, 1407059314747, 1, '2014-08-03 09:48:34');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (38, 'OSCE​', 1, 1407027453954, 0, '2014-08-03 00:57:33');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (39, 'Deep Discovery family', 0, 1406995304204, 0, '2014-08-02 16:01:44');
INSERT INTO invoice (Id, code, dropped, due_date, paid, due_date2) VALUES (40, 'Worry-Free', 0, 1406968142441, 0, '2014-08-02 08:29:02');

Group select

select 
  Id, 
  code, 
  (dropped|paid) status, 
  (due_date < 1407200000000) expired, 
  due_date2 
from invoice 
order by 
  paid|dropped, 
  (due_date > 1407200000000), 
  due_date desc

result

android.intent.action.SCREEN_ON & android.intent.action.SCREEN_OFF

First, I've tried create a receiver to receive screen on/off and register receiver on AndroidManifest.xml like below, but unfortunately it does not work.

ScreenReceiver.java
package com.prhythm.training.service;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

/**
 * Created by Bruce on 7/15/2014.
 */
public class ScreenReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("Receiver", String.format("Broadcast action: %s", intent.getAction()));
    }
}
AndroidManifest.xml
<receiver android:name=".service.ScreenReceiver">
    <intent-filter>
        <action android:name="android.intent.action.SCREEN_ON"/>
        <action android:name="android.intent.action.SCREEN_OFF"/>
    </intent-filter>
</receiver>

I's nessary to register screen action receiver by code, so I've change the as

ScreenReceiver.java
package com.prhythm.training.service;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

/**
 * Created by Bruce on 7/15/2014.
 */
public class ScreenReceiver extends BroadcastReceiver {

    public static ScreenReceiver CURRENT;

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("Receiver", String.format("Broadcast action: %s", intent.getAction()));
    }
}
ScreenService.java
package com.prhythm.training.service;

import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;

/**
 * Created by Bruce on 7/15/2014.
 */
public class ScreenService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        // Register screen on/off receiver
        if (ScreenReceiver.CURRENT == null) ScreenReceiver.CURRENT = new ScreenReceiver();
        getApplicationContext().registerReceiver(ScreenReceiver.CURRENT, new IntentFilter(Intent.ACTION_SCREEN_ON));
        getApplicationContext().registerReceiver(ScreenReceiver.CURRENT, new IntentFilter(Intent.ACTION_SCREEN_OFF));
    }
}

Then, start this service is required

startService(new Intent(this, ScreenService.class));
Enable SSL connection for Jsoup
import org.jsoup.Connection;
import org.jsoup.Jsoup;

import javax.net.ssl.*;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public class TaipeiWater {
    
    final static String requestUrl = "https://somewhere.com/target.jsp";

    public static void enableSSLSocket() throws KeyManagementException, NoSuchAlgorithmException {
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });

        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, new X509TrustManager[]{new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }}, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
    }

    public static void main(String[] args) throws IOException, NoSuchAlgorithmException, KeyManagementException {
        String bigno = "S";
        String midno = "11";
        String useno = "111111";
        String chkno = "1";

        enableSSLSocket();

        Connection.Response response = Jsoup.connect(requestUrl)
                .data("bigno", bigno)
                .data("midno", midno)
                .data("useno", useno)
                .data("chkno", chkno)
                .userAgent("Mozilla/5.0 (Windows NT 6.2; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0")
                .method(Connection.Method.POST)
                .execute();

        switch (response.statusCode()) {
            case 200:
                doProcess(response.parse());
                break;
            default:
                
                break;
        }
    }

    public static void doProcess(Document document){
        // do something...
    }
}
Register preference change event with PreferenceFragment
public class DevicePreferenceFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preference);
    }

    @Override
    public void onResume() {
        super.onResume();
        getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);

    }

    @Override
    public void onPause() {
        getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
        super.onPause();
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        Log.d(MainActivity.TAG, String.format("Preference '%s' has been changed.", key));
        if ("pref_track_interval".equals(key)) {
            // do something
        }
    }
}
Set remote respository for Intellij IDEA git integration
  1. Create a new project
  2. VCS -> Import into Version Control -> Create Git Repository... , then select the project path
  3. Open project path, find the config file at hidden subfolder .git
    Edit config file, add following contents at end:
    [remote "origin"]
     url = http://www.yourgitserver.com/repository.git
     fetch = +refs/heads/*:refs/remotes/origin/*
  4. File -> Synchronize
  5. That it! Try commit and push your project
Override Response Content-Type with OutputCache

Response content-type will be set as text/html on OutputCache is assigned, even if there is an assignment inside action.

HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace WebApplication1.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        [OutputCache(Duration = 60)]
        public ActionResult NavigationScript()
        {
            Response.ContentType = "application/javascript";
            return View();
        }
    }
}
Result

Custom a attribute and change content type after output cache processed.

ResponseHeaderAttribute.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace WebApplication1.Filter
{
    public class ResponseHeaderAttribute : ActionFilterAttribute
    {
        public string ContentType { get; set; }
        public System.Text.Encoding ContentEncoding { get; set; }

        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            if (!string.IsNullOrWhiteSpace(ContentType)) filterContext.HttpContext.Response.ContentType = ContentType;
            if (ContentEncoding != null) filterContext.HttpContext.Response.ContentEncoding = ContentEncoding;
        }
    }
}
HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using WebApplication1.Filter;

namespace WebApplication1.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        [OutputCache(Duration = 60, Order = 1)]
        [ResponseHeader(ContentType = "application/javascript", Order = 2)]
        public ActionResult NavigationScript()
        {
            return View();
        }
    }
}
Result
Session State Locks cause pending requests

In some scenario, while there is a long pending request exist, it may cause other request pending because session data is locked by the first request. Here is a example of session lock.

First I have a controller with default session policy

using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using WebApplication2.Models;

namespace WebApplication2.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult LongTimeout()
        {
            System.Threading.Thread.Sleep(30000);
            return Json(new { code = 200 }, JsonRequestBehavior.AllowGet);
        }

        public ActionResult ShortTimeout()
        {
            return Json(new { code = 200 }, JsonRequestBehavior.AllowGet);
        }

        public ActionResult Captcha()
        {
            var captcha = MathCaptcha.Generate();
            Session["captcha"] = captcha.Answer;

            return File(captcha.GetImageData(ImageFormat.Png), "image/png");
        }
    }
}

And then create another controller that make session state readonly

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace WebApplication2.Controllers
{
    [SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
    public class SessionLessController : Controller
    {
        public ActionResult LongTimeout()
        {
            System.Threading.Thread.Sleep(30000);
            return Json(new {code=200 }, JsonRequestBehavior.AllowGet);
        }

        public ActionResult ShortTimeout()
        {
            return Json(new { code = 200 }, JsonRequestBehavior.AllowGet);
        }
    }
}

Request is pending because the session of first request is locked

After first request completed, other requests will go on

Request won't be block when session state is set as readonly

[Json.net] Convert enum to string
[JsonConverter(typeof(StringEnumConverter))]
public enum Gender
{
    [EnumMember(Value = "man")]
    Male,
    [EnumMember(Value = "woman")]
    Femail,
    [EnumMember(Value = "unknown")]
    Unknown
}