I am trying to send a parameter using Angular2 POST to my Python/Tornado back-end which returns a JSON object. The parameters are being sent properly but at the Python side, it is returning 400 POST missing arguments error. I am using Ionic 2/Angular2 in the front-end and Python/Tornado server.
Angular2 code is as follows:
Here content is a variable containing HTML table
let body = JSON.stringify({content: content});
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
this.http.post(url, body, options).map(res => res.json()).subscribe(data => {
console.log(data)
}, error => {
console.log(JSON.stringify(error));
});
Python code is as follows:
def post(self):
print self.request.arguments
print self.get_argument('content')
self.finish(dict(result="ok", data=content))
Here is the error:
[W 160824 06:04:30 web:1493] 400 POST /test (182.69.5.99): Missing argument content
[W 160824 06:04:30 web:1908] 400 POST /test (182.69.5.99) 1.67ms
Your Angular2 code looks reasonable, however your Python code is wrong, because you are treating the request as x-www-form-urlencoded. You have to access the JSON string through the request.body property:
data = tornado.escape.json_decode(self.request.body)
See https://stackoverflow.com/a/28140966/2380400 for an answer to a similar question.
You should maybe try to use something like URLSearchParams() with an URLencoded content type. I don't know much about Tornado but I am using ASP controllers and it works fine.
See Angular2 Documentation : https://angular.io/docs/ts/latest/api/http/index/URLSearchParams-class.html
Watch the following authentication example I am using :
controllerURL: string = "/APIConnexion";
login(aLogin: string, aMdp: string) {
// parameters definition (has to have the same name on the controller)
let params = new URLSearchParams();
params.set("aLogin", aLogin);
params.set("aMdp", aMdp);
// setup http request
let lHttpRequestBody = params.toString();
let lControllerAction: string = "/connexion";
let lControllerFullURL: string = this.controllerURL + lControllerAction;
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
let options = new RequestOptions({ headers: headers });
// call http
return this.http.post(lControllerFullURL, lHttpRequestBody, options)
.map((res: any) => {
// data received as JSON
let data = res.json();
// Do something with your data
}
).catch(this.handleError);
}
Related
I am developing an ionic app that makes a rest call to a backend to send an email, when I make the rest call I get the following error, what can be due to (the rest call in postman works, I use chrome with the cors disabled)
Error:
POST http://172.16.50.92/send 500 (Internal Server Error)
Code Angular:
const params = {
'type': 'mail',
'attributes[to_recipients]': mail,
'attributes[body]': body,
'attributes[subject]': subject,
'attributes[attachments]': attachments
};
endpoint = url + '/send';
var headers_object = new HttpHeaders();
headers_object.append('contentType', 'application/json');
headers_object.append('Authorization', `Basic ${window.btoa(username + ':' + password)}`);
return this.http.post(endpoint, params, [headers_object]);
return this.http.post(endpoint, params, [headers_object]);
You put your headers into an array. But the signature is supposed to be
post(url: string, body: any, options: { headers: HttpHeaders })
for your usecase.
Please change to below and try again.
return this.http.post(endpoint, params, { headers: headers_object });
Using the Http module, this construction is used:
Http service:
let tokenUrl1 = this.apiUrl + 'login';
let headers1 = new Headers({'Content-Type': 'application/json'});
return this.http.post(tokenUrl1, JSON.stringify(model), {headers: headers1});
Call of the service:
this.loginService.sendCredential(this.model).subscribe(
data => {
// Next statement ... how to convert this for use in an HttpClint environment??
localStorage.setItem("token", JSON.parse(JSON.stringify(data))._body);
The nice HttpClient module parses the http content body with JSON returning an object. Nice!
How can the marked statement, "JSON.parse(JSON.stringify(data))._body)" be rewritten to fit nicely in the HttpClient environment?
You can manipulate the return type to string - that is sufficient for e.g. tokens:
let tokenUrl2 = this.apiUrl + 'users';
let getHeaders = new HttpHeaders({'Authorization':'Bearer '+token});
return this.httpClient.get(tokenUrl2, { observe: 'body', responseType: 'text', headers: getHeaders})
You can add observe in your header.
let tokenUrl1 = this.apiUrl + 'login';
let headers1 = new Headers({'Content-Type': 'application/json'});
return this.http.post(tokenUrl1, JSON.stringify(model), {
headers: headers1,
observe: 'response'
});
And
this.loginService.sendCredential(this.model).subscribe(
data => {
console.log(data.body);
You can use response as JSON object directly to get value like this.
this.loginService.sendCredential(this.model).subscribe(
data => {
let res = data.json();
let tokenValue = res['token']; // based on your json response...
localStorage.setItem("token", tokenValue);
}
And If you are using api-call with type then...(From Angular Doc)
showConfig() {
this.configService.getConfig()
.subscribe((data: Response) => {
// like data.xyz properties of 'Reponse' class
// here you can get values from Your 'Reponse' class directly instead of parsing as above.
});
}
Hope this helps.
Ask me if any query.
I have this project I am working on.
the idea is to retrieve books from google book API, using angular 4
I am struggling to understand how to read the JSON response, I am still learning Angular.
I was searching the internet and found this source code on GitHub
google bookstore
I am getting the following error
Argument of type 'Response' is not assignable to parameter of type 'string'.
for this line
let bookResponse = JSON.parse(body);
I am not sure if I am doing it the correct way.
appreciate your help
below is my method for send HTTP request.
getBooks(searchparam: any){
let par = new HttpParams();
par.set('q', searchparam);
return this.httpClient.get('https://www.googleapis.com/books/v1/volumes', {
params : new HttpParams().set('q',searchparam)
}).map(
(response: Response) => {
let data = response;
return data;
}
);
}
below is the method to get data from HTTP request and read JSON response
getBook() {
const dataArrya = this.searchForm.get('param').value;
console.log(dataArrya);
this.requestService.getBooks(dataArrya)
.subscribe(
response => {
this.printData(response)
// console.log(this.bookData);
},
(error) => console.log(error)
);
}
private printData(res: Response) {
let body = res;
let books: Array<Book> = new Array<Book>();
let bookResponse = JSON.parse(body);
console.log(bookResponse);
console.dir(bookResponse);
for (let book of bookResponse.items) {
books.push({
title:book.volumeInfo.title,
subTitle: book.volumeInfo.subTitle,
categories: book.volumeInfo.categories,
authors: book.volumeInfo.authors,
rating: book.volumeInfo.rating,
pageCount: book.volumeInfo.pageCount,
image: book.volumeInfo.imageLinks === undefined ? '' : book.volumeInfo.imageLinks.thumbnail,
description: book.volumeInfo.description,
isbn: book.volumeInfo.industryIdentifiers,
previewLink: book.volumeInfo.previewLink
});
}
}
JSON.parse takes a string, but you're passing it an Angular Response, which is an object (not a string). In order to convert an Angular Response to a string, you can call toString on it, like this:
let bookResponse = JSON.parse(body.toString());
As the reference states,
The responseType value determines how a successful response body will be parsed. If responseType is the default json, a type interface for the resulting object may be passed as a type parameter to request().
HttpClient get already parses the response with JSON.parse by default and there is no need to call it twice.
The result is plain object, not Response (it belongs to Http API and can be confused with Response global when not being imported).
The mentioned repository uses Angular 2 and Http, and the code from there isn't suitable here. HttpClient is newer API that was introduced in Angular 4. The main practical difference between Http and HttpClient is that the latter doesn't require to add .map(response: Response => response.json()) to the request.
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
I'm working on an Apple TV app using TVMLKit. My app's JavaScript code tries to send an HTTP request to a server using XMLHttpRequest. The server is expecting a specific user agent, so I tried this:
var request = new XMLHttpRequest();
request.open("GET", url, true);
request.setRequestHeader("User-Agent", "MyApp");
request.send();
The server receives a different User-Agent header:
User-Agent: <Projectname>/1 CFNetwork/758.1.6 Darwin/15.0.0
If I change the header name to something different, it shows up in the request headers. I guess Apple is replacing the User-Agent field right before sending the request. Is there a way to prevent this?
After spending two days on investigating this question I've came to solution with creating native GET and POST methods in swift end exposing them to javascript. This isn't best solution but still I want to share it. Maybe it could help someone.
Here how it works
First we need to install Alamofire library. We will use it for creating requests.
Readme on github has all instructions you need to install it
After installing Alamofire we need to import it in AppDelegate.swift
import Alamofire
Then we need to create function in app controller (AppDelegate.swift) that will expose methods to javascript
func appController(appController: TVApplicationController, evaluateAppJavaScriptInContext jsContext: JSContext)
{
let requests = [String : AnyObject]()
let get: #convention(block) (String, String, [String : String]?) -> Void = { (cId:String, url:String, headers:[String : String]?) in
Alamofire.request(.GET, url, headers: headers)
.responseString { response in
jsContext.evaluateScript("requests." + cId + "(" + response.result.value! + ")")
}
}
let post: #convention(block) (String, String, [String : AnyObject]?, [String : String]?) -> Void = { (cId:String, url:String, parameters:[String : AnyObject]?, headers:[String : String]?) in
Alamofire.request(.POST, url, parameters: parameters, headers: headers)
.responseString { response in
jsContext.evaluateScript("requests." + cId + "(" + response.result.value! + ")")
}
}
jsContext.setObject(requests, forKeyedSubscript: "requests");
jsContext.setObject(unsafeBitCast(get, AnyObject.self), forKeyedSubscript: "nativeGET");
jsContext.setObject(unsafeBitCast(post, AnyObject.self), forKeyedSubscript: "nativePOST");
}
Full code of AppDelegate.swift you can find here
All set! Now we have access to nativeGET and nativePOST functions from javascript.
The last thing is to make requests and retrieve responses. I haven't understand how to make callback executions in swift so I've used jsonp approach using runtime generated functions and passing their names to native functions.
Here how it looks in javascript
export function get(url, headers = {}) {
return new Promise((resolve) => {
const cId = `get${Date.now()}`;
requests[cId] = response => {
delete requests[cId];
resolve(response);
}
nativeGET(cId, url, headers);
});
}
export function post(url, parameters = {}, headers = {}) {
return new Promise((resolve) => {
const cId = `post${Date.now()}`;
requests[cId] = response => {
delete requests[cId];
resolve(response);
}
nativePOST(cId, url, parameters, headers);
});
}
The code above is written in ES6 and you'll need to include Promise polifill in your TVJS app.
Now we can make GET and POST requests applying any header we need
post('http://example.com/', {
login: 'xxx',
password: 'yyy'
}, {
'User-Agent': 'My custom User-Agent'
})