Posting from one RequestHandler to another in App Engine - python

Goal: Using app engine's basic webapp framework I want to create a new request, with post data, to send to another RequestHandler. Something like pageGenerator.post({'message':'the message','datum1':datum1,...})...
Problem Description: One request handler, call it pageGenerator, creates a page with a form on it. When the user submits the form, the post goes to a different handler: dataProcessor. If dataProcessor finds some problem with the submitted data it would send the submitted data plus an Error Message to 'pageGenerator`'s post method, and pageGenerator would serve up the page with the error message.
How do I pass data (and control) back and forth like this? I would like pageGenerator to be able to get the data with self.request.get('message').

Sounds like you're over-complicating things. Consider just having a common method to show the form that can be invoked in different circumstances:
class FormHandler(webapp.RequestHandler):
def get(self):
self.show_form()
def post(self):
if form_is_valid():
handle_success()
else:
self.show_form({'feedback':'Validation failed'})
def show_form(self, vals={}):
vals['field1'] = self.request.get('field1')
vals['field2'] = self.request.get('field2')
html = template.render('form.html', vals)
self.response.out.write(html)
If you really need "display form" and "process form" to be in different handler classes, you can accomplish the same thing by defining show_form() in a common parent class.

Related

How to wait in django until a request for endpoint arrives?

Tell me with what you can wait for a response to another endpoint?
I am on the main page (index), entering something into the form. The POST request is sent to another server. At this moment:
another server processes the data and, depending on their correctness, makes a POST request to my url /answer (True or False).
I will be redirected, for example, to another page.
How to register the logic of another page (another) so that Django waits for a POST request from another server to /answer and depending on this request True/False, I output everything OK or everything Bad on this page?
url.py
urlpatterns = [
path('index/', index, name='index'),
path('page_2/', page_2, name='page_2'),
path('answer/', answer, name='answer'),
]
-------------------------------------------------
views.py
def index(request):
requests.post(example.com, data='My data')
return redirect('page_2')
def page_2(request):
# wait request in answer
if request.session['answer'] is True:
return 'Ok'
retunr 'Bad'
def answer(request):
data = request.data
# send to page_2 or save in request.session['answer']
return Response(status=200)
I reckon it's a strange situation and it's better if you could redesign the logic of your code so that the view functions process the request ASAP and not busily wait for external events to be triggered as it increases response time.
However, in order to achieve this purpose we need a communication channel between index and answer view. So to implement a communication like this:
index: Hey answer! I've sent the request. I'm going to sleep, wake me up if you got its result.
answer: Oh I got it man. Here you are. Wake up!
index: Thanks. Now I process it and return my response.
So this channel might be anything! A model in database, some entities in redis, some files in filesystem, etc.
One possible solution using the models might be:
Create a model(name it ExampleRequest for example) consisting of a boolean field named received
In index view, create an instance of ExampleRequest with received = False before sending the request.
In answer view, find the previously created ExampleRequest and set its received field to True
In index view, after sending the request, in a while loop, query the database and check if the created ExampleRequest instance has received = True? If yes, then the external server has called answer. So break the while and do the rest of the work; otherwise, just time.sleep(1) and continue the while loop.
Just note:
When multiple clients are using your website, some of them might request index view and then there will be more than one instance of ExampleRequest. In answer view, you have to be able to find out the current request is related to which one of those instances. You might need to store a unique data related to that request in ExampleRequest model.
You might consider the situation where the other server doesn't call answer view ever. So there might be an upper bound for the iterations of index view's while loop.
Also you may remove ExampleRequest instances after capturing them in index view in order to optimize disk usage of your database.
I say it again, it's better if you can do the polling stuff in frontend instead of backend to avoid high response time and other syncing issues.
This might not the complete answer, but it gives you way.
def index(request):
requests.post(example.com, data='My data')
return redirect('page_2')
Change it to following
import httpx
async def index(request):
async with httpx.AsyncClient() as client:
response = await client.post(example.com, data='My data')
print(response.json())

How to pass some input to my Flask AI API in my locustfile.py

I'd like to do a load test over my NLP web app using Locust. The website is simple where users only need to pass in their text and its language type, and then the results will show on the next page.
Therefore, I want to make my "locusts" to first pass in two values on the index page and then go to the corresponding page, which is supposed to be quick and easy. But my code doesn't work and the error message wrote there was something wrong with GET POST method (HTTP 405 & HTTP 500 error). Can anyone help me check my code?
from locust import HttpUser, TaskSet, between, task, SequentialTaskSet
class UserBehavior(SequentialTaskSet):
#task
def submit(self):
self.client.post('/', {'text': 'Kobe Bryant is the best NBA player.', 'language': 'en'})
#task
def get_boto(self):
self.client.get('/boto')
class WebsiteUser(HttpUser):
tasks = [UserBehavior]
wait_time = between(1, 2)
May be before post request with data user must login?
class UserBehavior(SequentialTaskSet):
def on_start(self):
self.client.post("/login", {
"username": "test_user",
"password": ""
})
A 405 sounds like you might not be sending your requests in the right way, maybe using the wrong verb. I would use a debug proxy (like Charles Proxy) to record the traffic to your site while you go through your flow so you know how the requests are structured and expected to be. Then I would create a new separate Python file and try to use Requests to get something working successfully (check out Python : Trying to POST form using requests for a POST example). Locust's HttpUser.client is based on Requests so once you get the separate Python file working and doing what you want, you should be able to drop in the same calls and replace request or session with self.client and it should work.

Problems with django api on second request

