Distinguish data received via POST and GET in Cherrypy - python

The variable cherrypy.request.params as it is described in the API contains the query string and the POST variables in a dictionary. However combing over this, it seems that it contains every variable received after processing the full request URI to pull the GET data. This then becomes indistinguishable from POST data in the dictionary.
There seems to be no way to tell the difference, or perhaps I am wrong.
Can someone please enlighten me as to how to use purely posted data and ignore any data in the query string beyond the request URI. And yes I am aware I can find out whether it was a POST or GET request but this does not stop forgery in requests to URIs containing GET data in a POST request.
>http://localhost:8080/testURL/part2?test=1
>POST username = test
"cherrypy.request.params" has 2 variables
test = 1
username=test

The docs aren't very clear on this point, but starting in CherryPy 3.2, you can reference request.body.params to obtain just the POST/PUT params. In 3.2 and below, try request.body_params. See http://docs.cherrypy.org/dev/refman/_cprequest.html#cherrypy._cprequest.Request.body_params

Related

REST API url design

I have a REST API that has a database with table with two columns, product_id and server_id, that it serves product_ids to specific servers which request the data(based on the server_id from table).
Let's say I have three servers with server_ids 1,2 and 3.
My design is like this: /products/server_id/1 and with GET request I get json list of product_ids with server_id = 1, similarly /products/server_id/2 would output list of product_ids for server_id = 2.
Should I remove these routes and make a requirement to send POST request with instructions to receive product_ids for specific server_id in /products route only?
For example sending payload {"server_id":1} would yield a response of list of product_ids for server_id = 1.
Should I remove these routes and make a requirement to send POST request with instructions to receive product_ids for specific server_id in /products route only?
Not usually, no.
GET communicates to general purpose components that the semantics of the request message are effectively read only (see "safe"). That affordance alone makes a number of things possible; for instance, spiders can crawl and index your API, just as they would for a web site. User agents can "pre-fetch" resources, and so on.
All of that goes right out the window when you decide to use POST.
Furthermore, the URI itself serves a number of useful purposes - caches use the URI as the primary key for matching a request. Therefore we can reduce the load on the origin server by re-using representations have have been stored using a specific identifier. We can also perform magic like sticking that URI into an email message, without the context of any specific HTTP request, and the receiver of the message will be able to GET that identifier and fetch the resource we intend.
Again, we lose all of that when the identifying information is in the request payload, rather than in the identifier metadata where it belongs.
That said, we sometimes do use the payload for identifying information, as a work around: for example, if we need so much identifying information that we start seeing 414 URI Too Long responses, then we may need to change our interaction protocol to use a POST request with the identifying information in the payload (losing, as above, the advantages of using GET).
An online example of this might be something like an HTML validator, that accepts a candidate document and returns a representation of the problems found. That's effectively a read only action, but in the general case an HTML document is too long to comfortably fit in the target-uri of an HTTP request.
So we punt.
In a hypermedia api, like those used on the world wide web, we can get away with it, because the HTTP method to use is provided by the server as part of the metadata of the form itself. You as the client don't need to know the server's preferred semantics, you just need to know how to process the form data.
For instance, as I type this answer into my browser, I don't need to know what the target URI is, or what HTTP method is going to be used, because the browser already knows what to do (based on the HTML and whatever scripts are running "on demand").
In REST APIs, POST requests should only be used in order to create new resource, so in order to retrieve data from server, the best practice is to perform a GET request.
If you want to load products 1,2,4,8 on server 9 for example, you can use this kind of request :
GET https://website/servers/9/products/1,2,4,8
On server side, if products value contains a coma separated list, then return an array with all results, if not return just an array with only one item in order to keep consistency between calls.
In case you need to get all products, you can keep only the following url :
GET https://website/servers/9/products
As there is no id provided in products parameter, then the server should return all existing products for requested server parameter.
Note : in case of big amount of results, they must by paginated.

Flask GET - Retrieve an undetermined number of parameters in the URL

