How to retrieve FormData values from Yii 1.1 back-end? - json

I am building a page in this platform that will require a title and a file. I am using FormData. I've already used it like this with another back-end framework where I appended a string and a file to the FormData:
const data = new FormData();
data.append("title", this.state.title);
data.append("file", this.state.file[0]);
I am using Redux, so this is the action that will communicate with the back-end:
export function createDownloadableForm(data) {
return (dispatch, getState) => {
const { auth: { oauthToken, oauthTokenSecret } } = getState();
return dispatch({
[CALL_API]: {
endpoint: "/api/downloadable-forms",
method: "POST",
headers: {
'xoauthtoken': oauthToken,
'xoauthtokensecret': oauthTokenSecret,
},
body: data,
types: [CREATE_DOWNLOADABLE_FORMS, CREATE_DOWNLOADABLE_FORMS_SUCCESS, CREATE_DOWNLOADABLE_FORMS_FAILURE]
}
})
}
}
But now I need to access this information from the back-end. How do I do it since what I send is not a JSON? How do I recover the appended data in FormData?
public function actionCreate()
{
$request = \Yii::app()->request;
// ?
}
I've done few things to check what was being sent. One,
\Yii::log(json_encode($request));
will display
{"jsonAsArray":true,"enableCookieValidation":false,"enableCsrfValidation":false,"csrfTokenName":"YII_CSRF_TOKEN","csrfCookie":null,"behaviors":[]}
Using getPost and passing the name of the fields do not work either, it is null. getRaw, just in case something else would be shown, shows nothing as well. It is like nothing is being sent, but the code for sending FormData, the way it is, was used before and works.

Related

Angular 2 and .NET Core Web Api: http post does not call the API

I'm trying to perform a post request to my WebAPI controller, but there is no way to make my action to be called.
This is the action:
[HttpPost("about")]
public async Task<IActionResult> PostAbout([FromBody] PostAboutBindingModel model)
{
if (model == null || !ModelState.IsValid)
return BadRequest(ModelState);
var about = new About
{
Text = model.Text,
Date = model.Date,
Images = _jsonSerializer.Serialize(model.Images)
};
_context.Abouts.Add(about);
await _context.SaveChangesAsync();
return Created($"/api/about/{about.Version}", about);
}
The PostAboutBindingModel has only three properties: Text, Date and Images.
This is the angular2 code snippet where I perform the API call:
let model: IAbout = <IAbout>{
date: new Date(),
images: [],
text: "test"
}
let opts = jwtAuthorization();
opts.headers.append("Content-Type", "application/json");
return this.http.post("/api/about", model, opts)
.map((response: Response) => console.log("TEST", response.json()))
.catch(this.handleError);
The jwtAuthorization simply add the Authorization header:
export function jwtAuthorization(): RequestOptions {
"use strict"
if (localStorage.getItem("auth")) {
// create authorization header with jwt token
let auth: IAuth = getAuth(JSON.parse(atob(localStorage.getItem("auth"))));
if (auth && auth.access_token) {
let headers: Headers = new Headers({ "Authorization": auth.token_type + " " + auth.access_token });
return new RequestOptions({ headers: headers });
}
}
}
I've tried to specify, as body, the following things:
model
{ model }
{ model: model }
JSON.stringify(model)
JSON.stringify({ model: model })
I've tried to specify my model as a generic object (without type) too.
I've tried to perform the call with and without the Content-Type header.
None of the previous seems to work. The API action is not called and no errors are returned.
I would like to perform the request specify only model as-is if it's possible but I would be happy in any case, if it works :)
What am I missing?
EDIT
I read now that http requests in angular 2 are "lazy" so they need a subscriber (subscribe) to work.
Thanks for help

Watson SpeechToText Java and javascript model differences

