Angular 5 to CherryPy POST - http

I am trying to send data to a cherrypy backend from an Angular 5 app. I am able to call the correct cherrypy function and get a 200 response, but none of my parameters are getting through.
One thing that caught my eye was that my payload, when I click view source in the chrome debugger looks like this {"username":"admin","password":"admin"}. Is this supposed to be formatted differently?
Here's my post:
getUserDetails(username, password) {
const _headers = new HttpHeaders();
const headers = _headers.append('Content-Type', 'application/json');
const options = {headers: headers };
this.data = JSON.stringify({ username, password });
return this.http.post(this.url1 + 'login', this.data, options )
.subscribe(data => {
console.log(data);
});
}
Again, this gets to the proper endpoint, just none of the data gets through.
Here's cherryPy:
Login Function:
class authServer(object):
#cherrypy.expose
def login(self,*args, **kwargs):
print(type(args),args, kwargs)
I've tried various args, if I have username and password I get the error saying missing parameters.
Here's the cherrypy configuration.
def CORS():
"""Allow AngularJS apps not on the same server to use our API
"""
cherrypy.response.headers["Access-Control-Allow-Origin"] = "*"
cherrypy.response.headers["Access-Control-Allow-Headers"] = \
"content-type, Authorization, X-Requested-With"
cherrypy.response.headers["Access-Control-Allow-Methods"] = 'GET, POST'
if __name__ == '__main__':
cherrypy.tools.CORS = cherrypy.Tool('before_handler', CORS)
cherrypy.log.error_log.propagate = False
cherrypy.log.access_log.propagate = False
cherrypy.config.update({'server.thread_pool': 30,
'tools.CORS.on': True,
'server.socket_host': '0.0.0.0',
'server.socket_port': 8081})
cherrypy.quickstart(authServer(), '/')
I've made countless posts to cherrypy before. The only difference here is the frontend I'm using. Any help would be greatly appreciated.
Thanks,

Turns out it was a CORS issue. I changed my CORS function from this:
def CORS():
cherrypy.response.headers["Access-Control-Allow-Origin"] = "*"
cherrypy.response.headers["Access-Control-Allow-Headers"] = \
"content-type, Authorization, X-Requested-With"
cherrypy.response.headers["Access-Control-Allow-Methods"] = 'GET, POST'
to this:
def CORS():
if cherrypy.request.method == 'OPTIONS':
cherrypy.response.headers['Access-Control-Allow-Methods'] = 'POST'
cherrypy.response.headers['Access-Control-Allow-Headers'] = 'content-type'
cherrypy.response.headers['Access-Control-Allow-Origin'] = '*'
return True
else:
cherrypy.response.headers['Access-Control-Allow-Origin'] = '*'
cherrypy.tools.CORS = cherrypy._cptools.HandlerTool(CORS)
Above my main function I put this:
#cherrypy.expose
#cherrypy.config(**{'tools.CORS.on': True})
#cherrypy.tools.json_in()
#cherrypy.tools.json_out()

Related

What is difference between node-fetch(nodejs) and requests(python)

python codes get response but javascript code can't get response.
what is diff between the two??
import fetch from 'node-fetch';
const url = "http://host3.dreamhack.games:15592/img_viewer"
const imageUrl = "/static/dream.png"
const data = {
"url": imageUrl
}
const res = await fetch(url, {
method: "POST",
body: JSON.stringify(data)
})
const text = await res.text()
console.log(text)
//<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
//<title>500 Internal Server Error</title>
//<h1>Internal Server Error</h1>
//<p>The server encountered an internal error and was unable to complete your request. Either the //server is overloaded or there is an error in the application.</p>
import requests
url = "http://host3.dreamhack.games:15592/img_viewer"
imageUrl = "/static/dream.png"
data = {
"url": imageUrl
}
response = requests.post(url, data=data)
print(response.text)
I think the two codes are same, why get different response?
(after time above url is not available)
** server
#app.route("/img_viewer", methods=["GET", "POST"])
def img_viewer():
if request.method == "GET":
return render_template("img_viewer.html")
elif request.method == "POST":
url = request.form.get("url", "")
urlp = urlparse(url)
if url[0] == "/":
url = "http://localhost:8000" + url
elif ("localhost" in urlp.netloc) or ("127.0.0.1" in urlp.netloc):
data = open("error.png", "rb").read()
img = base64.b64encode(data).decode("utf8")
return render_template("img_viewer.html", img=img)
try:
data = requests.get(url, timeout=3).content
img = base64.b64encode(data).decode("utf8")
except:
data = open("error.png", "rb").read()
img = base64.b64encode(data).decode("utf8")
return render_template("img_viewer.html", img=img)
I see 2 reasons:
The headers in node-js might be drifferent by default. You could specify them to make the requests identical tho.
It seems like javascript has a different fingerprint than python-requests, even when using the same headers. Some websites like cloudfare can detect that, and then usually throw a 403 [Forbidden] http error.
But I didn't find out, what exactly is defferent that case.

