Delete not working with Web Api - angularjs

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.

Related

Passing multiple paramters to web api 2 from angularJs Controller

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);
}

Byte array and JSON in [FromBody]

I am trying pass an object which consists of different data type. I am always getting null value for orderDetails in Web API.
However if do this,
purchaseOrder.Attachments = null,
in the client then orderDetails is no longer null and I have other informations like "SendEmail" and PurchaseOrderNumber.
It looks I might not be correctly set the parameter in the client (angular 2).
However testing the same Web Api method from Console app works fine and I am not getting a null value.
Do I need to separate the JSON data and byte array?
regards,
-Alan-
Models
public class Attachments
{
public int AttachmentId { get; set; }
public string FileName { get; set ;}
public byte[] FileData { get; set ;}
}
public class UpdatePurchaseOrderViewModel
{
public bool SendEmail { get; set; }
public int PurchaseOrderNumber { get; set; }
public Attachments Attachments { get; set;
}
Here is my Web API put method definition
[HttpPut("AddPurchaseOrderNumber/{purchaseOrderId}")]
public StatusCodeResult AddPurchaseOrderNumber(int purchaseOrderId, [FromBody] UpdatePurchaseOrderViewModel orderDetails)
{
try
{
var status = _service.AddPurchaseOrderNumber(purchaseOrderId, orderDetails);
if (status == 200)
_unitOfWorkAsync.SaveChanges();
else return StatusCode(status);//No Data
}
catch
{
return StatusCode(400); // Bad Request
}
return StatusCode(200);//OK
}
Typescript snippet
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept','application/json');
let options = new RequestOptions({ headers: headers });
var body = JSON.stringify(
purchaseOrder
);
var uri = 'http://localhost:33907/api/purchaseorder/addpurchaseordernumber/' + purchaseOrderId;
return this._http.put(uri, body , options)
.map((response: Response) => {
let data = response.json();
if (data) {
return true;
}
else {
return false;
}
})
Update
The orderDetails is created as below
let file = Observable.create((observer) => {
let fr = new FileReader();
let data = new Blob([this.attachment]);
fr.readAsArrayBuffer(data);
fr.onloadend = () => {
observer.next(fr.result);
observer.complete();
};
fr.onerror = (err) => {
observer.error(err);
}
fr.onabort = () => {
observer.error("aborted");
}
});
file.map((fileData) => {
//build the attachment object which will be sent to Web API
let attachment: Attachments = {
AttachmentId: '0',
FileName: this.form.controls["attachmentName"].value,
FileData: fileData
}
//build the purchase order object
let order: UpdatePurchaseOrder = {
SendEmail: true,
PurchaseOrderNumber:this.form.controls["purchaseOrderNumber"].value * 1, //for casting purpose
Attachments: attachment
}
console.log("Loading completed");
return order;
})
When sending objects that have byte arrays as a property back and forth between a client to a WebAPI endpoint, I typically use a DTO that stores the property to explicitly define it as a Base64 string. On the server side I map the DTO to my entity by converting the Base64 string to / from the byte array for server side operations and storing in the database.
The serializer will do something like this automatically but the format passed from JavaScript may not match what the WebAPI JSON serializer is expecting (which is why it's working from your C# Console App).
You didn't include how you are creating the purchaseOrder object in your JavaScript so I can't comment on how that object is being setup - which may be where your issue is.

Getting status 500 when using angularjs $http to get data from server

I am working on an asp.net mvc application and I am using Entity Framework and AngularJS in it. I am using AngularJS's $http service to call an action method and retrieve data from the server. The correct data is retrieved from the server (I confirmed this by debugging), but somehow an error occurs after the action method returns the retrieved data and the error callback function is fired instead of the success callback function. And then I get a status 500 in the browser's console.
Here are the involved blocks of codes:
(From angularjs controller)
$http({
url: rootUrl + "User/GetUser",//'#Url.Action("GetUser","User")',
method: 'POST',
params: {
uname: $scope.username,
pword: $scope.pass
}
}).then(function (response) {
alert('success!');
$scope.user = response.data;
if ($scope.user.Fullname != undefined) {
$http({
url: rootUrl + "Session/Set",
method: "POST",
data: {
"key": "curr_user",
"value": JSON.stringify($scope.user)
}
});
window.location.href = rootUrl + 'Product/List/';
} else {
//invalid login
$("input[name='password']").select();
$("#validation-summary").html("Wrong email or password.");
$scope.invalidlogin = true;
$(btnLogin).removeClass('disabled');
$(btnLogin).text("Submit");
}
(From mvc controller)
[HttpPost]
public JsonResult GetUser(string uname, string pword)
{
JBManager manager = null;
using (SE_Context db = new SE_Context())
{
try
{
manager = db.Managers
.Include("Transactions.Items")
.Where(m => m.Username == uname && m.Password == pword)
.FirstOrDefault();
//At this point, manager has the desired data
return Json(manager, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return null;
}
}
}
And here's a screenshot of the error in the browser:
Would really appreciate any help. Thanks!
UPDATE:
Everything was working fine before I used Entity Framework. (Just in case it has something to do with the issue)
I think your issue is nested objects.You can flatten object graphs that contain nested objects using DTOs (Data Transfer Objects).
You can just try simple example as like below.If it'll work then you need to extend it to work with your EF query.
public class MyDto
{
public string Name { get; set; }
}
[HttpPost]
public JsonResult GetUser(string uname, string pword)
{
JBManager manager = null;
using (SE_Context db = new SE_Context())
{
try
{
//construct the DTO here
manager = db.Managers.Select(a=> new MyDto(
{
Name = a.Name
})).FirstOrDefault(m => m.Username == uname && m.Password == pword);
return Json(manager, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return null;
}
}
}
You can read more about DTOs here : Create Data Transfer Objects (DTOs)

Jhipster, AngularJS Client Side Model Rest One-To-Many

I have an application with two entities:
Employer (OneToMany with Employee)
Employee (ManyToOne with Employer)
Employer Entity:
#OneToMany(mappedBy = "employer")
#JsonIgnore
private Set<Employee> employees = new HashSet<>();
Employee Entity:
#ManyToOne
private Employer employer;
I know that the #JsonIgnore remove the possibility to fetch the list of employee from JSON. But I don't know what should I do to retrieve them.
Client Side
EmployerService.js:
angular.module('app')
.factory('Employer', function ($resource) {
return $resource('api/employer/:id', {}, {
'query': { method: 'GET', isArray: true},
'get': {
method: 'GET',
transformResponse: function (data) {
data = angular.fromJson(data);
return data;
}
},
'update': { method:'PUT' }
});
});
Should I create a new rest call to fetch them here, (with a new rest RequestMapping on the server side Rest Controller) and remove the #JsonIgnore?
Thank you.
So to resolve this, I used a service to retrieve the data in a Eager way.
Employer and Employee entities don't change.
EmployerService
#Service
#Transactional
public class EmployerService {
#Inject
private EmployerRepository employerRepository;
#Transactional(readOnly = true)
public Set<Employee> getAllEmployeeForEmployerId(Long id) {
Employer employer = employerRepository.findOne(id);
if ( employer == null ) {
return null;
}
employer.getEmployees().size(); //Fetch Eager on Lazily
return employer.getEmployees();
}
}
In EmployerService.js add a new factory
.factory('EmployerFactory', function($resource) {
return {
employees: $resource('api/employer/employee/:id'),
}
And add the matching RestRequest in your controller.

How can I specify a return type for a $http call with Typescript?

Here's my function so far. I already added an interface for the two input parameters but because of the way that this is coded I really don't know how to deal with the return parameters:
function httpGetQuestion(q: ITestQuestion, action: string) {
return $http({
url: '/api/Question/GetByUId/' + q.questionUId + '/' + action,
method: "GET"
})
}
also:
httpGetQuestion(q, 'fetch')
.success(function (data) {
Here is the data that gets returned from the $http call:
public class Q
{
public string Answer { get; set; }
public string Text { get; set; }
}
You can do it inline:
function httpGetQuestion(q: ITestQuestion, action: string) : ng.IHttpPromise<Q> {
You could use the typing for your return value as ng.IHttpPromise:- Using and interface for your service and adding the interface as typing on your dependency, and adding comments (JSDoc) on your interface methods will give you nice intellisense as well providing the description and other information about the method.
Something like this:-
export interface IMyService{
/**
* Returns promise which will resolve to .....
* #returns {ng.IHttpPromise<Q>}
*/
httpGetQuestion(q: ITestQuestion, action: string) : ng.IHttpPromise<Q>;
}
class MyService implements IMyService{
constructor(...
private $http: ng.IHttpService,
...) {
}
.....
httpGetQuestion(q: ITestQuestion, action: string) {
return this.$http({
url: '/api/Question/GetByUId/' + q.questionUId + '/' + action,
method: "GET"
});
}
....
}

Resources