Showing posts with label ASP.NET MVC. Show all posts
Showing posts with label ASP.NET MVC. Show all posts

14 October 2018

what is HTTP HEAD request?

Off late REST services have become very popular. People pays lot of attention to HTTP verbs while desiging the REST services. There are different HTTP verbs meant for different kinds of HTTP requests, namely GET, POST, PUT, DELETE, PATCH, HEAD, etc.

In this post let’s discuss perticulalry on HTTP HEAD requests and when to use them.

What is HTTP HEAD requests?

With HTTP Head requests (as the name already implying) server will return only the HTTP Headers but not the body.

When to use HTTP HEAD then?

Responses for the Http Head request from the servers will be light weight compared to HTTP GET requests, because they don’t contain any body. So you can use HTTP HEAD request / response together to determine whether a heavy payload entity is modified at the server end or not.

One example the server can just return when was a requested resource last modified with the HTTP HEAD response, then client will verify to see if the version of the entity it possess alrerady is latest or not.

If the entity is latest, then client can continue to use the entity it has in its store, else it can issue a HTTP GET request to fetch the heavy payload entity from the server.

In this way using the HTTP HEAD request and responses between client and servers we can design more performant systems by avoiding exchange of heavy payload object when not really required.

11 October 2015

DateTime model property is not assigned value in Model binding

Recently one junior member in the team was building a web page using ASP.NET MVC; he was using model binding to get the model properties posted back.

// ViewModel definition
public class InsurancePolicy
 {
    public DateTime MaturityDate;
 }
// Controller sample
[HttpPost]
public ActionResult Edit(InsurancePolicy policy)
 {
   // policy.MaturityDate was not assigned correct 
   // value posted back
    DateTime policyMaturityDate= policy.MaturityDate;
     
    //Other logic..
    return RedirectToAction("PurchasedPolicyList");
 }

But however whenever the Edit form is posted back the policy. The Maturity Date was having the default value for the date time, but not the posted back value.

Later after looking into the code, found out below:

  1. He was selecting the policy date time field, in the DD/MM/YYYY format.
  2. But the server had “MM/DD/YYYY” date format set.

Requirement was not to change the UI date time selection format and also changing the server date time format was not a feasible solution, given that application may be hosted in cloud environment, where you may don't have permission to do such changes to the system.

Later System.Globalization.CultureInfo class came to rescue. Added the below in the web.config file to let know application should with INDIA(hi-IN) culture info.

<system.web>
 <globalization culture="hi-IN" uiCulture="en-US" />
</system.web>

After having the above globalization setting, the Maturity Date time properties started getting the right values.

Caution: Before changing the setting at the global level, care must be taken to ensure it doesn't have any adverse effect on the web application.

10 October 2015

mutex could not created error while running the ASP.NET web application

Last week one of my junior software developer was getting started with ASP.NET MVC application. He created a sample application using the ASP.NET MVC templates available in Visual Studio 2013. But when he tried to run the application using Start Debugging (F5), the application started running using the Visual Studio inbuilt server but failed to show the default route page with the below error:

Mutex could not be created.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.InvalidOperationException: Mutex could not be created.


Solution to fix the Mutex could not be created error

He didn’t start the Visual Studio as an Administrator, so it was failing because of accessing some registries because of the restricted permissions.

He started the Visual Studio as an Administrator and everything started working as expected.

Some times it takes to run Visual Studio always as an ADMIN, in that case follow Running an application as ADMIN always.

09 August 2015

changing JQGRID URL dynamically

While developing web applications, there will be need sometimes to render the data in JQGRID, based on what customer wants to see it. But in JQGRID, the URL will be provided for the first time when you created the JQGRID like below:

jQuery("#customerGrid").jqGrid({
          
    url: "/Customer/GetByType/3"
    datatype: 'json'
});

With the hardcoded the URL, cannot render different data based on what user wants to see it. If you need to display different data by changing URL, with the above-hardcoded URL approach, you end up recreating the JQGRID every time. I think that is expensive, considering that, need is to change just the URL form which data will be fetched.

One option is to consider changing just the URL, depending on what user what to see it. When you want to display data from a different URL, you need to do two things:

  1. First, change the jqgrid URL
  2. Second, reload the grid

Code to change the jqgrid URL

$("#customerGrid").jqGrid(
'setGridParam',{ url: "/Customer/GetByType/" + $('#customerType').val() 
}).trigger("reloadGrid");
});

