Posted object with collection does not get bound to http body content - c#

I am doing a http put method to the server sending an object with a list of PupilsScores.
The TestId is bound, but the PupilsScores collection is null.
I also tried [FromBody] but that should be the default I assume doing a post/put.
So why is my PupilsScores collection null?
CLIENT
updateTestAssignment(pupilsScores, testId) {
return this.http.put('/api/testassignments/' + testId, { pupilsScores: pupilsScores }).map(res => res.json());
}
SERVER
public class UpdateTestAssignmentRequestDto
{
public int TestId { get; set; }
public IEnumerable<PupilsScoresRequestDto> PupilsScores { get; set; }
}
[HttpPut("~/api/testassignments/{testId:int}")]
public async Task<IActionResult> Put(UpdateTestAssignmentRequestDto dto)
{
return NoContent();
}

You need to specify the Content-Type too. Its default value is text/plain.
import { Headers, RequestOptions } from '#angular/http';
let headers = new Headers({ 'Content-Type': 'application/json' }); // for ASP.NET MVC
let options = new RequestOptions({ headers: headers });
this.http.post(url, JSON.stringify(product), options).map(....)

Related

Back-end doesn't recognize the json body Angular

My Angular (front-end) is making a POST request with JSON body and sends it to the server as follows :
const reponse = {
"Id_Harry" : this.harry.getId,
"x_harry" : this.harry.getX(),
"y_harry" : this.harry.getY(),
"Pv_Harry" : this.harry.getPv(),
"Force_Harry" : this.harry.getForce()
};
fetch(url, {
method: 'POST',
body: JSON.stringify(reponse),
headers :{
"Content-Type" : "application/json ,charset=UTF-8"
}
}).then((resp) => resp.json()).then(function(response) {
console.info('fetch()', response);
return response;
});
The back-end doesn't seem to recognize the server json body.The back-end code is a post that updates all the database with the values given by the json body.
The backend code is :
[Route("api/Harry/update")]
[HttpPost]
public IHttpActionResult PostHarry([FromBody] HarryViewModel harry)
{
try
{
if (harry == null)
{
return NotFound();
}
HarryDTO ha = new HarryDTO();
ha.Id_Harry = harry.Id_Harry;
ha.Pv_Harry = harry.Pv_Harry;
ha.Force_Harry = harry.Force_Harry;
ha.x_harry = harry.x_harry;
ha.y_harry = harry.y_harry;
_harryService.UpdateHarry(ha);
return Content(HttpStatusCode.OK,"OK");
}
catch (Exception ex)
{
return Content(HttpStatusCode.InternalServerError, "Something happenes!");
}
}
HarryViewModel class is :
public class HarryViewModel
{
public int Id_Harry { get; set; }
public int Pv_Harry { get; set; }
public int Force_Harry { get; set; }
public int x_harry { get; set; }
public int y_harry { get; set; }
}
The back-end doesn't seem to recognize the Angular json format. I get this error :
415 Unsupported Media Type. "no mediatypeformatter is available to read an object of type 'harryviewmodel' from content with media type 'application/octet-stream'."
Take a better look at the ERROR :
Thank you so much for your attention.
You are not passing response to the api.
const reponse = {
"Id_Harry" : this.harry.getId,
"x_harry" : this.harry.getX(),
"y_harry" : this.harry.getY(),
"Pv_Harry" : this.harry.getPv(),
"Force_Harry" : this.harry.getForce()
};
// Instead of this.checkResponse, pass "response" which is your post data.**
this.http.post(url, response).toPromise().then((data:any) => {
console.log(data);
console.log(data.json.test);
this.json = JSON.stringify(data.json);
});
And if API is not recognizing post data type, then pass on proper content type header to your post request like this:-
const reponse = {
"Id_Harry" : this.harry.getId,
"x_harry" : this.harry.getX(),
"y_harry" : this.harry.getY(),
"Pv_Harry" : this.harry.getPv(),
"Force_Harry" : this.harry.getForce()
};
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
this.http.post(url, response, httpOptions).toPromise().then((data:any) => {
console.log(data);
console.log(data.json.test);
this.json = JSON.stringify(data.json);
});
You can read more about HttpClient options here
I have checked your code:
Here's what you need to do :
import { HttpHeaders } from '#angular/common/http';
const response = {
"Id_Harry" : this.harry.getId,
"x_harry" : this.harry.getX(),
"y_harry" : this.harry.getY(),
"Pv_Harry" : this.harry.getPv(),
"Force_Harry" : this.harry.getForce()
};
const headers = new HttpHeaders();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
this.http.post(url, response, { headers: headers }).toPromise().then((data:any) => {
console.log(data);
console.log(data.json.test);
this.json = JSON.stringify(data.json);
});

