I'm trying to send a request to the server with 4 parameters. It seems to work with less than 4, but I cannot get it to work with 4 or more.
Here are the snippets:
historyy: $resource('/history/:user.:item.:count.:cost', {
user: '#userr',
item: '#itemm',
count: '#countt',
cost: '#costt'
}
Calling RestService:
RestServices.historyy.get({userr: "Asd",itemm: $scope.item.id,countt: amount,costt: '0' });
When I execute this, the URL looks like this:
http://localhost:8080/history/...?costt=0&countt=6&itemm=1&userr=Asd
Why is it adding these crazy (...?) ?. When I remove one of the parameter (no matter which one) it works properly.
You can use query (see: doc)
Resource
$resource('/history/');
Call
RestServices.historyy.query({user: "Asd", item: $scope.item.id, ...}, function(results) {
//on success
});
Result
/history?user=Asd&item=ExampleItem...
Spring REST Controller
#RestController
#RequestMapping(value = "/history", produces = MediaType.APPLICATION_JSON_VALUE)
public class SomeRestController {
#ResponseBody
#RequestMapping(method = RequestMethod.GET)
#JsonView(View.List.class)
public List<SomeThing> getSomething(
#RequestParam(value = "user", required = true) String user,
#RequestParam(value = "item", required = true) String item, ...) {...}
You can find more information here: Using $resource.query with params object
Related
I am pretty new to AngularJS. I am trying out $http.
I created a very simple WebAPI to test. Below is the code:
public class ProductsController : ApiController
{
Product[] product = new Product[]
{
new Product { Id = 1, Name = "Fruit" },
new Product { Id = 2, Name = "Yo-yo"},
new Product { Id = 3, Name = "Hammer"}
};
public IHttpActionResult GetProducts()
{
return Ok(new { results = product as IEnumerable<Product> });
}
}
When I try to access using Fiddler, it properly returns JSON. But when I try using this working example here: http://embed.plnkr.co/lrn0NNMNbIVE4rS4Fytd/preview
And call http://localhost:55403/api/products (I change the properties in the option value to match the Products), it doesn't even go there.
It seems to go to the onFetchError.
Does it mean that the WebAPI is not returning JSON as expected?
Thank you
I am just learning on how to use Web API with angular but I am having a few issues on retrieving data and I would appreciate it if someone could help me.
I have a controller called GetUserController with the following methods.
public class GetUserController : ApiController
{
[HttpGet]
public string GetUserName()
{
var user = "John Doe"
return user;
}
[HttpGet]
public string GetUserName2()
{
string user = "Jane Doe";
return user;
}
}
//Angular side of things
function getUser() {
return $http.get('/api/GetUser').then(function (data) {
return data;
});
}
The above code works fine and and returns the first user from the controller . however when I try to get the second by using the below angular code:
function getUser() {
return $http.get('/api/GetUser/GetUserName2').then(function (data) {
return data;
});
}
This does not work for some reason it says it can't find the GetUserName2 method. Am I missing something ? Please help?
EDIT: The error i'm getting is : Multiple actions were found that match the request
As #shammelburg has pointed out, this is as a result of Web.API not being able to match your request to a controller/method.
It's not so much that it's not RESTful, and has nothing to do with the verb you are using... it's just that an appropriate route map was not found.
Per the accepted answer, you can add another generic route map to enable the method of access you are attempting, however a more specific option exists using attribute routing:-
public class GetUserController : ApiController
{
[Route("api/getuser")]
[HttpGet]
public string GetUserName()
{
var user = "John Doe"
return user;
}
[Route("api/getuser/getusername2")]
[HttpGet]
public string GetUserName2()
{
string user = "Jane Doe";
return user;
}
}
And to enable the use of attribute routes, add this to your WebApiConfig class:-
config.MapHttpAttributeRoutes();
This method allows you to setup specific custom mappings of URLs to controllers/methods at the individual method level, without having to make a global route map that may conflict with something else in your application at a later date.
You can find more info on attribute routing here
Whilst the above will resolve the specific issue you are having, there would in practice be a different way to implement the example you gave:-
public class GetUserController : ApiController
{
[Route("api/user/{id}")]
[HttpGet]
public string GetUserName(int id)
{
// this would be replaced with some sort of data lookup...
var user = "unknown";
if (id == 1) {
user = "John Doe";
} else if (id == 2) {
user = "Jane Doe";
} // and so on...
return user;
}
}
In the above, the URL api/user/x where x is a number, e.g. api/user/1 will match the GetUserName method of the GetUserController and pass the number as an argument to the method.
This would be accessed using something like this in Angular:-
function getUser(id) {
return $http.get('/api/user/' + id).then(function (data) {
return data;
});
}
This is caused because it is not a true RESTful call which use HTTP verbs, GET, POST, PUT, DELETE.
The way to get your code to work is by altering your WebApiConfig.cs file.
From:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
To:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
As you can see we've added the {action} to the routeTemplate which makes this very much like a MVC Controller.
This allows you to call your API methods (GetUserName & GetUserName2) name like you are trying to do in your angular $http function.
Example:
return $http.get('/api/GetUser/GetUserName')
return $http.get('/api/GetUser/GetUserName2')
I have 2 classes on Server side.. MyClass1 and MyClass2..
Im creating a JSON object..
var myclass1 = {name:'xyz', id:16};
var myclass2 = {name:'abc', id:17}
and using restangular..
Restangular.one('some url').customPUT({var1:myclass1 , var2:myclass2 });
Im using spring framework to create my REST Services
#RequestMapping(value = "/someurl", method = RequestMethod.PUT) <br>
#ResponseBody
public void someMethod(#RequestBody MyClass1 var1, #RequestBody MyClass2 var2) {
//some code
}
This is the scenario...
But I had read a post on stackOverflow that for 1 request body we can pass (put request) only 1 object.. If so then what is the alternate way...
I can send MyClass2 object through request param (there is an annotation #RequestParam)
ex. Restangular.one('some url').customPUT({var1:myclass1 }, {}, {var2:myclass2 }, {});
But again this gets appended to my URL and i dont want to change my URL..
Please help. Thnx..
I am using AngularJs and Resources module. I want to do a GET to obtain an object.. to do this GET I do not have to pass simply the ID to the server, but I should pass a complex object with different properties and values..
Here the code I am using:
$scope.getActivationStatus = function (event) {
event.preventDefault();
if ($scope.segui_attivazione_form.$valid) {
$scope.activationStatus =
new SeguiAttivazioneService
.seguiAttivazione()
.$get(
{
request: $scope.activationStatus
}, function () { });
}
};
On server side I have:
[HttpGet]
public IHttpActionResult GetActivationStatus(MyComplexObject request)
{
//I will do something here later...
return Ok();
}
The problem is that "request" arrive on server equals to NULL...
I have solved the problem passing two strings to the server... in this way:
$scope.getActivationStatus = function (event) {
event.preventDefault();
if ($scope.segui_attivazione_form.$valid) {
$scope.activationStatus =
new SeguiAttivazioneService
.seguiAttivazione()
.$get(
{
codiceFiscale: $scope.activationStatus.CodiceFiscale,
codiceRichiesta: $scope.activationStatus.CodiceRichiesta
}, function () { });
}
};
And server side:
[HttpGet]
public IHttpActionResult GetActivationStatus(string codiceFiscale, string codiceRichiesta)
{
return Ok();
}
In this way everything works... but I don't like this solution because I will have more than two input...
And this is a get, not a post (not a save, an update)...
How can I pass a complex object doing a GET?
Thank you...
It's best to use the POST method if you want to send data in the body of the request. While it's possible with Angular, some servers might ignore the body of GET requests.
This approach allows to send complex objects with arrays and sub objects:
Angular:
$http({
url: '/myApiUrl',
method: 'GET',
params: { param1: angular.toJson(myComplexObject, false) }
})
C#:
[HttpGet]
public string Get(string param1)
{
Type1 obj = new JavaScriptSerializer().Deserialize<Type1>(param1);
...
}
This is not an elegant solution but it works using HTTP GET:
$http.get(url + "?" + $.param(obj).replace(/%5b([^0-9].*?)%5d/gi, '.$1'))
It converts the complex object into a string with dot notation to define levels. Most of the server side frameworks like ASP.NET Core can bind it to complex objects.
This is an example of the string I send for a complex object:
StartDate=2021-06-11&EndDate=2021-06-11&TimeRange.TimeFrom.Time=07%3A00&TimeRange.TimeFrom.TimeFrame=AM&TimeRange.TimeTo.Time=10%3A00&TimeRange.TimeTo.TimeFrame=AM
Request body can only be sent by POST. With get you could at best URL Encode the OBJECT and then send it as query string params. But thats not the best solution to post some data to the server
I'm looking for a solution to POSTing an array of objects to MVC3 via JSON.
Example code I'm working off of:
http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx
JS:
var data = { ItemList: [ {Str: 'hi', Enabled: true} ], X: 1, Y: 2 };
$.ajax({
url: '/list/save',
data: JSON.stringify(data),
success: success,
error: error,
type: 'POST',
contentType: 'application/json, charset=utf-8',
dataType: 'json'
});
ListViewModel.cs:
public class ListViewModel
{
public List<ItemViewModel> ItemList { get; set; }
public float X { get; set; }
public float Y { get; set; }
}
ItemViewModel.cs:
public class ItemViewModel
{
public string Str; // originally posted with: { get; set; }
public bool Enabled; // originally posted with: { get; set; }
}
ListController.cs:
public ActionResult Save(ListViewModel list)
{
// Do something
}
The result of this POST:
list is set, to a ListViewModel
Its X and Y properties are set
The underlying ItemList property is set
The ItemList contains one item, as it should
The item in that ItemList is uninitialized. Str is null and Enabled is false.
Put another way, this is what I get from MVC3's model binding:
list.X == 1
list.Y == 2
list.ItemList != null
list.ItemList.Count == 1
list.ItemList[0] != null
list.ItemList[0].Str == null
It would appear the MVC3 JsonValueProvider is not working for complex objects. How do I get this to work? Do I need to modify the existing MVC3 JsonValueProvider and fix it? If so, how do I get at it and replace it in an MVC3 project?
Related StackOverflow questions I've already pursued to no avail:
Asp.net Mvc Ajax Json (post Array)
Uses MVC2 and older form-based encoding - that approach fails with an object that contains an array of objects (JQuery fails to encode it properly).
Post an array of complex objects with JSON, JQuery to ASP.NET MVC Controller
Uses a hack I'd like to avoid where the Controller instead receives a plain string which it then manually deserializes itself, rather than leveraging the framework.
MVC3 RC2 JSON Post Binding not working correctly
Didn't have his content-type set - it's set in my code.
How to post an array of complex objects with JSON, jQuery to ASP.NET MVC Controller?
This poor guy had to write a JsonFilter just to parse an array. Another hack I'd prefer to avoid.
So, how do I make this happen?
In addition to { get; set; }, these are some of the conditions for JSON Binding Support:
This is new feature in ASP.NET MVC 3 (See “JavaScript and AJAX Improvements“).
The JSON object’s strings (‘X’, ‘Y’, ‘Str’, and ‘Enabled’) must match ViewModel object’s properties.
ViewModel object’s properties must have { get; set; } method.
Must specify Content Type as “application/json” in the request.
If it's still not working, check the JSON string to make sure it's valid one.
Read more at my post.
Hope that helps!
The problem was that the properties in the models that were in the List did not have get/set on their public properties. Put another way, MVC3's automatic JSON binding only works on object properties that have get and set.
This will not bind:
public string Str;
This will bind:
public string Str { get; set; }
That's strange. I am unable to reproduce your behavior. Here's my setup (ASP.NET MVC 3 RTM):
Model:
public class ItemViewModel
{
public string Str { get; set; }
public bool Enabled { get; set; }
}
public class ListViewModel
{
public List<ItemViewModel> ItemList { get; set; }
public float X { get; set; }
public float Y { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Save(ListViewModel list)
{
return Json(list);
}
}
View:
#{
ViewBag.Title = "Home Page";
}
<script type="text/javascript">
$(function () {
var data = { ItemList: [{ Str: 'hi', Enabled: true}], X: 1, Y: 2 };
$.ajax({
url: '#Url.Action("save", "home")',
data: JSON.stringify(data),
type: 'POST',
contentType: 'application/json',
dataType: 'json',
success: function (result) {
alert(result.ItemList[0].Str);
}
});
});
</script>
Running this alerts "hi" and inside the Save action everything is correctly initialized.
And just for the record what doesn't work are Dictionaries. I've opened a ticket about the issue.
I had a similar issue, and found that for a complex object, the numeric values were getting missed. They were coming in as zeros.
i.e.
var person = {
Name: "john",
Age: 9
}
was being received by MVC controller as a Person object where the properties were being populated as Name=John and Age=0.
I then made the Age value in Javascript to be string... i.e.
var person = {
Name: "john",
Age: "9"
}
And this came through just fine...
Its because the MVC binders kind of suck. However, they do work pretty well if all JSON values come over as a string.
In JS if you do this
var myObject = {thisNumber:1.6};
myObject.thisNumber=myObject.thisNumber-.6;
It will evaluate to 1 not to 1.0
So when you sent it over to the server it will try to bind to a float of that name and it will not find it since it came over as 1 instead of 1.0. Its very lame and crazy that MS engineers did not come up with a default solution to this. I find if you string everything the bindings are smart enough to find things.
So before sending the data over run it though a stringifier that will also convert all values to strings.
All previous answers were great to point me to solution of the similar problem. I had to POST x-www-form-urlencoding instead of application/json (default option if contentType parameter is missing) to be able to pass __RequestVerificationToken and simultaneously faced with problem when object properties being in the array do not bind their values. The way to solve the issue is to understand internal work of MVC model binder.
So, basically when you need to supply verification token you are restricted with validation attribute. And you must provide the token as the parameter not as a part of the JSON-object you are sending. If you would not use ValidateAntiForgeryToken, you could get along with JSON.stringify. But if you would, you could not pass the token.
I sniffed traffic to backend when ContentType was x-www-form-urlencoding and I remarked that my array of complex objects was serialized to something like that: klo[0][Count]=233&klo[0][Blobs]=94. This array initially was a part of root object, let's say some model. It looked like that: model.klo = [{ Count: 233, Blobs: 94}, ...].
At the backend side this klo property was creating by MVC binder with the same elements count that I sent. But these elements itself did not obtain values for their properties.
SOLUTION
To deal with this I excluded klo property from the model object at the client side. In the ajax function I wrote this code:
data: $.param(model) + "&" + arrayOfObjectsToFormEncoding("klo", [{ Count: 233, Blobs: 94}, ...])
....
function arrayOfObjectsToFormEncoding (modelPropertyName, arrayOfObjects) {
var result = "";
if (arrayOfObjects && typeof arrayOfObjects == "object") {
for (var i = 0; i < arrayOfObjects.length; i++) {
var obj = arrayOfObjects[i];
if (obj) {
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
result += encodeURIComponent(modelPropertyName + "[" + i + "]." + p) + "=" + encodeURIComponent(obj[p]) + "&";
}
}
}
}
}
if (result[result.length - 1] == "&") {
result = result.substr(0, result.length - 1);
}
return result;
}
The function transforms array of complex object into form that is recognized by MVC-binder. The form is klo[0].Count=233&klo[0].Blobs=94.