Making JQGrid not to load data, just upon constructing it

Sometimes when you want to show data in jqgrid, you might not know the URL up front to get the JSON data until the user selects something. In such scenario, if you mention the URL while constructing the jqGRID, the JQGRID will try to load the data from the URL mentioned, that may not be desirable in some scenarios.

In such scenario, you can stop JQGRID making the URL request for the data. To do this you can do two things:

  1. First mention 'datatype' param as 'local' while building the jqgrid instead on 'json'. Then the 'URL' param mentioned doesn't have any impact.

    jQuery("#customerGrid").jqGrid({
                datatype: 'json'
    });
  2. Second, when user changes the selection to view specific data, change the 'datatype' param to 'json', set the URL to fetch the data and reload the grid.

    $("#customerGrid").jqGrid('setGridParam', { datatype: 'json' }); 

    $("#customerGrid").jqGrid(
        'setGridParam',{ url: "/Customer/GetByType/" +
         $('#customerType').val() 
    }).trigger("reloadGrid");
    });

14 June 2015

working with local data in jqgrid

Recently I helped a friend of mine on Jqgrid. He was trying to build a page, which had multiple tabs and some tabs had grid user interface, to add, edit and delete rows. JQGRID fitted his requirement nicely. Only on submission of the entire form he wanted to post data, until then rows added, edited should be maintained locally in the browser.

There are many E2E sample available in internet on jqgrid working with server API, but we couldn’t find any single E2E solution in jqgrid working with local data.

In this post let’s see how did we get jqgrid to work with local data - explaining adding a row, editing a row and deleting a row and with all the data stored in a JSON object locally.

JSON data structure in question

var customersData= [
{id: guid(), FirstName: "Ranga", LastName: "H C", sendEmail: true },
{id: guid(), FirstName: "Vijay", LastName: "Kumar", sendEmail: true },
{id: guid(), FirstName: "Praveen", LastName: "C", sendEmail: true }];

It’s a JSON array of customers. Each customer representing an auto-generated unique id, first name, last name and email subscription option.

Screenshot showing the Grid in action

jqgrid tutorial working with local data

ASP.MVC application view to display the customer grid

<div id="gridPager"></div>
<table id="customerGrid"></table>

JavaScript code using jqgrid

Let's go through the most important java-script code to setup the jqgrid working with local JSON data.

Settig up the jqgrid

Inside the $(document).ready() function add the below code.Highlights of the below code:

  1. In the jqgrid, datatype has been set to ‘local’, indicating JQGrid should work with local data.
  2. data, has been set customersData, which is a JSON data.
  3. Uses the custome guid() function to get unique identifier for the row.
var customersData = [
{ id: guid(), FirstName: "Ranga", LastName: "H C", sendEmail: true },
{ id: guid(), FirstName: "Vijay", LastName: "Kumar", sendEmail: true },
{ id: guid(), FirstName: "Praveen", LastName: "C", sendEmail: true }];

$("#customerGrid").jqGrid({
 datatype: 'local',
 data: customersData,
 colNames: ['id', 'FirstName', 'LastName', 'sendEMail'],
 colModel: [
  { name: 'id', index: 'id', width: 90, hidden: true },
  {
  name: 'FirstName', index: 'FirstName', width: 120, 
  editable: true,
  editrules: { required: true }
  },
  {
  name: 'LastName', index: 'LastName', width: 120, 
  editable: true,
  editrules: { required: true }
  },
  {
  name: 'sendEmail', index: 'sendEmail',
  width: 90, align: 'center',
  formatter: 'checkbox', editrules: { required: true },
  edittype: 'checkbox', editoptions: { value: 'Yes:No',   
defaultValue: 'Yes' },
  editable: true
  }],
    gridview: true,
    rownumbers: false,
    pager: '#gridPager',
    height: '100%',
    rowList: [],       //
    pgbuttons: false,  // 
    pgtext: null,      // Disable pagination options
    viewrecords: false // 
    });

beforeSubmit function

While working with the local data, the logic of reading the posted data should in place before submitting the details into server API. Hence the beforeSubmit() function is the right place put such logic and also note that fields validation will also trigger set validation rules set will also be functional.

beforeSubmit function in the add row option

Highlights of the beforeSubmit code.
  1. Get’s submitted form data using postdata parameter.
  2. Creates the unique customer id using the guid() method.
  3. Adds the customer JSON data to the customersData using the JSON array push method.
  4. Refreshes the jqgrid, so that newly added customer row is visible in the grid.
  5. Closes the ‘Add Record’ dialog box using the click trigger on the close button.This action results in exception, so eat the exception. This is not clean solution to close the dialog!
