Is it possible to post an array with $http.post? - javascript

I'm trying to save an json array to service in back-end via $http.post but I always get 400 Bad Request. I have googled all around and there's not a clear answer to be found.
My questions are follows:
1. Is it possible to post json array via $http.post?
2. If yes, how it is done?
3. If no, what other alternatives there would be to save json array with such aim?
To make this even more clear, I'm going to add some of my codes below which are necessary.
At Front-end
SavingsService
createBatchSavings: function(savingsBatch){
return $http.post('/finance/savings/create/batchsavings', savingsBatch).success(function(data){
return data;
});
}
Above function gets it's savingsBatch parameter properly. savingsBatch is json array.
At Back-end
SavingServiceImpl
#Component
#Path("/savings")
#Produces("application/json")
public class SavingsServiceImpl implements SavingsService {
#Autowired
FinanceEntityDao financeDao;
#POST
#Path("/create")
public FinanceEntity createSaving(FinanceEntity financeEntity) {
financeEntity.setType(FinanceType.SAVING);
financeEntity.setDateAdded(new Date());
return financeDao.save(financeEntity);
}
#POST
#Path("/create/batchsavings")
public List<FinanceEntity> createBatchSavings(List<FinanceEntity> savingsBatch) {
if(savingsBatch.size() > 0){
return financeDao.massSave(savingsBatch);
}
else {
throw new NullPointerException("Saving batch was null!");
}
}
}
I have also examined the header values. Request headers is having proper values but Response headers content-length is always zero.

Yes its absolutely possible to post an array data and the code you wrote at client side to post data is correct.
The status code 400 you are getting must be due to problem at your server side code. I see a method createBatchSavings in your server side code but your URL at client side is lower case, batchsavings.
This is just an hint. You need to add more information about server side code.

This problem is finally solved. There were two problems in this implementation. First one in front-end and second in back-end.
Front-end
First problem was that I tried to post json array straightly to Java service class, which won't work, because it must be converted into proper string json array. Code to send the json array from controller to service is something like this:
$scope.createBatchSavings = function(batchSavings){
return SavingsService.createBatchSavings(batchSavings).success(function(data){
swal("Saving batch saved!", "Saving batch was saved successfully!", "success");
$scope.savingsBatch = [];
});
}
Here batchSavings is json array which content-type in header was application/json. This must be stringified and thus above method must be written like below in order to pass proper json array to back-end service:
$scope.createBatchSavings = function(batchSavings){
return SavingsService.createBatchSavings(JSON.stringify(batchSavings)).success(function(data){
swal("Saving batch saved!", "Saving batch was saved successfully!", "success");
$scope.savingsBatch = [];
});
}
Back-end
Secondly this stringified array must be deserialized in the back-end. Previously the connection between front-end service and back-end service didn't even work because of passing inappropriate json array. So the connection was fixed by converting the json array into proper one.
In order for back-end service to work I had to change the parameter type from List<FinanceEntity> into String. Also I had to choose deserialization "machine". In my case I chose Gson. Thus below method:
#POST
#Path("/create/batchsavings")
public List<FinanceEntity> createBatchSavings(List<FinanceEntity> savingsBatch) {
if(savingsBatch.size() > 0){
return financeDao.massSave(savingsBatch);
}
else {
throw new NullPointerException("Saving batch was null!");
}
}
becomes:
#POST
#Path("/create/batchsavings")
public List<FinanceEntity> createBatchSavings(String savingsBatch) {
Gson gson = new Gson();
Type collectionType = new TypeToken<List<FinanceEntity>>(){}.getType();
List<FinanceEntity> savingsList = gson.fromJson(savingsBatch, collectionType);
if(savingsList.size() > 0){
return financeDao.massSave(savingsList);
}
else {
throw new NullPointerException("Saving batch was null!");
}
}
Now this whole thing works.
Just as a notification, if you have any dates you must format them into proper format because otherwise you will encounter Unparsable date problem in Java.

questions are follows:
Is it possible to post json array via $http.post? yes
If yes, how it is done?
$http({ method:'POST', url:'api/data', data:item array})
If no, what other alternatives there would be to save json array with such aim?
It is possible.

