What is the best way to retrieve data using Web API 2 and AngularJS - 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')

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.

Why is this Web API 2 action returning 404 not found?

Here is my ApiController:
[RoutePrefix("api/Campaign")]
public class CampaignController : ApiController
{
private readonly ICampaignLogic _campaignLogic;
public CampaignController(ICampaignLogic campaignLogic)
{
_campaignLogic = campaignLogic;
}
[HttpGet]
[Route("GetCampaignsByYearCompanyAndContactIds/{companyId:int}/{contactId:int}/{year:int}")]
public List<Campaign> GetCampaignsByYearCompanyAndContactIds(int companyId, int contactId, int year)
{
return _campaignLogic.GetByYearCompanyAndContactIds(year, companyId, contactId);
}
}
And here is where I make the call in AngularJS:
$http.get(ApiUrl + "/api/Campaign/GetCampaignsByYearCompanyAndContactIds",
{ params: { year: $scope.year, companyId: $scope.client, contactId: $scope.contactId } })
.then(function (responses) {
})
This is what is being called when I debug it in Chrome:
http://localhost/MyApi/api/Campaign/GetCampaignsByYearCompanyAndContactIds?companyId=12046&contactId=13662&year=2016
Everything is set up correctly in terms of my localhost as I am calling other API actions without any problem. However, when I call this one I am getting a 404 Not Found error.
What am I missing here?
Based on your route the url needs to have the parameters included in the route and not a part of the query string. if you want to make them available only in the query string then remove them from the route attribute.
What you should be passing in AngularJS
http://localhost/MyApi/api/Campaign/GetCampaignsByYearCompanyAndContactIds/12046/13662/2016
So you will need to build the URL using the parameters, you cannot use params with this url.
If you want to use params in AngularJS change the Route attribute to the following.
[Route("GetCampaignsByYearCompanyAndContactIds")]

AngularJS web api call - can't find get method on controller

