Thursday, July 16, 2015

Do GET, POST, PUT, DELETE in asp.net MVC with Jquery

Introduction

In asp.net MVC we have seen http verbs like HttpGet, HttpPost, HttpPut and HttpDelete. Here will see how can we use these words in actions, what does these words mean, and how can use jquery ajax calls for such http verb enable actions.

Why use HttpVerbs?

Let's examine these actions
UrlRespone()
1. Is accessible using browser url
2. It can also be used for Ajax calls
public JsonResult UrlResponse()     //accessible using Url
{
    return Json(new { Name = "UrlResponse", Response = "Response from Get", Date = DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss tt") }, JsonRequestBehavior.AllowGet);
}
TypedResponse()
1. It can only be usable using Ajax calls using (type: "GET")
2. If try to access using browser url, it would throw error
[HttpGet]
public JsonResult TypedResponse()    //error if try to access using Url
{
    return Json(new { Name = "TypedResponse", Response = "Response from Get", Date = DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss tt") }, JsonRequestBehavior.AllowGet);
}
MultipleTypedResponse()
1. If try to access using browser url, it would throw error
2. It can only be usable using Ajax calls using (type: "GET"/"POST")
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
//or [AcceptVerbs("GET","POST")]
public JsonResult MultipleTypedResponse()
{
    return Json(new { Name = "MultipleTypedResponse", Response = "Response from Get", Date = DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss tt") }, JsonRequestBehavior.AllowGet);
}
Now let’s see how can use http verbed actions in MVC, and Jquery Ajax calls using $.ajax objects together. But there are some concern with
1. How to specify a particular action for Ajax call.
        - At url attribute use specify the controller and actions as /{controller}/{action} pattern;
url: '/User/Create',
url: '/User/Get/20',
2. How to specify the http verb in Ajax call
        - At the type attribute with values GET/ POST/ PUT/ DELETE of the Ajax object
type: "POST",
type: "GET",
3. How to pass parameters to that action if needed.
        - At data attribute we specify the data to be passed to a particular action.
data: JSON.stringify({ user: { name: 'Rintu', email: 'Rintu@gmial.com' } }),
data: { name: 'Rintu', email: 'Rintu@gmial.com' },
        - For GET requests we can also specify the data’s at the url attribute, but we need to work with route configurations too. (Here we are avoiding things, concerning rout config).
url: '/User/Get/20',
4. Where to find the response data.
        - At the success attribute we will get the response data’s.
success: function (data) {
    alert(data);
},
5. Is there any error
       - At the error attribute we will get error detail, if any found at the server end
error: function (xhr) {
    alert('error');
}

HttpGet

MVC action
// GET: /User/Get/5
[HttpGet]
public JsonResult Get(int id)
{
    return Json("Response from Get", JsonRequestBehavior.AllowGet);
}
ajax call
1. passing value of id as url: '/User/Get/20'
2. no use of data attr at the ajax object.
/*GET*/
$.ajax({
    url: '/User/Get/20',
    dataType: "json",
    type: "GET",
    contentType: 'application/json; charset=utf-8',
    async: true,
    processData: false,
    cache: false,
    success: function (data) {
        alert(data);
    },
    error: function (xhr) {
        alert('error');
    }
});

HttpPost

MVC action
// POST: /User/Create
[HttpPost]
public JsonResult Create(User user)
{
    return Json("Response from Create");
}
ajax call
Data is passed to action using the data attribute of the ajax object
/*POST*/
$.ajax({
    url: '/User/Create',
    dataType: "json",
    type: "POST",
    contentType: 'application/json; charset=utf-8',
    data: JSON.stringify({ user: { name: 'Rintu', email: 'Rintu@gmial.com' } }),
    async: true,
    processData: false,
    cache: false,
    success: function (data) {
        alert(data);
    },
    error: function (xhr) {
        alert('error');
    }
})

HttpPut

MVC action
// PUT: /User/Edit
[HttpPut]
public JsonResult Edit(int id, User user)
{
    return Json("Response from Edit");
}
ajax call
/*PUT*/
$.ajax({
    url: '/User/Edit',
    dataType: "json",
    type: "PUT",
    contentType: 'application/json; charset=utf-8',
    data: JSON.stringify({ id: 100, user: {name: 'Rintu', email: 'Rintu@gmial.com'} }),
    async: true,
    processData: false,
    cache: false,
    success: function (data) {
        alert(data);
    },
    error: function (xhr) {
        alert('error');
    }
});

HttpDelete

MVC action
// DELETE: /User/Delete
[HttpDelete]
public JsonResult Delete(int id)
{
    return Json("Response from Delete");
}
ajax call
/*DELETE*/
$.ajax({
    url: '/User/Delete',
    dataType: "json",
    type: "DELETE",
    contentType: 'application/json; charset=utf-8',
    data: JSON.stringify({id: 20}),
    async: true,
    processData: false,
    cache: false,
    success: function (data) {
        alert(data);
    },
    error: function (xhr) {
        alert('error');
    }
});
Well they all worked fine, but there are some interesting things too. Let’s just talk about them too.

Lets take some closer looks for GET's

when we are working with httpGet verbed actions we may face conserens like,

Pass data as objects rather than JSON.stringify(object)

action
// GET: /User/Contains
[HttpGet]   //use or not works same
public JsonResult Contains(string name, string email)
{
    return Json("Response from Contains", JsonRequestBehavior.AllowGet);
}
ajax call
/*GET*/
$.ajax({
    url: '/User/Contains',
    dataType: "json",
    type: "GET",
    contentType: 'application/x-www-form-urlencoded; charset=utf-8',    //replace /json to the urlenoded
    data: { name: 'Rintu', email: 'Rintu@gmial.com' },                  // data is not json
    async: true,
    processData: true,                                                  //important to use it as true
    cache: false,
    success: function (data) {
        alert(data);
    },
    error: function (xhr) {
        alert('error');
    }
});
Let’s find some difference with the previous HttpGet verb use.
At the old one, we have used,
1. url: '/User/Get/20'
2. There was no data attribute at the Ajax object.
At this one we have used,
1. url: '/User/Create'
2. data: { name: 'Rintu', email: 'Rintu@gmial.com' }
And this would work just fine.

What if we try to pass values using both url and data?

action
// GET: /User/Find/1/30 or
// GET: /User/Find
[HttpGet]
public JsonResult Find(int pageNo, int pageSize, User user)
{
    return Json("Response from Find", JsonRequestBehavior.AllowGet);
}
ajax call,
/*GET*/
$.ajax({
    url: '/User/Find/3/5',
    dataType: "json",
    type: "GET",
    contentType: 'application/json; charset=utf-8',
    //pageNo: 2, pageSize: 20 would not be posted to the action,
    //it would be 3 and 5 as we specified it at ajax url
    //and user would be null
    data: { pageNo: 2, pageSize: 60, user: { name: 'Rintu', email: 'Rintu@gmial.com' } },
    async: true,
    processData: false,
    cache: false,
    success: function (data) {
        alert(data);
    },
    error: function (xhr) {
        alert('error');
    }
})
At action pageNo and pageSize would be 1 and 30 rather than 2 and 20, as priority of the url data pass is higher.
This type of "GET" requests with data in url, helps in action overloading too. In such case, we have to be aware with the route configurations.
Yes there is another problem with the user object here, and we are going to discuss about it too.

Who to pass complex objects using "GET" to actions?

Action: here we need to pass pageNo, pageSize and user object detail (Id, Name, Email)
// GET: /User/Find/1/30 or
// GET: /User/Find
[HttpGet]
public JsonResult Find(int pageNo, int pageSize, User user)
{
    var value = Request.QueryString["user"];
    /*here we will not get user, beacuse mvc doesn't work like that*/
    /*solution 1: rather than an object, use all properties of user object as parms
      Find(int pageNo, int pageSize, long userId, string userName...)
    */
    /*solution 2: use httppost, which is the most proper for the job*/
    return Json("Response from Find", JsonRequestBehavior.AllowGet);
}
Try url value pass, but how to pass the user object in url? Let’s try something like,
Result at action, pageNo: 3, pageSize: 5, user: null
/*GET*/
$.ajax({
    url: '/User/Find/3/5',
    dataType: "json",
    type: "GET",
    contentType: 'application/json; charset=utf-8',
    data: { user: { name: 'Rintu', email: 'Rintu@gmial.com' } },       
    async: true,
    processData: false,
    cache: false,
    success: function (data) {
        alert(data);
    },
    error: function (xhr) {
        alert('error');
    }
});
Try value pass as object, like,
Result at action, pageNo: 2, pageSize: 20, user: null
/*GET*/
$.ajax({
    url: '/User/Find',
    dataType: "json",
    type: "GET",
    contentType: 'application/x-www-form-urlencoded; charset=utf-8',
    data: { pageNo: 2, pageSize: 20, user: { name: 'Rintu', email: 'Rintu@gmial.com' } },
    async: true,
    processData: true,
    cache: false,
    success: function (data) {
        alert(data);
    },
    error: function (xhr) {
        alert('error');
    }
});
In both cases we were unable to pass to the values of user object
So how can we pass those values all together?
Solution 1: rather than an object, use all properties of user object as parms
                 Find(int pageNo, int pageSize, long userId, string userName...)
Solution 2: use httppost and POST, which is the most proper for the job

Limitations

  1. Yes there could be something which I miss understood or presented. So if you find anything just let me know.
  2. All of these examples are not fully operational at real times projects yet. But used at prototype levels.
Find Visual Studio 2012 solution of MVC4 project at the attachment.

No comments: