AngularJS cannot successfully call WebAPI but Fiddler does? - angularjs

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

Related

JSON object not passing as param to webApi PUT method

I’m using Angularjs and asp.net mvc 5 with webApi2.
I’m having some trouble calling a custom PUT method. I’ve done some studying for the past few days, and although I have a decent feel for the situation, I can’t get my JSON object to pass as a parameter for some reason.
Route template:
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
Web api controller and model (shortened for brevity):
public class AttModel
{
public string dc { get; set; }
public string dt { get; set; }
}
[HttpPut]
public IHttpActionResult PutAttendRecord([FromBody]AttModel model)
{
string dc = model.dc;
DateTime dt = Convert.ToDateTime(model.dt);
var record = (from tbl in db.attend_am_y1
where tbl.dc_number == dc && tbl.class_date_am == dt
select tbl).SingleOrDefault();
record.status_am = "z";
db.SaveChanges();
}
Javascript object (angularjs PUT):
$scope.updateRecord = function () {
var stuInfo = {
dc: $scope.student.dc,
dt: $scope.student.dt
};
$http.put("/api/attendance/PutAttendRecord/" + stuInfo)
.then(function (d) {
alert(d.data.dc_number);
});
}
I tried using Newtonsoft without the extra AttModel class, and passing the param as jObject, but I still get a null value exception within the iHttpActionResult method. The data just isn’t making it to my method. Routing issue?
If I manually place values within these variables in the iHttpActionResult, the method works fine.
Assuming you are getting into your call alright,
you want to attach your object in the body
$http.put("/api/attendance/PutAttendRecord/", stuInfo)
.then(function (d) {
alert(d.data.dc_number);
});
and I don't think you need [FromBody] as I believe this is only specified if the function finds it unclear.

How to call custom name funtion

I am trying to figure, how to call a specific function name in WepApi.
Currently if i call the WebApi with that structure api/{controllerName
its working (in case that the function name start with "get")
What i am trying to accomplish is that:
in JS
$http.get("http://localhost:34662/api/webapi/CustomfunctionName/")
.then(function (response) {
vm.employees = response.data;
}
in WebApi
[HttpGet]
[Route(Name = "CustomfunctionName")]
public IEnumerable<tblEmployees> CustomfunctionName()
{
using (Entities entities = new Entities())
{
return entities.tblEmployees.ToList();
}
}
I also tried to add a decorator above the function in the WebApi:
[Route(Name = "CustomfunctionName")]
but it didn't help.
How can it be done?
Could you add RoutePrefix attribute at the controller, and try again?
[RoutePrefix("api/webapi")]
public class WebApiController : ApiController
{
[HttpGet]
[Route("CustomfunctionName")]
public IEnumerable<tblEmployees> CustomfunctionName()
{
}
}
Solution per #E.Meir: If you use [ActionName(...)], you should decorate it to every action method inside the controller.

What is the best way to retrieve data using Web API 2 and AngularJS

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')

AngularJS web API not working with strings

At the moment what I am trying to do is to return a list of strings from my web API, but I was getting this error 'No 'Access-Control-Allow-Origin' header is present on the requested resource.'
After struggling for a while I have realised it's because it will only allow me to return a list of integers and not a list of strings.
Here are the two methods (one of which would obviously be commented out).
public IEnumerable<string> Get(int userId)
{
IEnumerable<string> listOfFormGroups = new List<string>() { "Form Group 1", "Form Group 2", "Form Group 3" };
return listOfFormGroups;
}
public IEnumerable<int> Get(int id)
{
IEnumerable<int> listOfRoles = new List<int>() { 1, 2, 3 };
return listOfRoles;
}
The top method throws the error, but the second method returns fine.
my angular service
this.get = function (userId) {
return $http.get(toApiUrl('formgroup/') + userId);
}
my angular controller calling the service,
var promiseGet = formGroupService.get(1);
promiseGet.then(function (result) {
$scope.FormGroups = result.data
},
function (errorResult) {
$log.error('Unable to get users form groups. An error has occurred. :', errorResult);
});
If you look at the default WebApiConfig, it looks like this:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I believe the integer version is working because you called the param id as stated in the config. The string one you have is trying to map to id, seeing userId as the only parameter and not mapping it correctly. I believe that is your error.
Change your method to look like this:
public IEnumerable<string> Get(int id)
{
IEnumerable<string> listOfFormGroups = new List<string>() { "Form Group 1", "Form Group 2", "Form Group 3" };
return listOfFormGroups;
}
peinearydevelopment's answer may be correct, but there is another interesting part of the question. The No 'Access-Control-Allow-Origin' message is a CORS error message (a cross origin request). In general, a script is limited to making AJAX requests to the same URL origin that it came from.
My guess that part of the problem is coming from the fact that toApiUrl() creates a URL that is of a different domain than the script came from.
There are ways around this, but it requires changes on the server side. See the documentation on how to do this.
It does't have any problem. You can call it by having below url.
I have already tested it and its working fine.
http://localhost:52257/api/temp/Get?userId=3
// please use your domain or localhost....
So in your case you can go with,
return $http.get(toApiUrl('formgroup/Get?userId=3'));

WPF and MVC4 Web API Internal Server Error 500 on POST

So I'm attempting to attach to a web api method via a WPF service, but get only a 500 error on anything other than a GET.
WPF call:
using (var client = new HttpClient())
{
var user = new MyUser
{
EntityID = Guid.NewGuid(),
FirstName = "WPF",
LastName = "test"
};
var formatter = new JsonMediaTypeFormatter();
HttpContent content = new ObjectContent<MyUser>(user, formatter);
client.BaseAddress = new Uri("http://localhost:19527/api/");
var response = await client.PostAsJsonAsync("MyUser", content);
//.ContinueWith((postTask) => result = (postTask.Result.Content == null) ? "Could not create user" : "User created successully!");
var r = response.StatusCode;
}'
...and the receiving controller:
public HttpResponseMessage Get(string badgeId)
{
return Request.CreateResponse<bool>(HttpStatusCode.OK, (service.UserByBadge(badgeId) != null));
}
public HttpResponseMessage Put(MyUser user)
{
return Request.CreateResponse<bool>(HttpStatusCode.OK, service.UpsertUser(user));
}
public HttpResponseMessage Post(MyUser user)
{
if (service.UpsertUser(user)) return Request.CreateResponse<MyUser>(HttpStatusCode.OK, service.Get<MyUser>(u => u.BadgeID == user.BadgeID));
return Request.CreateResponse<MyUser>(HttpStatusCode.NoContent, null);
}'
The service on the WebApi controller is a GenericRepository, which is working fine, since the Get method returns as expected. It's only when I use Post that I get the error. Debugging the methods throws the break point in the Get, but not in the Post, so I don't think it's ever being called.
Here's the route config:
routes.MapRoute(
name: "Default",
url: "api/{controller}/{action}/{id}",
defaults: new { controller = "{controller}", action = "{action}", id = UrlParameter.Optional }
);
I've tried different examples from other SO posts, but none appear to address this issue specifically. I'm guessing there's something wrong with how I've constructed the Post() method?
================================================================
RESOLUTION: Model being passed was failing property validations. Why this was causing a 500, not certain. But once I solved for this, API method began working.
If anybody has a "why" explanation, would love to know for future reference.

Resources