AngularJs $http.get: Sending array of objects as params - javascript

I am having issues with finding the correct way to send an array of objects to my API using AngularJS.
FrontEnd Code
function getPrices(articles) {
return $http.get('http://someurl/api/prices/getprices', { params: { articles: articles } }).then(function (res) {
// do something with prices
}, function (err) {
// handle error
});
}
Articles are of the type
var oneArticle = {
code: 'someCode',
quantity: 1,
stockUnit: 'piece'
}
Api code
[VersionedRoute("getprices")]
[HttpGet]
public IHttpActionResult GetPrices([FromUri]List<Article> articles) {
// do something with input
}
Article class
public class Article {
public string Code {get;set;}
public int Quantity {get;set;}
public string StockUnit {get;set;}
}
Some questions:
1) Why do I not receive any data on my API. Articles is always null
2) Is this the right approach?
Thanks
EDIT 1:
Using a post option I receive following data in my request but I still don't know how to handle it on the API.

I finally got it working.
#Tomo: Thanks for the effort
#Naimad: My apologies, you were right from the beginning.
Here is the working solution:
Frontend:
function getPrices(articles) {
return $http.get('http://someurl/api/prices/getprices', articles).then(function (res) {
// do something with prices
}, function (err) {
// handle error
});
}
Backend
[VersionedRoute("getprices")]
[HttpPost]
public IHttpActionResult GetPrices([FromBody]List<Article> articles) {
// do something with code
}

