I am attempting to call an httpGet on a web api controller from my angular controller but nothing I've been able to do allows me to pass multiple parameters.
I have tried adding the parameters to the route, (route/{email}/{firstName}/{lastName}.
I can verify that the angular method, getCurrentAlumniInfo is getting called and is attempting to route to a controller. I have used Postman, with the following URI,
"localHost: PortNumber/api/alumni/currentAlumniInfo?Email=flintrock&FirstName=Fredf&LastName=Flintstone"
Here are the relevant snippets. First, WebApiConfig using attribute routing:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
}
Next my api controller
[RoutePrefix("api/Alumni")]
public class AlumniController : ApiController
{
private readonly AlumniRepository _repository;
private readonly AuthRepository _authRepository;
public AlumniController()
{
_repository = new AlumniRepository();
_authRepository=new AuthRepository();
}
[Route("alumniInfo")]
[HttpGet]
public IHttpActionResult GetAlumniInfo([FromUri]string email,string firstName, string lasttName)
{
return Ok("Success" + lasttName);
}
}
}
Lastly, the angular (v. 1)
var getCurrentAlumniInfo = function() {
var alumniData = alumniModel.buildAlumniMatchData();
var deferred = $q.defer();
//string email, string lastName,string middleName, string firstName
$http.get(serviceBase + 'api/alumni/alumniInfo',
{
params: {
Email: encodeURIComponent(alumniData.email),
FirstName: alumniData.firstName,
//MiddleName: alumniData.middleName,
LastName: alumniData.lastName
}
})
.then(function(response) {
deferred.resolve(response);
})
.catch(function(response) {
alert(response.statusCode + " error: " + response.status);
deferred.reject(response);
});
return deferred.promise;
};
For what it's worth, I'm using v. 4.5.2 of the framework and Visual Studio 2015.
Any help appreciated.
either you should change your get method to post and pass the parameter as model, so that you can receive the model as parameter of your API.
Service
$http.post(serviceBase + 'api/alumni/alumniInfo',
{
params: {
Email: encodeURIComponent(alumniData.email),
FirstName: alumniData.firstName,
//MiddleName: alumniData.middleName,
LastName: alumniData.lastName
}
})
.then(function(response) {
...
})
.catch(function(response) {
...
});
Model
public class RecieveModel
{
public string Email {get;set;}
public string FirstName {get;set;}
public string LastName{get;set;}
}
API
[Route("alumniInfo")]
[HttpPost]
public HttpResponseMessage GetAlumniInfo(ReceiveModel model)
{
return Request.CreateResponse(HttpStatusCode.OK);
}
OR
change your route config defaults: new { id = RouteParameter.Optional ,id1 = RouteParameter.Optional,id2 = RouteParameter.Optional}
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}/{id1}/{id2}",
defaults: new { id = RouteParameter.Optional ,id1 = RouteParameter.Optional,id2 = RouteParameter.Optional});
In case others land here looking to solve the issue, this won't help them -- unless they too have coded too late at night. The answer is in my code. I typed a parameter string lasttname with two t's. Yes, it was literally that easy and that stupid and I looked at it for an hour without realizing it. It was only seeing the question this morning that it stood out like two t's in 480 pt. font.
With attribute routing, literally all that is required is that the parameter names match the URI variable names. One could create a model and add the [FromUri] attribute to the parameter, but it isn't necessary in this case.
So, the controller can simply be
[Route("alumniInfo")]
[HttpGet]
public IHttpActionResult GetAlumniInfo(string email, string firstName, string lastName)
{
return Ok("Success" + lastName);
}
Related
When I try to use the DELETE verb I either get a null parameter or the controller doesn't fire.
First I tried this:
[HttpDelete]
public IHttpActionResult Delete(Announcement announcement) {
_unitOfWork.Announcements.Remove(announcement);
_unitOfWork.Complete();
return Ok();
}
The controller fires, but announcement is null. If I check on the client side the parameter is not null, it is a properly formed object.
If I add a Route attribute like the below, then the controller doesn't fire at all.
[HttpDelete]
[Route("api/announcements/{announcement}")]
public IHttpActionResult Delete(Announcement announcement) {
_unitOfWork.Announcements.Remove(announcement);
_unitOfWork.Complete();
return Ok();
}
The client side is initiating the DELETE via angular.
myAPIservice.DeleteAnnouncement = function (announcement) {
console.log('In myAPIservice DeleteAnnouncement');
console.log(announcement);
return $http.delete(serviceURLRoot + 'api/announcements/', announcement, { withCredentials: true }).success(function (data) {
console.log('myAPIservice.DeleteAnnouncement Success');
});
};
EDIT ---
The Announcement class:
public class Announcement {
public int AnnouncementId { get; set; }
public string AnnouncementText { get; set; }
}
You can't send a 'body' with a DELETE call.
You could send the announcement id in the form of a parameter:
myAPIservice.DeleteAnnouncement = function (announcementId) {
console.log('In myAPIservice DeleteAnnouncement');
console.log(announcement);
return $http.delete(serviceURLRoot + 'api/announcements/', announcementId, { withCredentials: true }).success(function (data) {
console.log('myAPIservice.DeleteAnnouncement Success');
});
};
Then retrieve it from your database and delete it server side:
[HttpDelete]
[Route("api/announcements/{announcementId}")]
public IHttpActionResult Delete(int announcementId) {
var announcement = _unitOfWork.GetAnnouncementById(announcementId);
_unitOfWork.Announcements.Remove(announcement);
_unitOfWork.Complete();
return Ok();
}
Or of course delete by id... whatever works.
The important part to note here is that DELETE can't carry a payload / body.
I have a simple spring REST API as follow :
2 Entities (Product, Category)
#Entity
public class Category implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idCat;
private String nameCat;
#OneToOne // I want is as a one to one relation
#JoinColumn(name = "product_id")
private Product product;
// setters getters etc
}
#Entity
public class Product implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idProduct;
private String nameProduct;
#OneToOne(mappedBy = "product")
private Category category;
// setters getters etc
}
I have 2 RestController :
Product :
#RestController
public class ProductRestService {
#Autowired
ProductBusiness productBusiness; // Spring DATA Repo
#RequestMapping(value="/products", method = RequestMethod.POST)
public Ferme addProduct(#RequestBody Product p) {
return productBusiness.addProduct(p);
}
}
Category :
#RestController
public class CategoryRestService {
#Autowired
CategoryBusiness categoryBusiness; // Spring DATA Repo
#RequestMapping(value="/categories", method = RequestMethod.POST)
public Ferme addCategory(#RequestBody Category c) {
return categoryBusiness.addCategory(c);
}
}
And here is my AngularJS code which consumes the REST API :
var app = angular.module("myApp", []);
app.controller("myController", function($scope, $http) {
$scope.product = null;
$scope.addedProduct = null;
$scope.category = {"product":$scope.addedProduct, "nameCat":null};
$scope.saveProduct = function() {
$http.post("/products", $scope.product)
.then(function(response){
$scope.addedProduct = response.data;
}, function(err){
console.log(err);
});
};
$scope.saveCat = function() {
$http.post("/categories", $scope.category)
.then(function(response){
$scope.addedCategory = response.data;
}, function(err){
console.log(err);
});
};
});
I call the methods saveProduct() and then saveCategory() by using a ng-click in an html file, the first method works and the value of the returned Product is saved in the $scope.addedProduct, then I click to save the Category it works too but the product doesn't get saved in the category even if i'm using $scope.category = {"product":$scope.addedProduct, "nameCat":null}; then I pass the category object here $http.post("/categories", $scope.category){.. which means that the previously addedProduct should be saved inside the $scope.category but it's not the case, a value NULL is added in the category table in the database instead of the actual product_id which I verified it exists in $scope.addedProduct. I don't know how to solve this. Sorry if it seems a bit unclear, and I can provide more info if needed.
Thank you in advance.
EDIT :
//
Try declaring $scope.category = {"product":$scope.addedProduct, "nameCat":null}; as
$scope.category = {product:$scope.addedProduct, "nameCat":null};
and then instead updating $scope.addedProduct = response.data; update with
$scope.category.product = response.data;
in your code addedProduct is nowhere backtracking where its assigned and its not going to work.
Solved, I had to put $scope.category = {"product":$scope.addedProduct, "nameCat":null}; inside the saveProduct method.
Im trying to combine those technologies, but nothing good comes, as entity framework meta-datas doesn't get consumed by breeze.js, even all configurations where setup, it's a bit tricky situation, there is literally no examples of that, so this is my sample code which doesn't work properly, but somehow maybe someone will find my mistake and eventually help to solve this puzzle or will find it as starting point.
OdataService.ts
'use strict';
module twine.components {
class MetadataStoreOptions implements breeze.MetadataStoreOptions{
namingConvention:breeze.NamingConvention = breeze.NamingConvention.defaultInstance;
}
class Manager implements breeze.EntityManagerOptions {
metadataStore: breeze.MetadataStore;
constructor( public dataService: breeze.DataService) {
}
}
class DataServiceOptions implements breeze.DataServiceOptions {
serviceName = 'http://twine.azurewebsites.net/odata';
hasServerMetadata = true;
}
export class ODataService {
options: Manager;
manager: breeze.EntityManager;
metadataStore: breeze.MetadataStore;
storeOptions: MetadataStoreOptions;
static $inject: string[] = ['$http', '$rootScope'];
cache: twine.Model.IEntity[];
constructor(private $http: ng.IHttpService, private $rootScope: ng.IRootScopeService){
this.storeOptions = new MetadataStoreOptions();
this.metadataStore = new breeze.MetadataStore(this.storeOptions);
this.options = new Manager( new breeze.DataService( new DataServiceOptions() ));
this.options.metadataStore = this.metadataStore;
this.manager = new breeze.EntityManager( this.options );
breeze.config.initializeAdapterInstance('dataService', 'webApiOData', true);
//this.manager.fetchMetadata((meta) => {
// this.metadataStore.importMetadata(meta);
//});
}
All( query:breeze.EntityQuery, successCallback: Function, failCallback?: Function ): void {
this.manager.executeQuery( query )
.then( ( data: breeze.QueryResult ) => {
successCallback( data );
this.$rootScope.$apply();
})
.catch( ( reason: any ) => {
if ( failCallback ) {
failCallback( reason );
}
});
}
Get( key:number, successCallback: Function, failCallback?: Function ): void {
//this.manager.fetchMetadata();
//var entityType = this.manager.metadataStore.getEntityType('Tag');
//var entityKey = new breeze.EntityKey(entityType, key);
this.manager.fetchEntityByKey( 'Tag', key )
.then( ( data: breeze.EntityByKeyResult ) => {
successCallback( data );
this.$rootScope.$apply();
})
.catch( ( reason: any ) => {
if ( failCallback ) {
failCallback( reason );
}
});
}
}
}
And this is tagController.ts
'use strict';
module twine.routes {
interface ITagsScope extends ng.IScope {
vm: TagsCtrl;
}
interface ITagsCtrl extends twine.components.ITwineRoute{
tags:any[];
getTags: () => void;
tag: any[];
getTag: (id:number) => void;
}
export class TagsCtrl implements ITagsCtrl{
/* #ngInject */
static controllerId: string = 'TagsController';
static controllerAsId: string = 'tagsCtrl';
static $inject: string[] = ["$scope", "ODataService", '$route'];
entityQueryName: string = 'Tag';
query: breeze.EntityQuery;
tags:any;
tag: any;
constructor (private $scope: ITagsScope, private ODataService: twine.components.ODataService, $route: ng.route.IRouteService) {
this.query = new breeze.EntityQuery(this.entityQueryName);
if($route.current && $route.current.params.id){
this.getTag($route.current.params.id);
}
else {
this.getTags();
}
}
getTags() {
this.ODataService.All(this.query , (data) => {
this.tags = data.results[0].value;
}, (error) => {
console.log('error', error);
});
}
getTag(id:number){
this.ODataService.Get(id , (data) => {
this.tag = data.results[0].value;
}, (error) => {
console.log('error', error);
});
}
}
}
There are many errors, on different configurations, sometimes it's There is no resourceName for this query or EntityKey must be set, or Other stupid errors which are indeed doesn't have to appear because it's a typescript which doesn't allow type mismatches, but the configuration itself is not correct.
And this is abstract controller
[EnableCors(origins: "*", headers: "*", methods: "*")]
public abstract class EntityController<T> : ODataController where T: Entity
{
protected ODataRepository<T> repo = new ODataRepository<T>();
private static ODataValidationSettings _validationSettings = new ODataValidationSettings();
public EntityController()
{
}
// GET: odata/Entity
[EnableQuery]
public IQueryable<T> Get(ODataQueryOptions<T> queryOptions)
{
try
{
queryOptions.Validate(_validationSettings);
}
catch (ODataException ex)
{
Trace.WriteLine(ex.Message);
return null;
}
return repo.All();
}
// GET: odata/Entity(5)
[EnableQuery]
public SingleResult<T> Get([FromODataUri] long key, ODataQueryOptions<T> queryOptions)
{
try
{
queryOptions.Validate(_validationSettings);
}
catch (ODataException ex)
{
Trace.WriteLine(ex.Message);
return null;
}
return SingleResult.Create(repo.All().Where(x=>x._id == key));
}
//ommitted
}
And lastly this is ASP.NET webApi configuration
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Конфигурация и службы веб-API
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
// Маршруты веб-API
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//CORS
var cors = new EnableCorsAttribute(
"*",
"*",
"*",
"DataServiceVersion, MaxDataServiceVersion"
);
config.EnableCors(cors);
// Маршруты Odata
//config.EnableQuerySupport();
config.AddODataQueryFilter();
Builder<Account>(config);
Builder<Branch>(config);
Builder<Bucket>(config);
Builder<Ingredient>(config);
Builder<Like>(config);
Builder<Meetup>(config);
Builder<Shot>(config);
Builder<Skill>(config);
Builder<Tag>(config);
Builder<Team>(config);
}
private static void Builder<T>(HttpConfiguration config) where T: class
{
var entityType = Activator.CreateInstance<T>().GetType();
if (entityType != null)
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<T>(entityType.Name);
config.Routes.MapODataServiceRoute("odata_" + entityType.Name, "odata", builder.GetEdmModel());
}
}
}
For testing purpose i have this working backed OData service at http://twine.azurewebsites.net/odata/Tag, (currently no restrictions by CORS, feel free) last entity can be changed to other name based on webApi configuration Build method. Please feel free to ask any other information. If someone need whole source, im willing to publish on github
Update
Forget to mension, problem is in method Get of ODataService. I cannot bind metadata from server to breeze, method All works fine. But query fetchByEntityKey throws errors as described above
evc, please take a look at the following article under AngularJs Apps/Projects. You'll find sample projects that you can actually follow and learn using AngularJs, Breeze, Asp.net Web api. You're right, there's a lot of material out there but there not that effective. Hope it helps. http://www.learn-angularjs-apps-projects.com/
Have a look at the Breeze samples, especially WEB Api OData and Breeze. It's sample tutorial for a angularjs+webapi+odata+breeze, no typescript (but isn't javascript just typescript :). For your WEB Api controllers you should definitely install this nuget package :
PM> Install-Package Breeze.Server.WebApi2
This will allow you to build a breeze aware api controller and expose breeze odata metada easily using the Breeze.Server.ContextProviderClass.
Also you can check the excelent but paid training from Pluralsight. It covers SPA from scratch using JavaScript, Angular, and Breeze.
http://www.pluralsight.com/courses/build-apps-angular-breeze
I'm trying to post bookData to my WebAPI2 service by using the "$resource" in AngularJS. This works fine when it gets a book from the BookService first, modify the book and post it to the BookService.
I'm looking for a way posting a book without getting a book from the BookService first.
First, i have created an REST-service in WebAPI2:
public IHttpActionResult Get([FromUri] string id)
public IHttpActionResult Post([FromBody] BookData bookData)
Second, in the controller i'm trying to get en post some bookdata. The BookService is injected in the Controller
var bookData;
var book = BookService.get({ id: 1 }, function()
{
bookData = book;
});
$scope.sendBook = function () {
bookData.Title = 'Test REST';
var book = new BookService(bookData);
book.$save();
}
I've tried the following, but it doesn't work
var bookData = {Title: 'Test REST'};
$scope.sendBook = function () {
var book = new BookService(bookData);
book.$save();
}
There are only some static data in it.
public class BookController : ApiController
{
[HttpGet]
public IHttpActionResult Get([FromUri] string id)
{
BookViewModel vm = new BookViewModel
{
Title = "TEST REST"
};
return Ok(vm);
}
[HttpPost]
public IHttpActionResult Post([FromBody] BookData bookData)
{
BookViewModel vm = new BookViewModel
{
Title = "TEST REST"
};
return Created(new Url(Request.RequestUri.ToString()), vm);
}
}
I've find a solution. Assign the bookData to $scope
Something like:
$scope.bookData = { Title: 'Some title' };
I am trying to do a http.Get request by passing a single string as parameter.
The angular code I have tried so far are,
1)
var abseURL = 'http://localhost:17493/api/UsersApi/GetUsersByEmail/';
var email ='testemail#domain.com';
$http.get(abseURL + email).then(OnComplete, OnError);
returns a http 404 error
2)
$http({
url: abseURL,
method: "GET",
params: { email: 'testemail#domain.com'}
}).then(OnComplete,OnError);
The above http.get request returns a Http 500 error
My webApi controller method is defined as,
[ActionName("GetUsersByEmail")]
[CamelCasedApiControllerAttribute]
public async Task<IHttpActionResult> GetUsersByEmail(string email)
{
Users users = await db.Users.FindAsync(email);
if (users == null)
{
return NotFound();
}
return Ok(users);
}
Also in my Global.asax.cs file I have mapped the routes as follows
protected void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configure(config =>
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = "get", id = RouteParameter.Optional }
);
});
}
If I pass an Int paramter it find the correct action in my WebApiController
But when I change the call with a string parameter, it it unable to find the correct action method.
you can rename email parameter to id:
public async Task<IHttpActionResult> GetUsersByEmail(string id)
{
Users users = await db.Users.FindAsync(id);
if (users == null)
{
return NotFound();
}
return Ok(users);
}
Looks to me like the routing is not setup to handle this. You could try using the following instead:
var abseURL = 'http://localhost:17493/api/UsersApi/GetUsersByEmail/?email=';
var email ='testemail#domain.com';
$http.get(abseURL + email).then(OnComplete, OnError);