beforeSubmit: function (postdata, formid) {
customersData.push({ id: guid(), FirstName: postdata.FirstName,
LastName: postdata.LastName, sendEmail: postdata.sendEmail });

$('#customerGrid').jqGrid('setGridParam', { datatype: 'local',
 data: customersData }).trigger('reloadGrid');

try {
$(".ui-icon-closethick").trigger('click');
}
catch (ex) { }
return [false, ''];
}

beforeSubmit function in the edit row option

Highlights of the beforeSubmit code:
  1. Identify the id of the record being edited.
  2. Identity the index of the record in the JSON array.
  3. Builds the updated customer JSON object data using the submitted form data.
  4. Uses the JSON splice() method to insert the edited JSON object at the same position as the original customer object in the array
  5. Refreshes the jqgrid, so that edited customer row values are visible.
  6. Closes the ‘Edit Record’ dialog box using the click trigger on the close button. This action results in exception, so eat the exception.
beforeSubmit: function (postdata, formid) {

var myGrid = $('#customerGrid');
var selRowId = myGrid.jqGrid('getGridParam', 'selrow');
var rowGuid = myGrid.jqGrid('getCell', selRowId, 'id');

var dataIndex = undefined;

$.each(customersData, function (index, value) {
if (value.id === rowGuid) {
dataIndex = index;
}
});

if (dataIndex != undefined) {
customersData.splice(dataIndex, 1, 
{ id: rowGuid, FirstName: postdata.FirstName, 
LastName:postdata.LastName, sendEmail: postdata.sendEmail });
}

$('#customerGrid').jqGrid('setGridParam',
{ datatype: 'local', data: customersData }).
      trigger('reloadGrid');

try {
$(".ui-icon-closethick").trigger('click');
}
catch (ex) { }
return [false, ''];
});

beforeSubmit function in the delete row option

Highlights of the beforeSubmit code:
  1. Identify the id of the record being removed.
  2. Identify the index of the record in the JSON array.
  3. Uses the JSON splice() method to remove the selected record from the JSON array data.
  4. Invokes the delRowData() to delete the row the jqgrid. Refreshing grid didn’t here!
  5. Closes the ‘Edit Record’ dialog box using the click trigger on the close button. This action results in exception, so eat the exception.
beforeSubmit: function (postdata, formid) {

var myGrid = $('#customerGrid');
var selRowId = myGrid.jqGrid('getGridParam', 'selrow');
var rowGuid = myGrid.jqGrid('getCell', selRowId, 'id');

var dataIndex = undefined;

$.each(customersData, function (index, value) {
 if (value.id === rowGuid) {
       dataIndex = index;
    }
   });

if (dataIndex != undefined) {
customersData.splice(dataIndex, 1);
}

$('#customerGrid').delRowData(selRowId);

try {
$(".ui-icon-closethick").trigger('click');
}
catch (ex) {
console.log(ex);
}
return [false, ''];
}

At end of the operations, the customersData JSON array holds latest of data added, edited and deleted. You can use JSON.stringfy() method to convert JSON data into the string and post the string value to the server.

ASP.NET MVC application's packages.config file

Use the below info to identify frameworks version being used.

<packages>
  <package id="Antlr" version="3.4.1.9004" 
          targetFramework="net451" />
  <package id="bootstrap" version="3.0.0" 
          targetFramework="net451" />
  <package id="jQuery" version="1.10.2" 
          targetFramework="net451" />
  <package id="jQuery.UI.Combined" version="1.8.11" 
          targetFramework="net451" />
  <package id="jQuery.Validation" version="1.11.1" 
          targetFramework="net451" />
  <package id="Microsoft.AspNet.Mvc" version="5.2.2" 
          targetFramework="net451" />
  <package id="Microsoft.AspNet.Razor" version="3.2.2" 
          targetFramework="net451" />
  <package id="Microsoft.AspNet.Web.Optimization" version="1.1.3"
          targetFramework="net451" />
  <package id="Microsoft.AspNet.WebPages" version="3.2.2" 

  targetFramework="net451" />
  <package id="Microsoft.jQuery.Unobtrusive.Validation" 
     version="3.2.2" targetFramework="net451" />
  <package id="Microsoft.Web.Infrastructure" 

   version="1.0.0.0" targetFramework="net451" />
  <package id="Modernizr" version="2.6.2" targetFramework="net451" />
  <package id="Newtonsoft.Json" version="6.0.4" 
               targetFramework="net451" />
  <package id="Respond" version="1.2.0" 
               targetFramework="net451" />
  <package id="Trirand.jqGrid" version="4.6.0" 
               targetFramework="net451" />
  <package id="WebGrease" version="1.5.2" 
               targetFramework="net451" />