Angularjs is sending null values to API, why?

I am sending an id and a list from angular to C# web API, but it is received by null when debugging the method, without knowing the reason.
angular service
this.deletevacs = function (staffid, vacs) {
return $http.post("/AssignUserToDepartmentapi/api/assignuser/deletevacs?staffid=" + staffid + '&vacs=' + JSON.stringify(vacs))
}
angular js
var result = DevExpress.ui.dialog.confirm("Are you sure want to delete vacations assigned employees ?", "Confirm changes");
result.done(function (dialogResult) {
if (dialogResult) {
var promisePost = Assignments.deletevacs($scope.SelectedEmp1.staffkey, $scope.oldvacs);
promisePost.then(function (pl) {
toastr.success("Successfully deleted");
C#
[HttpPost]
public HttpResponseMessage deletevacs(int staffid,int[] vacs)
{
try
{
obj.ExecNonQuery(string.Format("delete from HR_AssRole_Dep where staff_key={0} and Role=7 and Current_Flag=1 and VacMKey ={1}"
, staffid
, vacs));
}
catch (Exception ex)
{
}
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
any help, thanks in advance
new C#
[HttpPost]
public HttpResponseMessage deletevacs([FromBody] role r)
{
obj.ExecNonQuery(string.Format("delete from HR_AssRole_Dep where staff_key={0} and Role=7 and Current_Flag=1 and VacMKey ={1}"
,r.Staff_Key
, r.VacMKey));
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
class
public class role
{
//public int Ass_Dep_Key { get; set; }
public int Dep_Key { get; set; }
public int Staff_Key { get; set; }
public int Role { get; set; }
public List<int> VacMKey { get; set; }
public short SelfApprov { get; set; }
}
new angular
var deletevacssss = { Staff_Key: $scope.SelectedEmp1.staffkey, VacMKey: $scope.selectedvacs };
var result = DevExpress.ui.dialog.confirm("Are you sure want to delete vacations assigned employees ?", "Confirm changes");
result.done(function (dialogResult) {
if (dialogResult) {
var promisePost = Assignments.deletevacs(deletevacssss);
promisePost.then(function (pl) {
toastr.success("Successfully deleted");
new angular service
this.deletevacs = function (deletes) {
return $http.post("/AssignUserToDepartmentapi/api/assignuser/deletevacs", deletes)
}
when i make a debug, the r object from role class get the staffkey from angular correctly but the list of vacmkey count by 0 ???
you should pass the parameter values for post method in params property .
var data = {staffid: staffid, vacs:vacs};
$http({
url: "/AssignUserToDepartmentapi/api/assignuser/deletevacs",
method: "POST",
params: data
})
or try this below way
var data = {staffid: staffid, vacs:vacs};
$http.post("/AssignUserToDepartmentapi/api/assignuser/deletevacs", data ).then(function (data, status, headers, config) {
alert("success");
},function (data, status, headers, config) {
alert("error");
});
API code should be like
[HttpPost]
public HttpResponseMessage deletevacs([FromUri]int staffid, [FromUri] int[] vacs)
{
...
}
Solution 1:
Use [FromUri] attribute in your HttpPost method parameter since you are getting the value from the querystring in the Url.
Try this:
[HttpPost]
public HttpResponseMessage deletevacs([FromUri]int staffid, [FromUri] int[] vacs)
{
try
{
obj.ExecNonQuery(string.Format("delete from HR_AssRole_Dep where staff_key={0} and Role=7 and Current_Flag=1 and VacMKey ={1}"
, staffid
, vacs));
}
catch (Exception ex)
{
}
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
Solution 2:
Create an object that contains the staffId and vacs properties and assign it to the parameter. Add the [FromBody] attribute to before the object parameter. Then in your httpPost call, put the parameter inside the body of the request instead of the Url.
Google about the use of [FromBody] in webapi.
Solution 3:
Istead of using HttpPost, why not use HttpDelete method. It is clear in your problem that your intention is to delete records. In web API, you can use the HttpDelete attribute instead of HttpPost. To do this, change your attribute to HttpDelete from HttpPost, and in your paramter, you can just pass the [FromUri] int StaffId only and inside your API, all the vacs record related that StaffId, you can delete programmatically in DB. When calling your API use the $http.delete endpoint method instead.

ASP.Net Web API Http routing and non JSON responses

I want to mimic behaviour of existing web service. Here is a very simplified example showing what I want to achieve.
I use ASP.Net Web API routing: it's quite simple to configure routes with it.
Requirements, part 1: query:
GET whatever.../Person/1
shall return JSON:
Content-Type: application/json; charset=utf-8
{"id":1,"name":"Mike"}
That's piece of cake:
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
}
// In ApiController
[HttpGet]
[Route("Person/{id}")]
public Person GetPerson(int id)
{
return new Person
{
ID = id,
Name = "Mike"
};
}
Requirements, part 2: query:
GET whatever.../Person/1?callback=functionName
shall return javascript:
Content-Type: text/plain; charset=utf-8
functionName({"id":1,"name":"Mike"});
Any ideas how to achieve this (part 2)?
The ApiController would need to be modified to satisfy the desired behavior
Simple example based on provided code
//GET whatever.../Person/1
//GET whatever.../Person/1?callback=functionName
[HttpGet]
[Route("Person/{id:int}")]
public IHttpActionResult GetPerson(int id, string callback = null) {
var person = new Person {
ID = id,
Name = "Mike"
};
if (callback == null) {
return Ok(person); // {"id":1,"name":"Mike"}
}
var response = new HttpResponseMessage(HttpStatusCode.OK);
var json = JsonConvert.SerializeObject(person);
//functionName({"id":1,"name":"Mike"});
var javascript = string.Format("{0}({1});", callback, json);
response.Content = new StringContent(javascript, Encoding.UTF8, "text/plain");
return ResponseMessage(response);
}
Of course you would need to do proper validation on the call back as this currently open up the API for script injection.

Api lost lost param

Hi I have this code to run my api method
export class MessageService {
constructor(private http: Http) {
}
addMessage(textToSend: string) {
return this.http.post("/api/SendMessage", textToSend); //<- Everytime i have some text in textToSend and this is ok
}
}
And after in my Api my param is equals to null
[HttpPost]
[Route("/api/SendMessage")]
public void SendMessage(string msg) //null value
{
//some code
}
Your controller action is accepting a query parameter, not a router parameter or model.
If you want to accept a route parameter, you need to add it to the route.
If you want to pass a model or value in the body, you must mark the parameter with [FromBody] attribute.
[HttpPost]
[Route("/api/SendMessage")]
public void SendMessage([FromBody]string msg)
{
MessageBox MsgBox = new MessageBox();
MsgBox.AddMsgToMsgBox(msg);
}
If you don't define anything, the controller expects the parameter to be passed as query /api/SendMessage?msg=someMessage (which you shouldn't do in a REST service, as it's not very "RESTful"
Possible solution 1:
addMessage(textToSend: string) {
let body = JSON.stringify({ textToSend });
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post("/api/SendMessage/", body, options);
}
// Server side -1
[HttpPost]
[Route("/api/SendMessage")]
public void SendMessage([FromBody]IDictionary<string, string> msg)
{
var textToSend = msg["textToSend"];
}
// Or create a model and use it
//Server side -2
public class Model
{
public string textToSend { get; set; }
}
public void SendMessage([FromBody]Model model)
Possible solution 2:
addMessage(textToSend: string) {
return this.http.post("/api/SendMessage/" + textToSend);
}
[HttpPost]
[Route("/api/SendMessage/textToSend")]
public void SendMessage(string textToSend)
{
//some code
}
try
addMessage(textToSend: string) {
return this.http.post("/api/SendMessage", msg); //<- Everytime i have some text in textToSend and this is ok
}
changed the name of the variable to match the one you are expecting in the controller

Angular Upload File and Additional Data ASP WebAPI

I'm trying to upload a form with some text fields and a file to my WebAPI. Currently I always get a 415 error (a breakpoint in the ASP controller doesn't gets hit). My code looks like this:
Angular Service
// 'Upload' is from ng-file-upload
function applicationService(settings, $http, Upload) {
var createCustomApplication = function(application) {
var url = settings.baseUrl + '/api/applications/custom';
var data = new FormData();
angular.forEach(application, function (value, key) {
data.append(key, value);
});
return Upload.upload({
url: url,
data: data,
method: 'POST'
});
};
return {
createCustomApplication: createCustomApplication
}
}
WebAPI controller
[ResponseType(typeof(ApplicationModel))]
[HttpPost, Route("api/applications/custom")]
public IHttpActionResult CreateCustomApplication([FromBody]ApplicationModel application)
{
var file = HttpContext.Current.Request.Files[0];
return Ok();
}
If you want to include a form with files as parameter to an action it is necessary to add a custom media formatter. Fortunately someone already created a Nuget-package for this. Configuration is easy. Install the package and add a line to your WebApiConfig-file.
This package allows you to use a HttpFile-object which captures your file either directly as a parameter or inside a model. From the docs:
[HttpPost]
public void PostFileBindRawFormData(MultipartDataMediaFormatter.Infrastructure.FormData formData)
{
HttpFile file;
formData.TryGetValue(<key>, out file);
}
or
public class PersonModel
{
public string FirstName {get; set;}
public string LastName {get; set;}
public DateTime? BirthDate {get; set;}
public HttpFile AvatarImage {get; set;}
public List<HttpFile> Attachments {get; set;}
public List<PersonModel> ConnectedPersons {get; set;}
}
//api controller example
[HttpPost]
public void PostPerson(PersonModel model)
{
//do something with the model
}
I've had the same issue.
You should add the content type in your POST request.
headers: {
'Content-Type': undefined
},
You're code looks very similar to what I use. Perhaps the issue is to do with the multipart/form-data header value. I'm not experienced enough to say what is wrong in your implementation, but perhaps try this alternate async await approach.
[Route("api/applications/custom")]
[HttpPost]
public async Task<HttpResponseMessage> Upload()
{
MultipartMemoryStreamProvider memoryStreamProvider;
try
{
if (!Request.Content.IsMimeMultipartContent())
{
this.Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
}
memoryStreamProvider = await Request.Content.ReadAsMultipartAsync();
}
catch (Exception e)
{
return Request.CreateResponse(HttpStatusCode.BadRequest, string.Format("An error has occured while uploading your file. Error details: '{0}'", e.Message));
}
// do something with your file...
return Request.CreateResponse(HttpStatusCode.OK);
}
Here is my JS code. I also use promises instead of what ever is returned (if anything) from the Upload function.
$scope.submit = function() {
if ($scope.form.file.$valid && $scope.file) {
$scope.upload($scope.file);
}
};
$scope.upload = function(file) {
Upload.upload({
url: 'api/applications/custom',
data: { file: file }
}).then(function(response) {
// report the success to the user
}, function(response) {
// report the error to the user
}, function(evt) {
// report the progress of the upload to the user
$scope.uploadProgress = evt.loaded / evt.total;
});
};
I used the following article as a basis for my solution: http://monox.mono-software.com/blog/post/Mono/233/Async-upload-using-angular-file-upload-directive-and-net-WebAPI-service/

Categories