I'm working on integrating the watson-speech.js javascript library with a Spring-based server using the Watson Java SDK. I'm trying to send the output from a WatsonSpeech.SpeechToText.recognizeMicrophone call to the server with no luck. The Speech java classes appear to have the appropriate #SerializedName annotations that match the json being sent from the client, but I'm getting UnrecognizedPropertyException errors from Jackson.
Unrecognized field "keywords_result" (class com.ibm.watson.developer_cloud.speech_to_text.v1.model.SpeechResults), not marked as ignorable (2 known properties: "resultIndex", "results"])
Here's the controller method:
#RequestMapping(value = "/postWatsonRequest", method = RequestMethod.POST)
#ResponseBody
#ResponseStatus(value=HttpStatus.OK)
public ResponseObject postWatsonRequest(#RequestBody SpeechResults speechResults) {
...
}
I'm clearly missing something. Do I need to unpack the json manually on the server side (custom deserializer?) or format it into an acceptable json string on the client side?
It turned out to be a couple of mistakes on my part and although I'm not sure this is the best solution it does work. Here's the full code for anyone that's interested. Key things that made it work:
You must use the receive-jason event to capture the full json result. The data event appears to only return the final text
The result data had to be wrapped in a valid json wrapper - data:{message:data} (this was my big mistake)
Do not include contentType: 'application/json; charset=utf-8', in the ajax call or the controller will not recognize the json data
The Watson Java SDK WebSocketManager receives an okhttp3.ResponseBody from Watson from which it extracts a string. I presume this is similar to what the javascript SDK receives so I used the same code from the WebSocketManager to convert the JSON.stringify string to a SpeechResults object in the controller.
From the okhttp3.ResponseBody javadoc:
A one-shot stream from the origin server to the client application with the raw bytes of the response body
Watson javascript
function listen(token) {
stream = WatsonSpeech.SpeechToText.recognizeMicrophone({
token: token,
readableObjectMode: true,
objectMode: true,
word_confidence: true,
format: false,
keywords: keywordsArray,
keywords_threshold : 0.5,
continuous : false
//interim_results : false
//keepMicrophone: navigator.userAgent.indexOf('Firefox') > 0
});
stream.setEncoding('utf8');
stream.on('error', function(err) {
console.log(err);
stream.stop();
});
stream.on('receive-json', function(msg) {
console.log(msg);
if (msg.state != 'listening') {
if (msg.results[0].final) {
console.log('receive-json: ' + msg);
postResults(msg);
stream.stop();
}
}
});
}
Ajax post
function postResults(results) {
var data = JSON.stringify(results);
console.log('stringify: ' + data);
$.ajax({
type: 'POST',
url: appContextPath + '/postWatsonResult',
dataType: 'json',
data: {message:data}
})
.done(function(data) {
console.log('done data: '+ data);
})
.fail(function(jqXHR, status, error) {
var data = jqXHR.responseJSON;
console.log('fail data: '+ data);
});
}
Spring controller
#RequestMapping(value = "/postWatsonResult", method = RequestMethod.POST)
#ResponseBody
#ResponseStatus(value=HttpStatus.OK)
public ResponseObject postWatsonResult(#RequestParam("message") String message, Locale locale) {
logger.info("postWatsonRequest");
JsonObject json = new JsonParser().parse(message).getAsJsonObject();
SpeechResults results = null;
if (json.has("results")) {
results = GSON.fromJson(message, SpeechResults.class);
}
if (results != null) {
logger.debug("results: " + results.getResults().get(0).getAlternatives().get(0).getTranscript());
}
return new ResponseObject();
}
I still think it should be possible somehow to use #RequestBody SpeechResults speechResults so I'll continue to play around with this, but at least I have a working solution.

Send non-stringified objects with fetch