</packages>

Your comments are welcome!

03 June 2015

ActionName attribute in ASP.NET MVC

Using HttpGet and HttpPost ActionMethodAttribues, you can select between a HTTP POST and HTTP GET actions. But if you have same method signatures for your action methods in the controller class, the C# compiler will produce the below compilation error.

Type '..Controller' already defines a member called 'Delete' with the same parameter types.

Controller class with error

public class CustomerController : Controller
{
  [HttpGet]
  public ActionResult Delete(string customerId)
  {
     return View("ConfirmDelete", customerId);
  }

  [HttpPost]
  public ActionResult Delete(string customerId)
  {
    return RedirectToAction("List");
  }
}

Solution with ActionName attribute

Let’s see how we can fix the above error by making use ActionName attribute.

  1. With the ActionName attribute you can specify what method name MVC pipeline should look while invoking the action method.
  2. First rename the POST Delete() controller method to DeleteOnConfirmation to fix method overloading related compilation error.
  3. Then apply ActionName attribute to DeleteOnConfirmation() method by specifying "Delete" as the name.

Controller class with the fix

public class CustomerController : Controller
{
  [HttpGet]
  public ActionResult Delete(string customerId)
  {
    return View("ConfirmDelete", customerId);
  }

  [ActionName("Delete")]
  [HttpPost]
  public ActionResult DeleteOnConfirmation(string customerId)
  {
    // STEP1: Delete the customer in Database.
    return RedirectToAction("List");
  }
}

Benefits on ActionName attribute

By using ActionName attribute, now you can make the a URL with same action name and parameters maps to a different .NET method action method in your controller class.

  • HTTP GET "http://localhost/customer/delete/123" mapped to Delete action method.
  • HTTP POST "http://localhost/customer/delete/123" mapped to DeleteOnConfirmation action method.

02 June 2015

PRG (post-redirect-get) pattern in web application development

Today let’s learn Post-Redirect-Get pattern in web app development by applying it to an ASP.NET MVC application.

Problem statement

Assume that you are developing an e-commerce web app and you need to process the customer credit card information while checking out an order. In ASP.NET MVC web development technology, the action method to process payment would something like below:

[HttpPost]
public ActionResult ProcessPayment(string cardNumber, 
string cvv, string customerName)
  {
     //Step1: Validate credit card details
     //Step2: Charge the customer credit card
     //Step3: Send payment confirmation view
     return View("Confirmation");
 }

With above controller action method, everything works except one bug. After successful order placement while viewing the Confirmation page, if customer presses F5 button, the browser will show the duplicate transaction warning dialog.

Duplicate transaction submission warning message

If you click Retry button, the ProcessPayment () action method will be called and depending the logic written in the method, the customer might get charged again. Definitely, this is not good customer experience and should be prevented.

Why duplicate transaction warning

Let’s first understand why browser is displaying the duplicate transaction warning and what is happening under the wood.

Sequence diagram showing Request/Response flow causing the issue

UML sequence diagram showing duplicate form submit issue

Browser resends the last request to the web application on pressing F5 (Refresh) key. In this case, the last request is HTTP post submitting the form details. So the same HTTP post request will be submitted once again. The browser will detect this and display a duplicate transaction message to user.

Applying PRG pattern to prevent duplicate form submission issue

The PRG pattern will be implemented as below:

  1. POST the form details.
  2. Send REDIRECT response to browser with URL location where to look for the response.

  3. Browser sends GET request to obtain the response from URL location present in the HTTP 302 redirect result.

Code changes to implement PRG pattern:

  1. Update the POST action method to Send RedirectToRouteResult specifying the Confirmation action
    [HttpPost]
    public ActionResult ProcessPayment(string cardNumber, 
    string cvv, string customerName)
     {
      //Step1: Validate credit card details
      //Step2: Charge the customer credit card
      //Step3: Send RedirectToRouteResult specifying
      //       the Confirmation action
      return RedirectToAction("Confirmation");
     }
  2. Add Confirmation GET action method to send the Confirmation view
    [HttpGet]
    public ActionResult Confirmation()
     {
       return View();
     }