.controller('LoginController', ['$scope', '$http', function ($scope, $http) {
function getPrices(articles) {
$http.get('http://someurl/api/prices/getprices')
.success(function (data) {
articles: data
}
}
}])

Have u tried
return $http({
url: '/api/SomeCtrl/GetPrices',
method: 'POST',
data: JSON.stringify({ Article : articles }),
headers: { 'Content-Type': 'application/json' }
});
and
public IHttpActionResult GetPrices([FromUri]Article articles) {
or
[HttpPost]
public void GetPrices(Article articles)
where instead of void you put whatever you are returning
?

Related

Vue js parameter can't get in route

I use VueJS and laravel but now I can't get parameter value, please help me to solve this problem.
my VueJS code:
getTestData:function () {
let config = {
params: {
id: 1
}
};
axios.post('{{ route('get_user_data') }}', config)
.then(function (response) {
console.log(response);
// app.posts=response.data;
})
.catch(error => {
})
},
My Controller code:
public function testData(Request $request)
{
// how to get this value?
}
My route
Route::post('get-user-data','TestController#testData')->name('get_user_data');
You don't actually need a post request to get some values out of database. A get request would be more suitable.
You need to have the param in the route definition
Route::get('get-user-data/{id}','TestController#testData')->name('get_user_data');
//If you want to use post route
Route::post('get-user-data/{id}','TestController#testData')->name('get_user_data');
Then in controller method you can get the value out of the request
public function testData(Request $request)
{
$id = $request->route('id');
}
params are the URL parameters to be sent with the request
You could retrieve input from query string in your controller as:
public function testData(Request $request)
{
$id = $request->query('id');
}

JS JSON Error 400, Value could not be converted to List

I'm working on a college project and I'm at the finish line but I lack the knowledge to fix one bug and a few hours of googling hasn't brought me anywhere closer so this is my last resort.
I'm making a web-based quiz using JS AJAX and C# but the 'post' method always returns a error 400.
The JSON value could not be converted to
System.Collections.Generic.List`1[FrontEndGevorderdQuiz.Web.Models.QuizAntwoord].
I put all the answers the users inputs in an array antwoorden[] and want to post this to my QuizController.cs so it can calculate how much answers are correct and so it can give me back the correct percentage.
My POST-Method
fetch("/api/quiz",
{
method: "POST",
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(antwoorden)
})
.then((antwoorden) => {
return antwoorden.json();
})
.then((data) => {
document.getElementById("extraDisplay").innerText = JSON.stringify(data);
})
.catch((error) => {
console.error(error);
})
My QuizController Function
public QuizResultaat Post([FromBody] IEnumerable<QuizAntwoord> antwoorden)
{
int aantalJuisteAntwoorden = 0;
foreach (QuizAntwoord antwoord in antwoorden)
{
QuizVraag vraag = _vragen
.FirstOrDefault(v => v.Id == antwoord.VraagId);
if (vraag.JuisteAntwoordIndex == antwoord.GekozenAntwoordIndex)
{
aantalJuisteAntwoorden++;
}
}
float scorePercentage = ((float)aantalJuisteAntwoorden / (float)_vragen.Count) * 100;
return new QuizResultaat()
{
Percentage = scorePercentage
};
}
Any help is much appreciated. Thank you for your time.
The error message 400 Bad Request mean :
The 400 (Bad Request) status code indicates that the server cannot
or will not process the request due to something that is perceived
to be a client error (e.g., malformed request syntax, invalid
request message framing, or deceptive request routing).
HTTP standards
and the message :
The JSON value could not be converted to
System.Collections.Generic.List ...
Indicate a problem with the model,
you can start to double check if the data that you are sending are same with the model in controller is waiting for ...
If you need more help, you can add your model and your javascript function that create antwoorden
Also here is a link that can help you : AJAX Posts In Razor Pages And HTTP 400 Errors
I think the issue may in your antwoorden.
You can see my example.
public class Binder
{
public int Id { get; set; }
public string Name { get; set; }
}
The antwoorden in ajax example,you need to notice the antwoorden format:
var antwoorden=
[
{ id: 1,name:"A" },
{ id: 2, name: "B"},
{ id: 3, name: "C"}
]
fetch("/Home/Test",
{
method: "POST",
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(antwoorden)
})
.then((antwoorden) => {
return antwoorden.json();
})
.then((data) => {
document.getElementById("extraDisplay").innerText = JSON.stringify(data);
})
.catch((error) => {
console.error(error);
})
Action:
[HttpPost]
public IActionResult Test([FromBody] IEnumerable<Binder> antwoorden)
{
//....
}
Test Result:

All variables are null at the request with Axios to ASP.Net Core Controller

Here is my client request code:
import request from 'axios';
//...
let url = 'Login/SignIn',
headers = {
headers: {
'Content-Type': 'application/json'
}
},
data = JSON.stringify( {
name: 'danil',
password: 'pwd'
});
request.post(url, data, headers);
looks fine by the first glance.
Request is pending here:
But it all ended up like that in my controller:
Here's the code btw:
[HttpPost]
public async Task<ActionResult> SignIn([FromBody]string name, [FromBody]string password)
{
var userLoginCommand = new UserLogInCommand {
Login = name,
Password = password
};
await _dispatcher.DispatchAsync(userLoginCommand);
return Content(userLoginCommand.Result, "application/json");
}
Whats wrong with it? What did I forgot?
I tried to play around with JSON.stringify by adding it and removing, tried to not sending headers (and then it throws 415 error) but no changes there. Still got nulls.
UPD:
As Ali suggested in comments, passing data is goes fine if we use LoginModel for this like that:
public class LoginModel
{
public string name { get; set; }
public string password { get; set; }
}
But why it's not going work just like that in a simple way?
Only one parameter is allowed to read from the message body.
In your example you have two parameters with FromBody attribute, that's why you have null values.
Please find documentation here:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
"At most one parameter is allowed to read from the message body."

Edit /Update with Web API Repository Pattern

I'm trying to work out the very basics of updating my database using a Web API Controller that is backed by a repository pattern. So far I have everything working POST, GET, DELETE (Create, Read, Delete). But I'm missing the Update.
Below is my angular code, I'm not going to post the Angular Views/Templates, but just know that they do bind and they work just fine. My problem is only on the Edit View, where I try to update using the vm.save function. My save function works fine on the Angular side, but I'm not sure what to do on the Web API & Repository side. You will see that my code to get this working is very basic bare bones. I have all of the code pages from my project in a gist here:
All Files in Gist
Just in case you want to see the big picture, otherwise I will just put here the few pages where I am having trouble getting the Edit/Update methods to work in using http.put with Angular Controller, Web API Controller & Repository.
WORKING Angular Edit Controller:
function editFavoriteController($http, $window, $routeParams) {
var vm = this;
var url = "/api/favorites/" + $routeParams.searchId;
$http.get(url)
.success(function (result) {
vm.search = result[0];
})
.error(function () {
alert('error/failed');
})
.then(function () {
//Nothing
});
vm.update = function (id) {
var updateUrl = "/api/favorites/" + id;
$http.put(updateUrl, vm.editFavorite)
.success(function (result) {
var editFavorite = result.data;
//TODO: merge with existing favorites
//alert("Thanks for your post");
})
.error(function () {
alert("Your broken, go fix yourself!");
})
.then(function () {
$window.location = "#/";
});
};
};
NOT WORKING Web API Controller
public HttpResponseMessage Put(int id,[FromBody]Search editFavorite)
{
if (_favRepo.EditFavorite(id, editFavorite) && _favRepo.Save())
{
return Request.CreateResponse(HttpStatusCode.Created, editFavorite);
}
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
NOT WORKING Repository
public bool EditFavorite(int id, Search editFavorite)
{
try
{
var search = _ctx.Search.FirstOrDefault(s => s.SearchId == id);
search(editFavorite).State = EntityState.Modified;
return true;
}
catch
{
var item = "";
}
}
WORKING Interface
bool EditFavorite(int id, Search newSearch);
Again, my only problems are figuring out what to do for the update in the WebAPI FavoritesController and FavoritesRepository. I have example of how I have done everything else in the Gist, so I'm hoping someone might be able to help me out. I'm just hitting a wall of what I know how to do in Web API.
Fixed Code:
public HttpResponseMessage Put(int id,[FromBody]Search editFavorite)
{
if (_favRepo.EditFavorite(id, editFavorite))
{
_favRepo.Save()
return Request.CreateResponse(HttpStatusCode.Created, editFavorite);
}
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
I am also posting code which should work fine for handling edit on server side using WEB API and Repository Pattern.
WebAPI Controller:
public HttpResponseMessage Put(int id,[FromBody]Search editFavorite)
{
if (!ModelState.IsValid || id != editFavorite.Id)
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
db.EditFavorite(editFavorite);
try
{
db.Save();
}
catch (DbUpdateConcurrencyException)
{
if (!db.SearchExists(id))
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
else
{
throw;
}
}
return Request.CreateResponse(HttpStatusCode.Created, editFavorite);
}
Repository Method:
public void EditFavorite(Search editFavorite)
{
db.Entry(editFavorite).State = EntityState.Modified;
}
public void Save()
{
db.SaveChanges();
}
public bool SearchExists(int id)
{
return db.Search.Count(e => e.Id == id) > 0;
}
Modify Interface:
void Save();
void EditFavorite(Search newSearch);
bool SearchExists(int id);
Edit:
I have made some changes so that only operations that are carried out on your db context is done in repository layer (Data Layer) and the error checking is done in the WEB API Controller.
Suggestion:
You should inherit IDisposable on the interface and implement it your repository class so that your entities are properly disposed...
public interface IFavoritesRepository : IDisposable
{
// code here
}
public class FavoritesRepository : IFavoritesRepository
{
// code here
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
db.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}

Where exactly to put the antiforgeryToken

I have a layout page that has a form with AntiForgeryToken
using (Html.BeginForm(action, "Account", new { ReturnUrl = returnUrl }, FormMethod.Post, new { Id = "xcrf-form" }))
This generates a hidden field
<input name="__RequestVerificationToken" type="hidden" value="p43bTJU6xjctQ-ETI7T0e_0lJX4UsbTz_IUjQjWddsu29Nx_UE5rcdOONiDhFcdjan88ngBe5_ZQbHTBieB2vVXgNJGNmfQpOm5ATPbifYE1">
In my angular view (that is loaded in a div in the layout page, I do this
<form class="form" role="form" ng-submit="postReview()">
And my code for postReview() is as follows
$scope.postReview = function () {
var token = $('[name=__RequestVerificationToken]').val();
var config = {
headers: {
"Content-Type": "multipart/form-data",
// the following when uncommented does not work either
//'RequestVerificationToken' : token
//"X-XSRF-TOKEN" : token
}
}
// tried the following, since my other MVC controllers (non-angular) send the token as part of form data, this did not work though
$scope.reviewModel.__RequestVerificationToken = token;
// the following was mentioned in some link I found, this does not work either
$http.defaults.headers.common['__RequestVerificationToken'] = token;
$http.post('/Review/Create', $scope.reviewModel, config)
.then(function (result) {
// Success
alert(result.data);
}, function (error) {
// Failure
alert("Failed");
});
}
My MVC Create method is as follows
[HttpPost]
[ValidateAntiForgeryToken]
[AllowAnonymous]
public ActionResult Create([Bind(Include = "Id,CommentText,Vote")] ReviewModel reviewModel)
{
if (User.Identity.IsAuthenticated == false)
{
// I am doing this instead of [Authorize] because I dont want 302, which browser handles and I cant do client re-direction
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
}
// just for experimenting I have not yet added it to db, and simply returning
return new JsonResult {Data = reviewModel, JsonRequestBehavior = JsonRequestBehavior.AllowGet};
}
So no matter where I put the token, no matter what I use for 'Content-Type' (I tried application-json and www-form-urlencoded) I always get the error "The required anti-forgery form field "__RequestVerificationToken" is not present."
I even tried naming __RequestVerificationToken and RequestVerificationToken
Why does my server not find the damn token?
I also looked at couple of links that ask you to implement your own AntiForgeryToeknVerifyAttrbute and verify the token that is sent as cookieToken:formToken, I have not tried that but why I am not able to get it working whereas this works for the MVC controllers (non-angular posts)
Yes. By default, MVC Framework will check for Request.Form["__RequestVerificationToken"].
Checking the MVC source code
public AntiForgeryToken GetFormToken(HttpContextBase httpContext)
{
string value = httpContext.Request.Form[_config.FormFieldName];
if (String.IsNullOrEmpty(value))
{
// did not exist
return null;
}
return _serializer.Deserialize(value);
}
You need to create your own filter to check it from Request.Header
Code Snippet from Phil Haack's Article - MVC 3
private class JsonAntiForgeryHttpContextWrapper : HttpContextWrapper {
readonly HttpRequestBase _request;
public JsonAntiForgeryHttpContextWrapper(HttpContext httpContext)
: base(httpContext) {
_request = new JsonAntiForgeryHttpRequestWrapper(httpContext.Request);
}
public override HttpRequestBase Request {
get {
return _request;
}
}
}
private class JsonAntiForgeryHttpRequestWrapper : HttpRequestWrapper {
readonly NameValueCollection _form;
public JsonAntiForgeryHttpRequestWrapper(HttpRequest request)
: base(request) {
_form = new NameValueCollection(request.Form);
if (request.Headers["__RequestVerificationToken"] != null) {
_form["__RequestVerificationToken"]
= request.Headers["__RequestVerificationToken"];
}
}
public override NameValueCollection Form {
get {
return _form;
}
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
AllowMultiple = false, Inherited = true)]
public class ValidateJsonAntiForgeryTokenAttribute :
FilterAttribute, IAuthorizationFilter {
public void OnAuthorization(AuthorizationContext filterContext) {
if (filterContext == null) {
throw new ArgumentNullException("filterContext");
}
var httpContext = new JsonAntiForgeryHttpContextWrapper(HttpContext.Current);
AntiForgery.Validate(httpContext, Salt ?? string.Empty);
}
public string Salt {
get;
set;
}
// The private context classes go here
}
Check out here for MVC 4 implementation, to avoid salt issue
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
AllowMultiple = false, Inherited = true)]
public sealed class ValidateJsonAntiForgeryTokenAttribute
: FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
var httpContext = filterContext.HttpContext;
var cookie = httpContext.Request.Cookies[AntiForgeryConfig.CookieName];
AntiForgery.Validate(cookie != null ? cookie.Value : null,
httpContext.Request.Headers["__RequestVerificationToken"]);
}
}
I had the same problem. Turned out that I don't need to set antiforgery token anywhere explicitly in my angular js code. The MVC controller expects this token value to be delivered from 1. the form field, 2. cookie. The filter equates and is happy when they match.
When we submit the form, hidden field for the anti forgery token automatically supplies its value. Cookie is automatically set by the browser. So as I said, we don't need to do anything explicitly.
The problem really is request's content-type. By default it goes as as application/json and therefore the a.f. token value (or rather any form data) is not received.
Following worked for me:
// create the controller
var RegisterController = function ($scope, $http) {
$scope.onSubmit = function (e) {
// suppress default form submission
e.preventDefault();
var form = $("#registerform");
if (form.valid()) {
var url = form.attr('action');
var data = form.serialize();
var config = {
headers: {
'Content-type':'application/x-www-form-urlencoded',
}
};
$http.post(url, data, config).success(function (data) {
alert(data);
}).error(function(reason) {
alert(reason);
});
}
};
};
As Murali suggested I guess I need to put the toekn in the form itself, so I tried putting the token as part of form data and I needed to encode the form data as explained in https://stackoverflow.com/a/14868725/2475810
This approach does not require any additional code on server side, also we do not need to create and join cookie and form token. Just by form-encoding the data and including token as one of the fields as explained in the answer above we can get it rolling.
You should perform the HTTP request in this way:
$http({
url: '/Review/Create',
data: "__RequestVerificationToken=" + token + "&param1=1&param2=2",
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
}).success(function(result) {
alert(result.data);
}).error(function(error) {
alert("Failed");
});

Categories

Resources