I cannot for the life of me understand why this isn't working.
I have a simple ASP.Net MVC Web API controller, with 2 get methods. I have an AngularJS service with 2 corresponding functions. The GetAllRisks works perfectly well. However, the GetRiskByID comes back with an error saying "No HTTP request was found that matches the request "http://localhost:49376/api/RiskApi/GetRiskByID/6" and "No action can be found on the RiskApi controller that matches the request."
The URL is being passed correctly. I have tried various options for the API routing but can't get anywhere. I am sure I am missing something simple but can't see it.
I would really appreciate any thoughts.
Thanks,
Ash
RiskApiController
public class RiskApiController : ApiController
{
private readonly IRiskDataService _riskDataService;
public RiskApiController(IRiskDataService riskDataService)
{
_riskDataService = riskDataService;
}
// GET api/RiskApi
[HttpGet]
public IEnumerable<IRisk> GetAllRisks()
{
return _riskDataService.GetAllRisks().Take(20);
}
// GET api/RiskApi/5
[HttpGet]
public IRisk GetRiskByID(int riskID)
{
IRisk risk = _riskDataService.GetRiskByID(riskID);
if (risk == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return risk;
}
}
service.js
app.service('OpenBoxExtraService', function ($http) {
//Get All Risks
this.getAllRisks = function () {
return $http.get("/api/RiskApi/GetAllRisks");
}
//Get Single Risk by ID
this.getRisk = function (riskID) {
var url = "/api/RiskApi/GetRiskByID/" + riskID;
return $http.get(url);
}
});
WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "ActionRoute",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Try changing your WebApiConfig class to:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
And change your param: riskID to id.
// GET: api/RiskApi/GetRiskByID/5
[HttpGet]
public IRisk GetRiskByID(int id)
{
IRisk risk = _riskDataService.GetRiskByID(id);
if (risk == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return risk;
}
Then, you could use:
// GET: api/RiskApi/GetAllRisks
// GET: api/RiskApi/GetRiskByID/5
...try junking your config's HttpRoute Mapping and introduce the route mapping with Attributes.
Since this is first-gen Web API, you'll need to pull in the AttributeRouting.WebAPI NuGet package, and you'll likely want to refer here(strathweb) for straightforward guidance on how to implement.
Note that you have a mapping problem in your current implementation on your {id} parameter: you declare it to be id in the route configuration, but then you identify it as riskID inside the controller's method; these need to match. Switch your controller's method to have its incoming routeParameter be named id. You could optionally switch your config to declare the {riskID} parameter in your routes, but that would couple your global configuration to the semantics of a specific controller and you'd likely need to implement more routing constraints to have other controllers not named "Risk" make sense.
public class RiskApiController : ApiController
{
private readonly IRiskDataService _riskDataService;
public RiskApiController(IRiskDataService riskDataService)
{
_riskDataService = riskDataService;
}
// GET api/RiskApi
[HttpGet]
[Route("api/RiskApi")]
public IEnumerable<IRisk> GetAllRisks()
{
return _riskDataService.GetAllRisks().Take(20);
}
// GET api/RiskApi/5
[HttpGet]
[Route("api/RiskApi/{id}")]
public IRisk GetRiskByID(int id)
{
IRisk risk = _riskDataService.GetRiskByID(id);
if (risk == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return risk;
}
}

How to pass parameters from class to angular $scope

if i have this class:
public class MainMenuModel
{
public MainMenuModel(string transKey, string stateName, string displayUrl, bool hasSubMenu= false,List<SubMenuModel>subMenu=null)
{
TransKey = transKey;
StateName = stateName;
DisplayUrl = displayUrl;
HasSubMenu = hasSubMenu;
SubMenu = subMenu;
}
public string TransKey { get; set; }
public string StateName { get; set; }
public string DisplayUrl { get; set; }
public bool HasSubMenu { get; set; }
public List<SubMenuModel>SubMenu { get; set; }
}
And if i populate that class like this:
MainMenu.Add(new MainMenuModel("MY_TICKETS", "account.tickets", "/account/tickets/"));
MainMenu.Add(new MainMenuModel("TRANSACTION_HISTORY", "account.transactionhistory", "/account/transactions"));
MainMenu.Add(new MainMenuModel("PAYIN","account.payin","/account/payin"));
MainMenu.Add(new MainMenuModel("PAYOUT", "account.payout", "/account/payout"));
MainMenu.Add(new MainMenuModel("TICKET_PAYOUT", "account.ticketpayout", "/account/ticketpayout"));
MainMenu.Add(new MainMenuModel("SETTINGS","default","default",true,
new List<SubMenuModel>(){
new SubMenuModel("PERSONAL_INFORMATION","account.personalinformation","/account/personalinformation"),
new SubMenuModel("NOTIFICATIONS","account.notificationsettings","/account/notifications"),
new SubMenuModel("CHANGE_PASSWORD","account.changepassword","/account/passwordchange"),
new SubMenuModel("GAME_SETTINGS","default","default"),
}));
MainMenu.Add(new MainMenuModel("PROMOTIONS", "default", "default", true,
new List<SubMenuModel>(){
new SubMenuModel("BONUSES","default","default"),
new SubMenuModel("COMPETITIONS","default","default"),
new SubMenuModel("VOUCHER_REDEEM","default","default"),
}));
How can i call this in angular ..and pass it to $scope.something?Any suggestion?
If you need forming page on server - try to serialize this collection and put in on angular controller init
<div ng-controller="SomeController" ng-init="initialize('#Model.ToJson()')">
My recommendation is that you setup WebApi along with your existing ASP.NET MVC layer.
With that being said all you need to do is to implement the Rest services as GETs or POSTs and from your angular a simple invoke with $http :
Server Side
Using Microsoft Web Api Controller class "ValuesController" but it can be any class name which looks like this:
public class ValuesController : ApiController
{
public string Get(int id){ return "value"; }
...
Client Side
In my AngularJS Controller function it gets the value using $http service:
$http({method:'GET',url: '/api/values/1'}).success(function(data){
$scope.value =data;
})
First, you'll need to setup a server endpoint you can reach through ajax, that'll return the MainMenu structure as a JSON response.
Once you have that endpoint setup, there are several ways to get this data into angular, although I think the best way is to create an Angular service to manage this data. Something like this (bear with me, since I don't know the particulars of your project):
angular.module('application').factory('mainMenuService', ['$http', function($http) {
var ServiceInstance = {
_menuItems: [],
_fetchMenuItems: function() {
var self = this;
$http.post('your/server/endpoint').success(function(data, status, headers, config) {
// Clear exisiting items
self._menuItems.length = 0;
// Assuming menu items have been returned as JSON structure
// Add them all into the Service's mainMenu cache
self._menuItems.push.apply( self._menuItems, data.menuItems );
});
},
getMenuItems: function() {
if (!this._menuItems.length) {
this._fetchMenuItems();
}
// No need to wait on async operation because we're using the same array instance,
// and angular will observe this instance and detect when new items are added.
return this._menuItems;
}
};
return ServiceInstance;
}]);
Then, in your Angular controllers use the service:
// Notice how we reference the 'mainMenuService' here, and angular will inject it automatically
angular.module('application').controller('mainMenuController', ['mainMenuService', function (mainMenuService) {
$scope.mainMenuItems = mainMenuSevice.getMenuItems();
}]);
That way you'll decouple the data from the controller, and you can reuse the service in any controller where it's needed.

Web Api 2 with two method with the same HTTP verb an angularjs resources

I have this controller:
public class SeguiAttivazioneController : ApiController
{
[HttpGet]
public IHttpActionResult DoWork1()
{
...
return Ok();
}
[HttpGet]
public IHttpActionResult DoWork2()
{
...
return Ok();
}
[HttpGet] //I would like to have a search with GET verb, but I cannot validate my ModelState with dataAnnotation
public IHttpActionResult AnotherSearch(string filter1, string filter2, ...)
{
if (ModelState.IsValid)
{
...
return Ok();
}
return BadRequest(ModelState);
}
[HttpPost]
public IHttpActionResult DoSearch(SearchFilter filters)
{
if (ModelState.IsValid)
{
...
return Ok();
}
return BadRequest(ModelState);
}
[HttpPost]
public IHttpActionResult SubmitForm(FormData data)
{
...
return Ok();
}
}
As you can see I have two methods with same HttpVerbs (2 for GET and 2 for POST)... I don't know if I am violating REST principles... If so, I would like to avoid...
In this moment I am using AngularJs + NgResources to call my Controller..
public_area
.factory("SeguiAttivazioneService", function ($resource) {
//return {
// seguiAttivazione: $resource("/api/SeguiAttivazione/", null,
// {
// 'get2': { method: 'GET', url: '/api/SeguiAttivazione/GetActivationStatus2' }
// })
//};
return {
seguiAttivazione: $resource("/api/SeguiAttivazione/")
};
});
I am trying to do a GET:
$scope.getActivationStatus = function (event) {
event.preventDefault();
if ($scope.segui_attivazione_form.$valid) {
var request =
new SeguiAttivazioneService
.seguiAttivazione()
.$get({ }, getActivationStatusSuccess, getActivationStatusError);
}
};
But (correctly) I obtain an "Internal Server Error 500", because I have to GET method. How Can I solve this problem? (I suppose I will have same problem with POST too)
Thank you
UPDATE
Here the class of the filters
public class SearchFilter
{
[Required(ErrorMessage="")]
public string CodiceFiscale { get; set; }
[Required(ErrorMessage = "")]
[RegularExpression(#"^(?:\d{11,16})|(?:[a-zA-Z]{6}[a-zA-Z0-9]{2}[a-zA-Z][a-zA-Z0-9]{2}[a-zA-Z][a-zA-Z0-9]{3}[a-zA-Z])$", ErrorMessage = "Codice Fiscale o Partita IVA non validi")]
public string CodiceRichiesta { get; set; }
}
With this class I can use data Annotation to validate my model... If I do a GET Method I cannot use data annotation validation anymore...
Here is some explanation about a the REST Endpoints.
In REST we are manipulating ressources. As collections or individual.
Classics endpoint would be :
GET /rest/houses DATA : none -> will return a collection of houses
GET /rest/houses/{id} DATA : none -> will return the house find by its {id}
POST /rest/houses DATA : {"street":"3 bdv NY-city"} -> will create a new house object with the given data
PUT /rest/houses/{id} DATA : { "id":"{id}", "street":"4 bvd NY-city"} -> will update the whole house ressource find by its {id}
PATCH /rest/houses/{id} DATA : { "street":"4bvd NY-city" } -> will update the given fields of the house ressource find by its {id}
DELETE /rest/houses/{id} DATA : none -> will delete the house ressource find by its id.
There is too much things to know about restfull API that i can't give you all the keys. But try to find some good articles on the subjects such as :
http://www.restapitutorial.com/index.html
Not sure if this answer your question, but i hope it'll help you.
EDIT 1 :
Since i have to add some point about a restfull way to give some complicated action i'll give you the restfull url way to go.
In a restful world (extremely rare) you know only one entry point of your rest API let say this :
GET /rest/
This uri will respond you will all the services that the api can provide
Exemple :
{
"resources":"/rest/ressources",
"apiInfo" : "/rest/api/info"
}
To get your ressources informations you'll follow the link
GET response.resources
I may respond something like :
{
"houses":"/rest/ressources/houses/",
"cars" :"/rest/ressources/cars"
}
Now we want the houses
GET response.houses
Response :
{
"fields":[{
"constructionYear","street"
}],
"search":"/rest/houses"
"create":"/rest/houses"
}
etc... And at this place you can add some non restful endpoints. In a restful way. This action will be hold by a restful resource. Somes API that are using this kind of great Restful.
Standard Rest API :
https://developers.soundcloud.com/docs/api/reference#users
Restful API :
https://www.salesforce.com/us/developer/docs/api_rest/
The question is that the Web API infrastructure must have a way to choose one of the possible methods.
One way is changing the Web API route configuration, including an /{action}/ segment. If you do so it will work exactly like MVC, and you have to always include the action name.
The other way is making the received parameters different in each method, so that the Web API infrastructure can discover which method you're trying to invoke. You can read this answer I've written today for a similar question: How can I add multiple Get actions with different input params when working RESTFUL?.
As a final comment in that answer I say that the parameters can be also discerned by using route contraints.
The first solution of having to include the action name in all invocation is not RESTful, but do you need or prefer it to be RESTful for any particular reason?

Resources