Related

How can I access parameters from a POST (or other HTTP) request in a C# server?

I'm building a Reactjs application using ASP.Net Core, and I'm completely new to this. I have a JS component that (to the best of my knowledge) is making an appropriate fetch request (POST) to a C# server controller. The server is definitively receiving the request. However, I have absolutely no idea how to access the parameters of the request (which I am passing through the body).
I've tried using [FromBody] to access the body directly. I've also tried to utilize the Request.Body, the Request.Form, the Request.Query, the Request.Params, etc. I've attempted to follow the guidelines I've found online that seem to try to address my problem. Several of them flat-out haven't worked. Most of them are using key words, classes, etc. that are not available to me in my current libraries, and they don't list the appropriate libraries. For all I know, I've stumbled across the right answer already. But for someone like me with a highly logical mind but also pretty much zero experience in the field, I can't wrap my mind around any of it.
My POST request using fetch in JavaScript:
fetch('api/Database/PushStatus',
{
method: 'post',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify({ order: this.state.orderNum, status: this.state.newStatus })
}
).then(response => {
response.json().then(data => {
this.setState({ reqCheck: "" + response, didPush: false });
})
}).catch(err => {
this.setState({ reqCheck: "Failure! (js)", didPush: false })
});
And my request handling on my C# server:
[Route("api/[controller]/[action]")]
public class DatabaseController : Controller
{
string conStri = "Data Source=ADM293\\MSSQLSERVER01;Initial Catalog=testDB;User ID=sa;Password=password.1";
SqlConnection cnct = null;
[HttpPost]
public void PushStatus(string order, string status)
{
cnct = new SqlConnection(conStri);
SqlCommand command = new SqlCommand("insert into testTable values ('" + order + "', '" + status + "');", cnct);
using (cnct)
{
cnct.Open();
int result = command.ExecuteNonQuery();
cnct.Close();
}
}
}
I would like my server to be updated with the contents of this.state.orderNum and this.state.newStatus.
Instead, my server is updated with empty string values (which makes perfect sense).
You can create a class that represents the request:
public class PushStatusRequest
{
public string Order { get; set; }
public string Status { get; set; }
}
And use it like this:
[HttpPost]
public void PushStatus([FromBody] PushStatusRequest request){
//request.Status
//request.Order
}
#Anteo is right. But I would like to add some explanation.
You have faced two different ways to pass params to a controller.
You try to use a body (setting body in request) as an entrance and the query (in the controller) as an output.
Well, body is one type and it requires to be some model on a Controller side. Use [FromBody] to automatically retrieve your data from a request into a model. Create this model as a separate class following the same naming politics (ignoring case).
Then, a query string is what you are using at the controller side. On a request side, it follows the URL like this: https://host:port/route/to/then/query/params. So, if I am not mistaking a base URL is a part https://host:port. Everything is left is a query string. A part of that is being mapped to the route to your controller's action. Anything left is interpreted as useful info and mapped to an action params.
So, here is the moment for you to choose (depending on the length of your orderNum and newStatus) which approach to use. I would recommend using the body as it is more data than routing.
Also, If you inherit [ControllerBase][1], then you can use Request property in action to access the request itself. But be careful here.
P.S. I would recommend you to read more about requests, things like form data, body, queries, etc.
If have any further questions feel free to ask.
By default, capitalization changes between JavaScript and C#. So, you probably just need to change your method signature:
public void PushStatus(string Order, string Status)
That said, having the class the way #Anteo suggested is usually better. Also, I assume you put SqlConnection just for clarity. You shouldn't do it in the controller

How to send FormData attribute with an array of strings from React Client to Django + Django Rest Framework API

