REST API url design - python

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.

Related

Scraping ASPX after login with Python but every login gives you a different URL

I'm trying to get the exam result data from my college website for every Roll No. in my class.
Normally you can POST url (www.example.com/login.aspx)with login information, and GET a fixed url after login(www.example.com/home.aspx).
But the page I'm trying to get has a different URL for every Roll no. entered. The URL of login page look like this: "www.example.com/View.aspx". After login, the URL of the result page looks like: "www.example.com/ovengine.aspx?enc=BunchOfNumbersandAlphabets". And those numbers and alphabets are different for each roll number.
So I can't put a URL in my code to get the final result. I don't know how to get the page that comes automatically after the login, without mentioning it's URL.
But the page I'm trying to get has a different URL for every Roll no. entered
No, it is the same URL, and the URL has a parameter. You see this in URL's all the time.
So, for a temperature site it might look like
www.TheWeatherSite.com/?City=Rome
So, the above URL is always the same, but the web site "city" parameter is for the City of Rome. The web code behind can thus use/get/grab/consume that parameter in the code behind. That way we don't create a web page for EACH weather for each city.
so you create ONE page, and then and then PASS the web page a city value that the code behind can consume and use. (say query temperature data from a database for city = above value).
And thus you have to know ahead of time what city you want the weather for. Of course this approach is great since you don't have to create a new web site page to just show/display the weather in a given city.
You are in effect passing a value to some code behind that will run, and use that passed value.
The same goes for your example URL. You note there is ONE parameter called "enc".
So, the web site code behind would:
Grab, get, set the users ID. However, the users ID would be from the security system and the authentication provider. Unless you logged in as that particular user, then you not get that user id.
So, both a user ID (limited to the internal code).
And the "enc" value as the parameter in the URL you have would be required.
So, note in the above sql, we VERY likely need both a studentID and ALSO the "enc" value that some OTHER code from another page gets/grabs from the database.
Now that funny "GUID" (please do google what a GUID is), from a programmers point of view WOULD be sufficient to pull this one row of data from the database, but by ALSO using in the query the users logged on internal id?
Well, then only a given logged on user would be able to see their own set of values that belong to them.
In other words?
Only a drunken un-employed Rodeo clown would JUST require that GUID for pulling out that data. Since if that was the case, then any user could type in that GUID and see others peoples marks. However, there is "some" security by using a GUID, since a user could never guess that value.
If they used "city" like my first URL and parameter example? Then yes, you could guess and know the city value to type in. Or they could have used say student name, or even student number - those you COULD guess with relative ease.
But, for such data, no doubt the user adopted something MUCH more difficult then a starting number like a row number or PK id from a database. So, when the code added the results to that table? They also added a GUID of some type and saved that as a row in the database also.
So you NOT only need JUST the GUID, but that URL will ONLY work for a given pair of values. (the student ID - which is ONLY internal to the code and pulled FROM the authenticated provider. That was this line of code:
= Membership.GetUser.ProviderUserKey
So that above value is going to be the users logon internal ID.
The enc (external) exposed value in the web URL as a parameter, and ALSO the internal logged on value. So the code behind (asp.net) would look something like this:
Dim strSQL As String
strSQL = "SELECT * from tblStudentMarks where StudentID = #pID " &
" AND TestResultsGID = #GID"
Dim cmdSQL As New SqlCommand(strSQL, GetCon)
cmdSQL.Parameters.Add("#pID", SqlDbType.Int).Value = Membership.GetUser.ProviderUserKey
cmdSQL.Parameters.Add("#GID", SqlDbType.VarChar).Value = Request.QueryString("enc")
Dim dReader As New SqlDataAdapter(cmdSQL)
Dim rstData As DataTable
dReader.Fill(rstData)
Note the code:
Request.QueryString("enc")
That allows the code behind to get/grab the parameter (enc) from the URL. But, as I stated, it is high unlikely that JUST the "enc" number is required here. It is possible that ONLY this value is required to pull the data from the row, but then that would be a security hole the size of a open barn door.
Think of your on-line banking.
www.mybank.com/?CustomerNumber=1234
Well, if we JUST use the above CustomerNumber as the means to pull bank data, then I could go to the site and type in YOUR number, or someone's else's number.
So, for this to work?
You will need to obtain a list of enc values (that messy funny long string). Without that parameter then you not be able to set the parameter in the URL.
However, as I stated, you ALSO very likely need some internal "user" logon id that is NOT included in the public exposed URL to ALSO grab that one row of data from the database.
And, even more important? Such web pages usually cannot be hit UNLESS you are a logged in as an authenticated user. In other words that web page will ONLY be dished out to logged in users - if you not logged in, then the server security will automatic NOT dish out the web page unless you are logged in user.
So, for this to work, you need to contact the web site developers, and obtain that list of "enc" values. Once you have that list, then you can generate some code to process that list and insert the correct parameter in the URL. However, you also need to ask if that URL and parameter value will work for JUST you the logged in user, or if that this URL and parameter ONLY works for a give logged in user. Without these values, and without knowing if the URL and parameter will work for any user? (which I doubt it would), then just using a URL to get these values will not work.
It would be even BETTER to have the web site folks create a web service that you can call and in one command it would return all of the data you need anyway, as opposed to over and over having to send the "enc" value, which you don't have anyway.

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.

How to get access to the URL segments after a # in Python Flask?

I'm trying to support OAuth2 login through Python Flask, so I want to handle a URL that looks like this:
http://myserver/loggedIn#accessToken=thisIsReallyImportant
but when I handle the callback it just seems to drop all the characters after the # in the URL, which contains the important Oauth access token. How do I get this info? It's not included in request.url
ETA: I can retrieve it in client-side javascript using window.location in Javascript, but then I'd have to pass it back to the server, which feels a little hokey but maybe Oauth2 is meant to be done that way?
From the RFC:
Fragment identifiers have a special role in information retrieval
systems as the primary form of client-side indirect referencing
[...]
the fragment identifier is not used in the scheme-specific
processing of a URI; instead, the fragment identifier is separated
from the rest of the URI prior to a dereference
As such, flask drops everything after the '#'. If you want to forward these to the server, you'll have to extract them on the client and pass them to the server via a query parameter or part of the URL path.
You are using the incorrect OAuth 2 grant type (implicit grant) for what you want to do. Implicit grant supplies the token in the fragment as you observed to be used by a javascript client. There is another type of grant, authorization code, which is similar but supplies it in the URI query which you can access from Flask.
You can tell the two apart from the the redirect URI you create for authorization, if it has response_code=code you are on the right track. You currently use response_code=token.
If you are using Facebook look at https://developers.facebook.com/docs/facebook-login/login-flow-for-web-no-jssdk/
For Google look at https://developers.google.com/accounts/docs/OAuth2WebServer
You might also be interested in https://flask-oauthlib.readthedocs.org/en/latest/ which can help you with OAuth.

Distinguish data received via POST and GET in Cherrypy

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

Categories