I am working on a flutter app for creating schedules for teachers. I created a Django project to generate a schedule based on the data in the post request from the user. This post request is sent from the flutter app. The Django project doesn't use a database or anything, It simply receives the input data, creates the schedule and returns the output data back to the user.
The problem is that the process of creating the schedule only works 1 time after starting the Django server. So when I want another user to send a request and receive a schedule I have to restart the server... Maybe the server remembers part of the data from the previous request?? I don't know. Is there somekind of way to make it forget everything after a request is done?
When I try to repeatedly run the scheduler without being in a Django project it works flawlessly. The Scheduler is based on the Google cp_model sat solver. (from ortools.sat.python import cp_model). The error I get when running the scheduler the second time in a Django project is 'TypeError: LonelyDuoLearner_is_present_Ss9QV7qFVvXBzTe3R6lmHkMBEWn1_0 is not a boolean variable'.
Is there some kind of way to fix this or mimic the effect of restarting the server?
The django view looks like this:
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from .scheduler.planning import Planning
from .scheduler.planning import print_json
import json
# Convert the data and creates the schedule.
#csrf_exempt
def generate_schedule(request):
if request.method == 'POST':
try:
data = json.loads(request.body)
planning = Planning()
planning.from_json(data)
output_json = planning.output_to_json()
print_json(output_json)
response = json.dumps(output_json)
except Exception as e:
print(e)
print("The data provided doesn't have the right structure.")
response = json.dumps([{'Error': "The data provided doesn't have the right structure."}])
else:
response = json.dumps([{'Error': 'Nothing to see here, please leave.'}])
return HttpResponse(response, content_type='text/json')
There is no beautiful way to restart the server (aside from just killing it by force, which is hardly beautiful).
You're probably using some global state somewhere in the code you're not showing, and it gets screwed up.
You should fix that instead, or if you can't do so, run the solving in a subprocess (using e.g. subprocess.check_call(), or multiprocessing.Process()).
The CP-SAT solver is stateless. The only persistent/shared object is the Ctrl-C handler, which can be disabled with a sat parameter. (catch_sigint if my memory is correct).

In Flask is there a way to ignore a request for a route that doesn't exist

I'm writing an app using Flask.
I have a set of routes and they work.
What I want to do on the client side is to ignore any requests to invalid URLs. That is I do not want to render any 404/error pages in the app. I would like an alert that says the URL is invalid and for the browser to simply stay on the same page.
I don't want to be checking the URLs in JavaScript on the client, as this would expose them.
I have a route which responds correctly to unknown URLs:
#app.errorhandler(404)
def non_existant_route(error):
return jsonify({"no":"such page"})
If I delete the return statement I get a 500 error.
I can't use abort()
Does this idea violate some HTTP principle?
Thanks
It sounds like you need a "catch-all" endpoint. Typically, it seems a catch-all endpoint would return a generic 404, but in your case, you probably want to return a 200 with some contextual information. Here's basically how you can do it (credit goes to http://flask.pocoo.org/snippets/57/):
from flask import Flask
app = Flask(__name__)
#app.route('/', defaults={'path': ''})
#app.route('/<path:path>')
def catch_all(path):
# returns a 200 (not a 404) with the following contents:
return 'your custom error content\n'
# This is just one of your other valid routes:
#app.route('/stuff')
def stuff():
return 'stuff\n'
if __name__ == '__main__':
app.run()
If you run this and curl various endpoints of the test app, here's what you get:
$ curl localhost:5000/stuff
stuff
$ curl localhost:5000/foo/bar
your custom error content
$ curl localhost:5000/otherstuff
your custom error content
As you can see, your other routes will still work as you expect.
I've decided a solution to this is too hard! I can not find any way to get the browser to ignore a response. There is no response header for 'do nothing'. If there was we would probably never see a webserver error again, which would not be good.
I could ajaxify all the requests as a way to grab the response headers and analyze them before any rendering or redirecting happens. That starts to break all the navigation (back buttons at least) and the pretty URLs. I could bung in a JS routing framework etc, and while I'm leaning how it works I'm not building my app (I already have enough to learn!)
#app.errorhandler(404)
def page_not_found(error):
return redirect(url_for('index'))
If you come up with something great post it anyway, I'm not the first to ask this question, and probably not the last.
Thanks
I remember reading about a javascript library some days ago (but I don't remember the name...). The clou with this library was, that all links and form submits were loaded not directly into the browser "_top" frame/window but into a hidden div and afterwards, when done, the content of the page was replaced by the content of this hidden div.
So if you want to catch bad links and such on client side you could hook up all links and submits and check the http response code. If it is not 200 (ok) you display an error. If it is okay you decide, if you replace the old page with the new content.
But there are two problems with this solution:
1. You would have to change the browsers location (in the address bar) without reloading the page of course!
2. It might get tricky to post some file uploads with javascript.
If I find the link or name of the js-library I saw, I will tell you!

Reading POST data from Google App Engine pipeline callback

I'm trying to download data from an external API. There's going to be a lot of downloads, so I want to use pipelines for easier parallelization. The way the API is set up, I can make a request to start a download job, and pass a postback url in that request. When the download job finishes, their API sends a POST to the given url. I want to do the following:
class DownloadPipeline(pipeline.Pipeline):
async = True
public_callbacks = True
def run(self, filename):
postback = self.get_callback_url()
# make API request with postback as a param
def callback(self):
# Read data from the POST
However, all the docs I've read online only have examples of GET requests on the callback url, where data is passed through a query string on the URL. Is there a way to read POST data instead?
Looks like both the POST and GET both call over to run_callback() ... so you should be able to do either

Categories