How can I send JSON.stringify(array) data within form-data and decode the JSON with my Django api?
I'm trying to add the functionality to upload an array of date-strings within a form
originally we sent the post data using JSON and sending arrays of data worked, however, when we switched to using form-data in order to make uploading an image easier we started having problems with array types.
since form data has to be sent using a string type I converted the date-string array using JSON.stringify()
const myForm = new FormData();
myForm.set("date_strings", JSON.stringify(dateStrings));
when I post myForm to my Django + DRF API it responds with
{
"date_strings": [
"Datetime has wrong format. Use one of these formats instead: YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]."
],
"status_code": 400
}
In Postman I verified that sending a single date-string works, but when I send a string-array I get the same error.
I believe my Django API tests if the request.data is valid, sees that date_strings are a JSON string, then responds with a 400 Error.
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
Attempted Solutions:
converting the JSON string to an array in the PostViewset create method
I can't change request.data['publish_dates'] because it is not mutable and I've seen some advice that you should not attempt to copy or change request data in the viewset because it isn't safe.
convert the JSON string to an array in the serializer
neither MySerializer's create nor validate methods run (I logged to test).
date_strings are formated and created as a separate PublishDate model in the create method,
class MySerializer(MyChildSerializer):
date_strings = serializers.ListField(child=serializers.DateTimeField(), min_length=1, max_length=100, write_only=True)
How can I send/accept form-data to my Django + DRF API when one attribute is an array of date-time strings?
I realized the problem and found a solution. Because MySerializer had set date_strings to be a ListField it was coming in as a string and being rejected as a bad request.
by changing the model of date_strings to a CharField, MySerializer runs the validate and create methods. I now have to find a way to convert the JSON date_strings back into an array in the serializer create method, but I solved the API's 400 issue.
MySerializer now looks like:
class CreatePostSerializer(SocialVeilModelSerializer):
publish_dates = serializers.CharField(max_length=2000)

Angular - parse json string created by json dumps

I couldn't find a helpful solution.
I work in python 3.6 (django rest-framwork) server side and angular 5 client side.
At the server:
class TypesView(APIView):
def get(self,request):
a = ['Cat','Dog']
j = json.dumps(a)
return Response(data=j, status=status.HTTP_200_OK)
I am trying to parse this at the client:
public getAnimalRaces(): Observable<string[]>{
const path = environment.apiEndpoint + "animals/races/"
return this._http_client.get<string[]>(path)
}
but I keep getting:
Error trying to diff '["Cat", "Dog"]'. Only arrays and iterables are allowed
This is what Being returned to the client:
"[\"Cat\", \"Dog\"]"
any ideas?
You are trying to iterate on a string.
You need to parse the response as JSON which will turn the response into an array which you can then use with *ngFor in the template for example.
this._http_client.get<string[]>(path)
.map((res) => JSON.parse(res));
Your response is stringified. In order to make it work, you have to send it as plain JSON on server side, or treat it as string and use JSON.parse() on client side.

API Connect - 500 error when including basic Javascript

I'm trying some basic API Connect tutorials on IBM's platform (running locally using loopback) and have got completely stuck at an early point.
I've built a basic API service with some in-memory data and setter / getter functions. I've then built a separate API which takes two GET parameters and uses one of my getter functions to perform a search based on two criteria. When I run it, I successfully get a response with the following JSON object:
[{"itemId":1,"charge":9,"itemSize":2,"id":2}]
I've then tried to add a piece of server logic that modifies the response data - at this point, I'm just trying to add an extra field. I've added a Javascript component in the Assemble view and included the following code (taken from a tutorial), which I thought should modify the message body returned by the API while still passing it through:
//APIC: get the payload
var json = apim.getvariable('message.body');
//console.error("json %s", JSON.stringify(json));
//same: code to inject new attribute
json.platform = 'Powered by IBM API Connect';
//APIC: set the payload
//message.body = json;
apim.setvariable('message.body', json);
Instead of getting an extra JSON parameter ("platform"), all I get is a 500 error when I call the service. I'm guessing that I'm doing something fundamentally wrong, but all the docs suggest these are the right variable names to use.
You can't access json.platform but at that point json variable is json type. Are you sure that you can add a property to a json type variable if your json object lacks of that property? I mean: What if you first parse the json variable of json type to a normal object, then add new property, and finally stringify to json type again for body assigning purposes?
var json = JSON.parse(apim.getvariable('message.body')); //convert to normal object
json.platform = 'Powered by IBM API Connect'; //add new property
apim.setvariable('message.body', JSON.stringify(json)); //convert to json again before setting as body value
You need to get the context in some determined format, and in this function do your logic. For example if your message is in json you need to do:
apim.readInputAsJSON(function (error, json) {
if (error)
{
// handle error
apim.error('MyError', 500, 'Internal Error', 'Some error message');
}
else
{
//APIC: get the payload
var json = apim.getvariable('message.body');
//console.error("json %s", JSON.stringify(json));
if(json){
//same: code to inject new attribute
json.platform = 'Powered by IBM API Connect';
//APIC: set the payload
//message.body = json;
apim.setvariable('message.body', json);
}
}
});
Reference:
IBM Reference
You have the message.body empty, put a invoke/proxy policy before your gateway/javascript policy for example.