I'm using the fetch-api for the first time and having trouble passing a non-stringified JSON objects to the server.
Basically I want to achieve the same behavior as this:
$.post(url, {"test": "test"}, function(response) {
console.log(response);
});
The fetch method is communicating with an web API which is unaccessable for me and expects a plain JSON object.
Normally I would just use FormData to pass data to the server, however the JSON will be transformed to a string [Object object]:
fetch(url, {
method: 'POST',
body: {"test": "test"}
})
.then(data => data.json())
.then(json => console.log(json))
.catch(e => console.error(e));
The body request seems to be empty when using $_POST (which is what the API is using), though gives the right value when using file_get_contents('php://input).
I though this had something to do with the wrong header given to the request. So I tried to add the header Ajax post uses: content-type:multipart/form-data;. However, this also did not get any value.
I was wondering if this was explicity intentional to not use plain JSON object to give as data, or that I'm simply missing something?
This does work, but is not allowed as it is a stringify version of the JSON object:
var formData = new FormData();
formData.append('data', JSON.stringify(data));
fetch(url, {
method: 'POST',
body: formData
})
.then(data => data.json())
.then(json => console.log(json))
.catch(e => console.error(e));
Let's say your data is inside a variable var data = { a: "some data", b: 123 }. If you want your code in PHP to access these fields this way:
$_POST["a"] == "some data";
$_POST["b"] == 123;
Then you need to send the data in the formData format this way:
var fdata = new FormData();
fdata.append('a', 'some data');
fdata.append('b', '123');
Now you can send that data and PHP will have access to separated fields a and b but notice b will be a string, not a number.
What if you want to send an array. Let's say { c: ['hello', 'world', '!'] }? You must follow PHP name conventions and add the same name multiple times:
var fdata = new FormData();
fdata.append('c[]', 'hello');
fdata.append('c[]', 'world');
fdata.append('c[]', '!');
After setting the form data instance, you can use it as the body of the request.
So firstly, I need to post to the POST-protocol, which is used by $_POST. I do this by adding a header of application/x-www-form-urlencoded (which is the protocol used by $_POST, as described in the docs) to the fetch post request:
fetch(url, {
headers: new Headers({'Content-Type': 'application/x-www-form-urlencoded'}), // Default header which $_POST listens to
...
Now the way $.post actually sends data is by creating a serialized string (eg: a%5Bone%5D=1) of the given object. To transform an object to a serialized string, you can use $.param:
fetch(url, {
headers: new Headers({'Content-Type': 'application/x-www-form-urlencoded'}), // Default header which $_POST listens to
method: 'POST',
body: $.param(data)
})
This will make you able to retreive data from $_POST like you would do with a simple $.post.

How do I handle a MySQL query with multiple parameters whilst using Angular / Express

I am designing a used cars website using AngularJS and NodeJS/Express. The database is MySQL (so not quite a MEAN stack).
I am very familiar with Angular but this is the first time I have used Express (in the past I have gone for JAX-RS).
I have no problem whilst only sending one or two paramaters e.g.
app.get('/api/images/:vehicleId', function (request, response) {
images.getThumbnailImage(request, response, connection, request.params.vehicleId);
});
but I am unsure how to move forward with multiple parameters, some of which are optional.
The client is built using TypeScript. The form data is collected by way of a TypeScript object, but I am not sure what the best practice is for sending multiple parameters.
The options I have thought of are:
1). Send the object and then use that to build a query:
return this.$http({
method: 'POST',
url: this.API_ADDRESS + 'vehicles/',
data: vehicleSearchModel,
headers: {
'Content-Type': 'application/json'
}
}).then((response: any) => {
return response.data;
});
and retrieve it from the body using body-parser i.e.
request.body.data (or something similar)
or
2). send multiple params in the url i.e.
app.get('/api/vehicles/:make/:model/:bodyStyle/:fuelType/:transmission/:minPrice/:maxPrice/:minYear/:maxYear/:counties', function (request, response) {
//Do something with request.params!!!
});
Please advise.
First, you should make the http post request as following.
var data = {
make : make,
model : model,
fuelType : fueltype,
....
}
return this.$http({
method: 'POST',
url: this.API_ADDRESS + 'vehicles/',
data: vehicleSearchModel,
headers: {
'Content-Type': 'application/json'
}
}).then((response: any) => {
return response.data;
});
And then, you can receive the multiple parameters as following.
app.post('/api/vehicles/', function(req, res){
var make = req.body.make;
var model= req.body.model;
var bodyStyle= req.body.bodyStyle;
var fuelType= req.body.fuelType;
var transmission= req.body.transmission;
.....
});

How to send multiple parameters from kendo data source read operation to WebApi controller

I have the following scenario: I have a kendo.dataSource which is populated via read request to a WebApi Controller. In addition to the read, I am sending a couple of parameters, which then I use in my controller to do some server logic. I was able to send as many simple parameters as I want via the parameterMap property of the transport function. Till now it was a simple get request. However now I need to send additional json object to the controller as a parameter. I read that I have to transform the Get request to Post and put the Json onto the body of the request but I don't know how to do it.
The code that I have so far:
var gridDataSource = new kendo.data.DataSource({
type: 'odata-v4',
transport: {
read: {
url: wave.alarmsAndEvents.api('api/alarmsAndEventsSearch/post'),
type: "POST",
data: {
SearchModel: JSON.stringify(vm.searchModel)
},
contentType: 'application/json; charset=utf-8',
},
parameterMap: function (data, operation) {
if (operation === "read") {
data.startDate = kendo.toString(vm.selectedTimeInterval.start, "G");
data.endDate = kendo.toString(vm.selectedTimeInterval.end, "G");
data.alarmsToDisplay = vm.maxRecords;
}
return kendo.stringify(data);
}
},
pageSize: vm.maxRecords,
error: function (e) {
alert(e.xhr.responseText);
}
});
The SearchModel is the thing that I want to send as JSon. The rest are simple DateTime and int parameters.
My controller:
[HttpPost]
public IQueryable<AlarmsSearchViewModel> Post(DateTime startDate, DateTime endDate, int alarmsToDisplay, [FromBody]JToken jsonbody)
{
....
return something;
}
I end up with Not Found 404, but I am pretty sure that I have messed up the parameters. And from the Network window I can see that the json object is not sent at all. Any help will be much appreciated!