I am writing a simple web api application. I came to a phase when I need to have two POST methods in my web api controller. One of these methods works and the other does not. My route table looks like this:
config.Routes.MapHttpRoute(
name: "ApiRouteWithAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Then I have my methods defined like this:
[HttpPost]
public bool PostTaskAgain(My3TasksWebAPI.Data.Task task)
{
var oldTask = _db.Task.Where(t => t.Id == task.Id).SingleOrDefault();
oldTask.DoAgain = true;
oldTask.DateUpdated = task.DateUpdated;
if (_db.SetOfTasks.Where(t => CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(t.DateCreated, CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday) == CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday)).Any())
{
int currentSetOfTasksId = _db.SetOfTasks.OrderBy(s => s.DateCreated).FirstOrDefault().Id;
My3TasksWebAPI.Data.Task newTask = new Data.Task() { CreatedBy = oldTask.CreatedBy, DateCreated = oldTask.DateCreated, DateUpdated = null, DoAgain = false, Notes = string.Empty, SetOfTasksId = currentSetOfTasksId, Status = false, Title = oldTask.Title, UserId = oldTask.UserId };
_db.Task.Add(newTask);
}
_db.SaveChanges();
return true;
}
// Post api/values/PostSetOfTasks/{setOfTasks}
[HttpPost]
public bool PostSetOfTasks(My3TasksWebAPI.Data.SetOfTasks setOfTasks)
{
_db.SetOfTasks.Add(setOfTasks);
_db.SaveChanges();
return true;
}
When I try to call PostTaskAgain I get an internal server error. I think that it might be the routing table but I am not sure how to handle two post methods.
I call the web api from my asp.net mvc application like this:
HttpResponseMessage response = client.PostAsJsonAsync("api/values/PostSetOfTasks", model.SetOfTasks).Result;
and
HttpResponseMessage response = client.PostAsJsonAsync("api/values/PostTaskAgain", taskToPost).Result;
That means that I include the actions.
Working with POST in webapi can be tricky though conincidently, your issue turned out to be trivial. However, for those who may stumble upon this page:
I will focus specifically on POST as dealing with GET is trivial. I don't think many would be searching around for resolving an issue with GET with webapis. Anyways..
If your question is - In MVC Web Api, how to-
- Use custom action method names other than the generic HTTP verbs?
- Perform multiple posts?
- Post multiple simple types?
- Post complex types via jQuery?
Then the following solutions may help:
First, to use Custom Action Methods in Web API, add a web api route as:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}");
}
And they you may create action methods like:
[HttpPost]
public string TestMethod([FromBody]string value)
{
return "Hello from http post web api controller: " + value;
}
Now, fire the following jQuery from your browser console
$.ajax({
type: 'POST',
url: 'http://localhost:33649/api/TestApi/TestMethod',
data: {'':'hello'},
contentType: 'application/x-www-form-urlencoded',
dataType: 'json',
success: function(data){ console.log(data) }
});
Second, to perform multiple posts,
It is simple, create multiple action methods and decorate with the [HttpPost] attrib.
Use the [ActionName("MyAction")] to assign custom names, etc.
Will come to jQuery in the fourth point below
Third,
First of all, posting multiple SIMPLE types in a single action is not possible and there is a special format to post a single simple type (except for passing the parameter in the query string or REST style).
This was the point that had me banging my head with Rest Clients and hunting around the web for almost 5 hours and eventually, the following URL helped me. Will still quote the contents for the link may turn dead!
Content-Type: application/x-www-form-urlencoded
in the request header and add a = before the JSON statement:
={"Name":"Turbo Tina","Email":"na#Turbo.Tina"}
http://forums.asp.net/t/1883467.aspx?The+received+value+is+null+when+I+try+to+Post+to+my+Web+Api
Anyway, let us get over that story. Moving on:
Fourth, posting complex types via jQuery, ofcourse, $.ajax() is going to promptly come in the role:
Let us say the action method accepts a Person object which had an id and a name. So, from javascript:
var person = { PersonId:1, Name:"James" }
$.ajax({
type: 'POST',
url: 'http://mydomain/api/TestApi/TestMethod',
data: JSON.stringify(person),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function(data){ console.log(data) }
});
And the action will look like:
[HttpPost]
public string TestMethod(Person person)
{
return "Hello from http post web api controller: " + person.Name;
}
All of the above, worked for me!!
Cheers!
There was a problem with my LINQ query.
The response from the server was: {"$id":"1","Message":"An error has occurred.","ExceptionMessage":"LINQ to Entities does not recognize the method 'Int32 GetWeekOfYear(System.DateTime, System.Globalization.CalendarWeekRule, System.DayOfWeek)' method, and this method cannot be translated into a store expression.","ExceptionType":"System.NotSupportedException","StackTrace":" at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.DefaultTransl‌​ator.Translate(ExpressionConverter parent, MethodCall....
After correcting the linq query everything is working fine. Visual studio was fine about me doing the linq query wrong.
Related
How to invoke the WepApi Controller Action in browser URL, Below is the Action Method in APIController.
public virtual HttpResponseMessage Export(string ABC, string product, string Release, bool includeInheritedData = false)
{
}
you can define route path and name for web api methods. Using that route can access a API controller action if it is simple Get call and has Anonymous access.
For example:
Suppose this a method in your API controller:
[HttpGet,AllowAnonymous][Route("api/register")]
public void Register()
{
}
You can access it in the URL like: localhost/api/register from browser.This is a simple example to explain thing in simple terms. There are lot of other things involved accessing API methods depending upon various factors like security, requirements etc.
Already its predefined in Appstart/WebApiConfig.cs for WebApi controllers they were using like below.
config.Routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = #"\d+" });
config.Routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");
config.Routes.MapHttpRoute("DefaultApiGet", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
config.Routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new { action = "Post" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) });
Here is my request definition:
params = typeof params !== "undefined" ? params : {};
var deferred = $q.defer();
var response = $http({
method: "post",
dataType: "json",
data: JSON.stringify(params),
headers: {
'Content-Type': "application/json; charset=utf-8",
},
url: 'apiUrl' + 'MVCcontrollerName' + '/'
});
And C#:
public class MVCcontrollerName : Controller
{
public ActionResult Index(int? Id = null)
{
.......
I am not getting my Id parameter in the controller class. It's null. The parameter is defined as {Id:1}.
I also tested POST with Chrome REST client with the same results.
Any idea, what's wrong?
I see a couple of issues here.
First the name of your controller should follow the convention of SomenameController, so you might want to call it MVCcontrollerNameController
The URL you specify is missing a slash to delimit between the path and the controller name:
url: 'apiUrl' + '/' + 'MVCcontrollerName' + '/'
Or simply:
url: 'apiUrl/MVCcontrollerName/'
Or more correctly, let MVC do the routing for you (as suggested by #JamieRees):
url: '#Url.Action("Index","MVCcontrollerName")'
3. The main issue however, is that you are POSTing the data where you really should be calling it as a GET with the parameter as part of the URL. However, if you really need to have it in a POST, then you need to decorate the parameter with the FromBody attribute:
public ActionResult Index([FromBody]int? Id = null)
I'm not sure if this will help, but anyway you should have attribute [HttpPost] on action that will be called with post
I need to pass a dynamic JSON object to my Web API controller so that I can process it depending on what type it is. I have tried using the JSON.NET example that can be seen here but when I use Fiddler, I can see that the passed in JObect is always null.
This is an exert from the example pasted into Fiddler:
POST http://localhost:9185/api/Auto/PostSavePage/ HTTP/1.1
User-Agent: Fiddler
Content-type: application/json
Host: localhost
Content-Length: 88
{AlbumName: "Dirty Deeds",Songs:[ { SongName: "Problem Child"},{ SongName:
"Squealer"}]}
Ans here is my very simple Web API controller method:
[HttpPost]
public JObject PostSavePage(JObject jObject)
{
dynamic testObject = jObject;
// other stuff here
}
I am new to this and I have a couple of questions around this area:
Am I doing something wrong in this particular example?
Arguably, more importantly, is there a better way to pass in a dynamic JSON object (from an JavaScript AJAX post)?
As per Perception's comment your JSON doesn't look valid. Run it through JSONLint and you get:
Parse error on line 1:
{ AlbumName: "Dirty De
-----^
Expecting 'STRING', '}'
Change it to have " around the field names:
{
"AlbumName": "Dirty Deeds",
"Songs": [
{
"SongName": "Problem Child"
},
{
"SongName": "Squealer"
}
]
}
Also have you tried swapping out your JObject for either a JToken or a Dynamic object (e.g. here)?
[HttpPost]
public JObject PostSavePage(JToken testObject)
{
// other stuff here
}
OR
[HttpPost]
public JObject PostSavePage(dynamic testObject)
{
// other stuff here
}
Thanks to everyone who helped here. Unfortunately, I never got to the bottom of what was wrong.
I ported the project over piece by piece to a new project and it works fine.
For info, I have a RouteConfig class, which is quite simple at the moment:
public class RouteConfig
{
private static string ControllerAction = "ApiControllerAction";
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: ControllerAction,
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
My call into the API now uses JSON.Stringify:
$.ajax("http://localhost:54997/api/values/PostSavePage/", {
data: JSON.stringify(jObject),
contentType: 'application/json',
type: 'POST'
});
The original API action works.
Note, that I'm only playing with this at the moment so the code is not the best but I thought it may be useful in basic form in case someone else has a similar issue.
the problem is that i am not able to recieve any value in the controller . what could be wrong? the code is here.
$('#save').click(function () {
var UserLoginViewModel = { UserName: $('vcr_UserName').val(),
Password: $('vcr_Password').val()
};
$.ajax({
url: "/User/Login",
data: JSON.stringify(UserLoginViewModel),
contenttype: "application/json; charset=utf-8",
success: function (mydata) {
$("#message").html("Login");
},
error: function () {
$("#message").html("error");
},
type: "POST",
datatype: "json"
});
return false;
});
});
[HttpPost]
public ActionResult Login(UserLoginViewModel UserLoginViewModel)
{
}
As you're using MVC3 - you should be able to take advantage of the built in JSON model binding.
Your code example has a couple of typos: contentType and dataType are lowercase...(they should have an uppercase "T")
jQuery ajax docs
After you POST up the correct contentType/dataType, MVC should automatically bind your object to the posted JSON.
You're going to need an action filter or similar to intercept the json from the post body.
Here's a starter
Provider Factory
but here is the article that sorted this for me On Haacked
It is good if you know the type you are deserialising into up front, but if you need polymorphism you'll end up using these ideas in an action filter.
In doing an auto-refresh using the following code, I assumed that when I do a post, the model will automatically sent to the controller:
$.ajax({
url: '<%=Url.Action("ModelPage")%>',
type: "POST",
//data: ??????
success: function(result) {
$("div#updatePane").html(result);
},
complete: function() {
$('form').onsubmit({ preventDefault: function() { } });
}
});
Every time there is a post, I need to increment the value attribute in the model:
public ActionResult Modelpage(MyModel model)
{
model.value = model.value + 1;
return PartialView("ModelPartialView", this.ViewData);
}
But the model is not passed to the controller when the page is posted with jQuery AJAX request. How can I send the model in the AJAX request?
The simple answer (in MVC 3 onwards, maybe even 2) is you don't have to do anything special.
As long as your JSON parameters match the model, MVC is smart enough to construct a new object from the parameters you give it. The parameters that aren't there are just defaulted.
For example, the Javascript:
var values =
{
"Name": "Chris",
"Color": "Green"
}
$.post("#Url.Action("Update")",values,function(data)
{
// do stuff;
});
The model:
public class UserModel
{
public string Name { get;set; }
public string Color { get;set; }
public IEnumerable<string> Contacts { get;set; }
}
The controller:
public ActionResult Update(UserModel model)
{
// do something with the model
return Json(new { success = true });
}
If you need to send the FULL model to the controller, you first need the model to be available to your javascript code.
In our app, we do this with an extension method:
public static class JsonExtensions
{
public static string ToJson(this Object obj)
{
return new JavaScriptSerializer().Serialize(obj);
}
}
On the view, we use it to render the model:
<script type="javascript">
var model = <%= Model.ToJson() %>
</script>
You can then pass the model variable into your $.ajax call.
I have an MVC page that submits JSON of selected values from a group of radio buttons.
I use:
var dataArray = $.makeArray($("input[type=radio]").serializeArray());
To make an array of their names and values. Then I convert it to JSON with:
var json = $.toJSON(dataArray)
and then post it with jQuery's ajax() to the MVC controller
$.ajax({
url: "/Rounding.aspx/Round/" + $("#OfferId").val(),
type: 'POST',
dataType: 'html',
data: json,
contentType: 'application/json; charset=utf-8',
beforeSend: doSubmitBeforeSend,
complete: doSubmitComplete,
success: doSubmitSuccess});
Which sends the data across as native JSON data.
You can then capture the response stream and de-serialize it into the native C#/VB.net object and manipulate it in your controller.
To automate this process in a lovely, low maintenance way, I advise reading this entry that spells out most of native, automatic JSON de-serialization quite well.
Match your JSON object to match your model and the linked process below should automatically deserialize the data into your controller. It's works wonderfully for me.
Article on MVC JSON deserialization
This can be done by building a javascript object to match your mvc model. The names of the javascript properties have to match exactly to the mvc model or else the autobind won't happen on the post. Once you have your model on the server side you can then manipulate it and store the data to the database.
I am achieving this either by a double click event on a grid row or click event on a button of some sort.
#model TestProject.Models.TestModel
<script>
function testButton_Click(){
var javaModel ={
ModelId: '#Model.TestId',
CreatedDate: '#Model.CreatedDate.ToShortDateString()',
TestDescription: '#Model.TestDescription',
//Here I am using a Kendo editor and I want to bind the text value to my javascript
//object. This may be different for you depending on what controls you use.
TestStatus: ($('#StatusTextBox'))[0].value,
TestType: '#Model.TestType'
}
//Now I did for some reason have some trouble passing the ENUM id of a Kendo ComboBox
//selected value. This puzzled me due to the conversion to Json object in the Ajax call.
//By parsing the Type to an int this worked.
javaModel.TestType = parseInt(javaModel.TestType);
$.ajax({
//This is where you want to post to.
url:'#Url.Action("TestModelUpdate","TestController")',
async:true,
type:"POST",
contentType: 'application/json',
dataType:"json",
data: JSON.stringify(javaModel)
});
}
</script>
//This is your controller action on the server, and it will autobind your values
//to the newTestModel on post.
[HttpPost]
public ActionResult TestModelUpdate(TestModel newTestModel)
{
TestModel.UpdateTestModel(newTestModel);
return //do some return action;
}
I think you need to explicitly pass the data attribute. One way to do this is to use the
data = $('#your-form-id').serialize();
This post may be helpful.
Post with jquery and ajax
Have a look at the doc here..
Ajax serialize
you can create a variable and send to ajax.
var m = { "Value": #Model.Value }
$.ajax({
url: '<%=Url.Action("ModelPage")%>',
type: "POST",
data: m,
success: function(result) {
$("div#updatePane").html(result);
},
complete: function() {
$('form').onsubmit({ preventDefault: function() { } });
}
});
All of model's field must bo ceated in m.
In ajax call mention-
data:MakeModel(),
use the below function to bind data to model
function MakeModel() {
var MyModel = {};
MyModel.value = $('#input element id').val() or your value;
return JSON.stringify(MyModel);
}
Attach [HttpPost] attribute to your controller action
on POST this data will get available