JQuery Autocomplete GET & JSON array data security concerns

I am learning JQuery with a MVC3 book. I find that Json data is really easy to use, but it may not be safe.
Consider the scenario, say, I got a CRM with senstive customer infomation. Ajax returns Json array as search results. The search textbox ajax autocomplete also return Json array of senstive keywords from database. etc...They all use GET method.
However, it is said that GET method has vulnerabilities when passing around Json array data:
http://haacked.com/archive/2009/06/25/json-hijacking.aspx
http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
How do you JQuery experts out there go about fixing this issue? Please help.
--- EDIT: ---
#Gren. Awesome. Thank you. Based on your tips, here is what I figured out.
The normal autocomplete returning json array
and a mod one with a json object wrapping the array
Here is the code, assuming we got a global List named txtlst in the controller.cs...
// normal one
public JsonResult AutoCompleteHelper1(string term) {
//if (!Request.IsAjaxRequest()) return null;
var lst = txtlst.Where(s => s.StartsWith(term)).ToList();
var res = lst.Select(x => new { value = x }).ToList();
return Json(res, JsonRequestBehavior.AllowGet);
}
//mod one
public JsonResult AutoCompleteHelper2(string term) {
//if (!Request.IsAjaxRequest()) return null;
var lst = txtlst.Where(s => s.StartsWith(term)).ToList();
var res = lst.Select(x => new { value = x }).ToList();
return Json(new { wrapper= res, name="wrapper" }, JsonRequestBehavior.AllowGet);
}
}
and then in the .cshtml file...
<p>Auto Complete Example</p>
<input type="text" name="q" id="MyInput1" data-autocomplete-source="#Url.Action("AutoCompleteHelper1", "Home")"/>
<input type="text" name="q" id="MyInput2" data-autocomplete-source="#Url.Action("AutoCompleteHelper2", "Home")" />
and then in the .js file...
$(document).ready(function () {
// normal autocomplete
$("#MyInput1").autocomplete({ source: $("#MyInput1").attr("data-autocomplete-source") });
// mod autocomplete with a wrap
$("#MyInput2").autocomplete({
source: function (req, add) {
$.getJSON($("#MyInput2").attr("data-autocomplete-source"), req, function (data) {
var suggestions = [];
$.each(data.wrapper, function (i, o) {
suggestions.push(o.value);
});
add(suggestions);
});
}
});
});
--- EDIT 2: ---
Please ignore those comments that are telling me to use POST. They
are not reading the blog links or do not understand the issue.
The other option is to wrap your JSON Arrays within JSON objects. The article and comments in it answered this question.
Edit:
From the article:
The fact that this is a JSON array is important. It turns out that a script that contains a JSON array is a valid JavaScript script and can thus be executed. A script that just contains a JSON object is not a valid JavaScript file.
If you wrap your json array in an object {"myJsonArray":[{"name":"sensitive"},{"name":"data"}]} the HTML script tag would not be able to execute.
Security of an Ajax/JSONP/JSON call is the same exact thing as the security of an http call since Ajax requests are http requests. Nothing changes in how you handle it. You make sure the user is logged in and can access the information.
If you are worried about data being cached, use Post or set the proper no caching headers with the backend.
EDIT:
Things you can do to prevent JOSN from being read is the infinite loop trick. Stick an infinte loop in front of the call, means your Ajax call will have to strip this before using it.
You can use keys, third party site would not have the keys needed to validate the request.
You can check referrers.

Categories

Resources