WHAT: I have a Flask server and I would like to build a route and pass it an undetermined number of parameters via a GET method.
WHY: I would like to give the user the ability to pick several dates from a date picker, and give this list to the server which would make an SQL request to my database to retrieve data corresponding to those dates selected by the user. There would be hundreds of files and I would also limit the number of requests/responses made for performance as much as possible.
I have little experience with Flask but enough to handle routes like:
#app.route('/photos/year=<int:year>&month=<string:month>', methods=['GET'])
or even :
#app.route('/photos/<year>.<month>', methods=['GET'])
I have 3 cases :
The user has the ability to choose an interval of dates, in which case I would use a route like '/photos/< dateFrom> _ to _< dateTo>' (without spaces) ;
or a single date, in which case I would use a route like '/photos/< date >'
or multiple dates non-necessarily contiguous, and I don't know how to handle it, but what I would do would look like something like this : '/photos/< date1>.< date2>?.< date3>?...'
('?': representing an optional parameter ; '...': representing an undetermined number of parameters, just like in programming language (actually this would be enough : '/photos/< date>...' if a syntax like '...' exists).
I've been looking for answers but couldn't find something. The only thing that may be interesting is passing a JSON object, but yet I don't know how to deal with this, I'm going to look to it until I get an answer. I will also have a look to Flask-RESTful extension in case it helps.
Any help would be appreciated.
I don't think there is a need for separate routes for three use cases. You might want to have a single GET route and receive dates as url params.
In this case your flask route will become:
#app.route('/photos', methods=['GET'])
you can now pass any key value pair in url as
/photos?date1=1&date2=2
you can access these params using
from flask import request
date1 = request.args.get('date1')
date2 = request.args.get('date2')
If you want a list of date just send them using same key in the URL and use
request.args.getlist(<paramname>)
However since in your case the keys that will come as parameters may vary from request to request, be careful to check if the key you are trying to use exist in the request that came. I recommend you to go through documentation of request object for more details.
However as a general practice if your parameters are more complex you can consider using JSON objects as payload instead of URL params.

How can I directly retrieve only part of a JSON document, rather than extracting what I need from the whole thing?

I want to make a JSON request with the Python library requests where I only obtain certain JSON objects.
I know that it is really easy to process the JSON object obtained to only focus in the needed information, but that would throttle the request efficiency (in case it is done repeatedly).
As said, I know this is a possibility:
url = 'www.foo.com'
r = requests.get(url).json()
#Do something with r[3]['data4'], the only one who is going to be used.
But how could I directly only obtain r[3]['data4'] from the request?
Short Answer
To answer your question no, you can't but to understand why you need to know what is happening behind the scenes.
Behind the scenes
When you make a request such as r = requests.get('www.foo.bar') you are making a request to the server and you are viewing the result of that request when you do r.json(). This means that you cannot just get r[3]['data'] as you are parsing what the server sends to you unless the server only sends r[3]['data']. It may be possible to filter out everything else apart from that in the response processing but I am unaware of how to do it.
You can't, if the server does not allow it. If the target server allows you to specify fields you want then you can send that field list in your request and server will return you only those fields in JSON. Otherwise your will have to parse full JSON response and get your desired fields.

Where am I going wrong in the Amazon DynamoDB query API Signing Process

I'm using a product called Alteryx and I'm trying to write an Alteryx macro that utilizes the Dynamodb query API so that I can access my Dynamodb tables from within Alteryx. Unfortunately, I'm precluded from utilizing one of the Amazon SDK so have to code the Amazon query api signing manually / within Alteryx.
I am utilizing a Python Post example that is included in the Amazon documentation to guide me through the process. The Python example can be found here: Python Post Example.
I have completed each of the tasks laid out in the example:
Define Request Variables
Create a Canonical Request
Create the string to sign
Calculate the signature
Add signing information to request and make request.
Initially I was getting the following error:
InvalidSignatureException","message":"Signature·expired:·20150704T101118Z·is·now·earlier·than·20150704T135625Z·(20150704T141125Z·-·15·min.)
Although the time on my computer is correct and the time of: 101118Z included in my request was actually correct, the error message was telling me that the signature had expired four hours ago. My work around for this error was to add 4 hours to my date/time variable(s) and this seemed to do the trick.
QUESTION 1. Is anyone familiar with what causes this error and is there a way to fix it without adding 4 hours to my date/time variable. Could this cause additional complications in the signing API signing and request processes.
After applying my date/time workaround, I was rewarded with a new error message:
InvalidSignatureException","message":"The·request·signature·we·calculated·does·not·match·the·signature·you·provided.·Check·your·AWS·Secret·Access·Key·and·signing·method.·Consult·the·service·documentation·for·details.\n\nThe·Canonical·String·for·this·request·should·have·been\n'POST\n/\n\ncontent_type:\nhost:dynamodb.us-east-1.amazonaws.com\nx-amz-date:20150704T141834Z\nx-amz-target:DynamoDB_20120810.CreateTable\n\ncontent_type;host;x-amz-date;x-amz-target\n09a8bcdeea1d20631f887235820bbff0a614679080a2e74a89ceb1a1bcc71b44'\n\nThe·String-to-Sign·should·have·been\n'AWS4-HMAC-SHA256\n20150704T141834Z\n20150704/us-east-1/dynamodb/aws4_request\nec549e12e44faf7ee750e19b570eaf2389f82e722ae2978b535df6fd6f3df129'\n"}
So next I compared my Canonical Request to the one provided in the error message. This is what I found:
The request were identical with one exception. The canonical request provided in the error message had the content-type: header but excluded the related content type value.
My canonical request included both the content-type header and value.
The hash of request parameters at the end of the canonical request matched perfectly along with everything else.
This is important because the canonical request is an input for the next step of the process. You have to calculate the sha256 hash digest of the canonical request to create the String To Sign. I've tried two alternative approaches/workarounds for this issues:
First I used the canonical request I derived (including the content-type value) to calculate the string to sign. In this case everything matched the error messages String To Sign, except the last element: the hash of the canonical request.
My next approach was to calculate a canonical request that excluded the content-type value and therefore matched the canonical request included in the error message exactly. In this scenario, the derived String To Sign matched perfectly except for the hash of the canonical request.
QUESTION 2: Has anyone come across this error; do you know the cause and/or do you have a workaround.
My hope is once I'm able to address Question 3, I will be able to successfully complete the 4th task, calculating the signature and successfully make an api request.
QUESTION 3: Is anyone aware of any other gotchas in this process or have any additional suggestions or insights.
Unless you have very specific reasons to do it manually, the standard advice is to use boto for AWS access from Python.
EDIT: More generally, the standard advice is to use available libraries where they would fit your use case.
EDIT 2: See comments about sys.path and workarounds for your case.

Distinguishing between GET and POST data in CherryPy?

I've been deciding between Python web frameworks for a project of mine and I've really liked how lightweight, flexible, and concise CherryPy is compared to others. The only problem I'm having is I can't find any documentation on how to distinguish between data sent via GET and via POST.
For example, I don't want users to be able to provide their login credentials through a GET request (http://example.com/login?username=user&password=pass) but, according to CherryPy's tutorial, all data is sent as method parameters, no matter what HTTP method they're sent as. Is there some way to say I only want the POST data or do I have to use MethodDispatcher?
Thanks!
See the docs.
A string containing the HTTP method, such as "GET" or "POST". Set
in the "run" phase.
looks like checking cherrypy.request.method is what you want to do.

Categories