how to send HTTP request with APIKEY

I have an API Gateway created to trigger my lambda function. I am trying to secure the invoke URL. I understand that we can use the Lambda Authorizer or the APIKEY. I am trying to use the API key but not sure how to pass the API key using fetch.
I have also linked the API to the API Keys and the usage Plans.
I am trying to access the URL from the client-side.
invokeurl is referring to my Invoke URL which will return the JSON object.
egkeyname is my key value which I am not able to share.
Client.py:
onMount(async () => {
const res = await fetch('invokeurl',{
method:'get',
headers: new Headers ({
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Methods':'OPTIONS,POST,GET',
'X-API-KEY' :'egkeyname'
})
}); //wait until the promise return result
data = await res.json();
});
But I get an error:
Access to fetch at '..invoke ur...' from origin 'http://localhost:3000' has been blocked by CORS policy: Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.
Uncaught (in promise) TypeError: Failed to fetch
GET https:invokeurl net::ERR_FAILED
My lambda function:
responseObject = {}
responseObject['statusCode'] = 200
responseObject['headers']={}
responseObject['headers']['Content-Type'] = 'application/json'
responseObject['headers']['Access-Control-Allow-Origin'] = '*'
responseObject['headers']['Access-Control-Allow-Methods'] = 'OPTIONS,POST,GET'
return responseObject
How do I access the URL with the APIkey?
Solved it on my own. I was using the wrong information in the Header.
It should be:
onMount(async () => {
const res = await fetch('invokeurl',{
method:'get',
headers: new Headers ({
'Access-Control-Request-Headers': 'Origin, X-Requested-With, Content-Type, Accept, Authorization',
'Origin' : '*',
'Access-Control-Request-Method':'OPTIONS,POST,GET',
'X-API-KEY' :'egkeyname'
})
}); //wait until the promise return result
data = await res.json();
});

Angular5 Response Header (Content-Disposition) Reading

How Can I read Response Header (Content-Disposition)? Please share resolution.
When I check at either Postman or Google Chrome Network tab, I can see 'Content-Disposition' at the response headers section for the HTTP call, but NOT able to read the header parameter at Angular Code.
// Node - Server JS
app.get('/download', function (req, res) {
var file = __dirname + '/db.json';
res.set({
'Content-Type': 'text/plain',
'Content-Disposition': 'attachment; filename=' + req.body.filename
})
res.download(file); // Set disposition and send it.
});
// Angular5 Code
saveFile() {
const headers = new Headers();
headers.append('Accept', 'text/plain');
this.http.get('http://localhost:8090/download', { headers: headers })
.subscribe(
(response => this.saveToFileSystem(response))
);
}
private saveToFileSystem(response) {
const contentDispositionHeader: string = response.headers.get('Content-Disposition'); // <== Getting error here, Not able to read Response Headers
const parts: string[] = contentDispositionHeader.split(';');
const filename = parts[1].split('=')[1];
const blob = new Blob([response._body], { type: 'text/plain' });
saveAs(blob, filename);
}
I have found the solution to this issue. As per Access-Control-Expose-Headers, only default headers would be exposed.
In order to expose 'Content-Disposition', we need to set 'Access-Control-Expose-Headers' header property to either '*' (allow all) or 'Content-Disposition'.
// Node - Server JS
app.get('/download', function (req, res) {
var file = __dirname + '/db.json';
res.set({
'Content-Type': 'text/plain',
'Content-Disposition': 'attachment; filename=' + req.body.filename,
'Access-Control-Expose-Headers': 'Content-Disposition' // <== ** Solution **
})
res.download(file); // Set disposition and send it.
});
It is not the problem with Angular, is the problem with CORS.
If the server does not explicitly allow your code to read the headers, the browser don't allow to read them.
In the server you must add Access-Control-Expose-Headers in the response.
In the response it will be like Access-Control-Expose-Headers:<header_name>,
In asp.net core it can be added while setting up CORS in ConfigureServices method in startup.cs
this solution help me to get the Content-Disposition from response header.
(data)=>{ //the 'data' is response of file data with responseType: ResponseContentType.Blob.
let contentDisposition = data.headers.get('content-disposition');
}
Firstly you need to allow your server to expose these headers. Note that it will show in you browser network tab, regardless if you have these settings. This makes it 'available'.
With C# it would look something like this:
services.AddCors(options => {
options.AddPolicy(AllowSpecificOrigins,
builder => {
builder
.WithOrigins("http://localhost:4200")
.AllowAnyHeader()
.AllowAnyMethod()
.WithExposedHeaders("Content-Disposition", "downloadFileName");
});
});
When you send your API request to the server ensure that you include the "observe" in you return. See below:
getFile(path: string): Observable<any> {
// Create headers
let headers = new HttpHeaders();
// Create and return request
return this.http.get<Blob>(
`${environment.api_url}${path}`,
{ headers, observe: 'response', responseType: 'blob' as 'json' }
).pipe();
}
Then in your response of your angular on your subscribe you can access your filename like this (the subscribe method is not complete it attaches to a pipe function)
.....
.subscribe((response: HttpResponse<Blob>) => {
const fileName = response.headers.get('content-disposition')
.split(';')[1]
.split('filename')[1]
.split('=')[1]
.trim();
});