Now upon successful submission of credit card details and you will see the confirmation page. While viewing the confirmation page, if you press the REFRESH button, you will get to see confirmation page but without the duplicate form submission warning.

Request/response flow with PRG pattern

UML sequence diagram showing post-redirect-get(PRG) pattern

With PRG pattern we can prevent browser showing the duplicate transaction dialog to the user. Make sure you implement PRG pattern with every POST request.

Caveat with PRG pattern

With PRG pattern, there is an extra request/response cycle between client and server. This additional request/response cycle will impact your application performance. But that said, don’t put aside the pattern implementation instead look for other ways to improve application performance.

26 March 2013

Dynamic text as file download in ASP.NET MVC

Recently I got a question to download a file in ASP.NET MVC. The CSV file dynamic content had to be built at runtime. After bit of investigation found out how to download file such file in ASP.NET MVC

ASP.NET MVC action showing the download file code:

public class HomeController : Controller
{
  public ActionResult Download()
  {
    // Get the file content as string
    string fileContent = GetCSVFileContent();

    // Get the bytes for the dynamic string content
    var byteArray = Encoding.ASCII.GetBytes(fileContent);

    string fileName = "Download File in ASP.NET MVC 
                     with dynamic content.csv"
;

    return File(byteArray, "text/csv", fileName);
  }
}

The steps followed are below:

  1. First get the dynamic file content as string.
  2. Later Get the byte array using the required encoding. Here I have used ASCII encoding.
  3. Use the File() method in System.Web.Mvc.Controller class to send the array of bytes as file.
    protected internal virtual FileStreamResult 
    File(Stream fileStream, string contentType, string fileDownloadName);
Related links

Recently posted an article on implementing PRG pattern when doing HTTP POST request.

05 May 2012

Generate unique URL for unique resources to leverage caching

URL used to access any resource on web. To increase the performance of the web application, the URL used as one of the keys to cache data at different locations. Accessing the resources using unique URLs helps to avoid putting duplicate response in cache and also helps to increase performance of web application

In ASP.NET MVC application we can cache the action output so that we can improve the site performance.

[OutputCache(Duration = 10, VaryByParam = "visitor",
Location = OutputCacheLocation.Server)]
 public ActionResult Index(string visitor = "Guest")
   {
   ViewBag.Message = visitor + 
  ", Welcome to ASP.NET MVC!" + DateTime.Now.ToString();
   return View();
  }

So Index action is cached on the web server for 10 seconds and new cache entry will be created for each different visitor.The same cached action output response will be served for the subsequent request comes within 10-second duration provided the visitor parameter is same.

As you can see the visitor parameter is optional here. So when no value specified for the visitor param, "Guest" will be used as its value.

So, http://example.com/Home/Index/Guest and http://example.com/Home/Index will actually display the same content.

When query strings are used, http://example.com/Home/Index?visitor= Guest and "http://example.com/Home/Index" will actually display the same content.

But the question is the URLs which serve the same content will they make use of the same cached content when?

Applying the same question to the example, http://example.com/Home/Index/Guest URL once servers the response, its output for the "Guest" parameter is cached for the next request within the set duration in seconds. Having said that will the URL, http://example.com/Home/Index, which syntactically similar to the URL which specifies the default value explicitly, will they share the same cached copy?

Answer is NO.

Because the cache is based on the complete URL when ASP.NET MVC decides whether to use available cache for the incoming URL request, it checks whether the URL is exactly same as the cached URL. Since http://example.com/Home/Index/Guest and http://example.com/Home/Index are not the same strings, the action will execute on the server and will result in new output response. So, when generating the URLs for the cached actions, always strive to generate the unique URLs wherever we can reuse the cached copies of the action output response.

When Action parameter is used in VaryByParam, pay attention to the parameter casing. Different output cache will be generated for the same parameter value if they differ in casing.

Let's take an example.

[OutputCache(VaryByParam = "id", 
   Location = OutputCacheLocation.ServerAndClient,
   Duration = 60)]
public ActionResult Detail(string id)
 {
   ViewBag.Message = id + " Details are displayed";
   return View();
 }

The URL http://example.com/detail/Detail/apple and http://example.com/detail/Detail/Apple will not use the same output cache. They will create a different output cache. So when such URLs generated in the server side pay attention to the casing.

Its a best practice to always generate the URL in lower case.