How to get custom response header in angular 2?

I am new to angular 2 and currently working with angular 2.2.1 in which I am successfully able to post request and get success response however when I try to get Authorization header from response I always get null whether I am able to get Content-Type header. Below is my solution so far.
service.ts login method:
login(model: LoginModel) {
let requestUrl = '/someurl';
let requestPayload = JSON.stringify(model);
let headers = this.getHeaders(false); // ... Set all required headers
let options = new RequestOptions({ headers: headers }); // Create a request option
return this.http.post(requestUrl, requestPayload, options) // ...using post request
//.map((res: Response)) // ...and calling .json() on the response to return data
.subscribe((res: Response) => {
var payload = res.json();
var authorization = res.headers.get('Authorization');
var contentType = res.headers.get('Content-Type');
console.log(payload, contentType, authorization)
});
}
Header Helper
getHeaders(isSecureAPI: boolean) {
let headers = new Headers({ 'Content-Type': 'application/json', 'Accept': 'application/json' });
if (isSecureAPI) {
headers.append('Authorization', 'GetFromSession');
headers.append('X-UserID', 'GetFromSession');
}
return headers;
}
Fiddler track:
Angular Console Output
So anyone can guide me what I am possibly doing wrong?
Header was allowed but not exposed on CORS server however adding headers.add("Access-Control-Expose-Headers", "Authorization, X-Custom"); on server did the job :)
I've been trying to find a solution and came across this
Let's say I'm using the Microsoft Cors WebAPI 2 Nuget package, and I have the following global configuration in my WebApiConfig.cs:
...
var corsAttr = new EnableCorsAttribute("http://localhost:4200", "*", "*");
config.EnableCors(corsAttr);
...
The EnableCorsAttribute constructor accepts a 4th string parameter to globally allow any additional headers:
var corsAttr = new EnableCorsAttribute("http://localhost:4200", "*", "*", "X-Foo, X-Bar, X-Baz");

How to read received headers in Angular 2?

Can some body tell me how to read received headers in Angular 2?
i have mad a request, for login and password, and there should be sent back headers with Token. I need the token for further workaround.
here is part of the code:
sendLogin(username, password) {
let body = JSON.stringify({"username": username, "password": password});
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({headers: headers});
return this.http.post(this.loginUrl, body, options)
.map(res => res.json())
.map((res) => {
if (res.ok) {
// at least how to console.log received headers?
console.log( res.headers); //undefined
this.loggedIn = res.ok;
} return res.ok;
});
};
thank you.
Most of the time such an issue is related to CORS. You need to explicitly enable allowed headers in the response headers.
You're only be able to see the header in the map only if it's enabled by CORS.
Your server needs to return the following in headers:
Access-Control-Allow-Headers: X-SomeHeader

Resources