'Bad request' error on flask [duplicate] - javascript

When pressing a button i call a javascript function in my html file which takes two strings as parameters (from input fields). When the function is called i want to pass these parameters to my flask file and call another function there. How would i accomplish this?
The javascript:
<script>
function ToPython(FreeSearch,LimitContent)
{
alert(FreeSearch);
alert(LimitContent);
}
</script>
The flask function that i want to call:
#app.route('/list')
def alist(FreeSearch,LimitContent):
new = FreeSearch+LimitContent;
return render_template('list.html', title="Projects - " + page_name, new = new)
I want to do something like "filename.py".alist(FreeSearch,LimitContent) in the javascript but its not possible...

From JS code, call (using GET method) the URL to your flask route, passing parameters as query args:
/list?freesearch=value1&limit_content=value2
Then in your function definition:
#app.route('/list')
def alist():
freesearch = request.args.get('freesearch')
limitcontent = request.args.get('limit_content')
new = freesearch + limitcontent
return render_template('list.html', title="Projects - "+page_name, new=new)
Alternatively, you could use path variables:
/list/value1/value2
and
#app.route('/list/<freesearch>/<limit_content>')
def alist():
new = free_search + limit_content
return render_template('list.html', title="Projects - "+page_name, new=new)

Related

How can I pass a server-side variable to HTML and have the HTML page return an auto-filled form?

I am building an HTML page in Google Apps Script with CRUD functionality. The user can currently add data, edit data, and search data (I am not adding a delete feature). I would like the user to receive the form url link with an ID that when they go BACK to that link, it auto-fills the form with the previously added data.
In my HTML file, I have the following button defined:
document.getElementById("sbtn").addEventListener("click",getTID);
Once a user has entered data, it gets sent to a Google Sheet. The user HAS to enter a unique ID that they've already been provided. Using this ID, they can enter it, hit search, and it runs getTID():
function getTID() { //TID CODE
var transID = document.getElementById("tid").value;
if (transID.length === 36) {
google.script.run.withSuccessHandler(updateAllData).getID(transID);
} else {
alert("Transaction ID is not long enough.\nPlease copy the Transaction ID EXACTLY!\n\nFor Example: https:/workwebsiteconcealedforprivacy/w?txid=36275284-2ed6-4868-97b2-16bc1fde1a08\n\nThe Transaction ID is: 36275284-2ed6-4868-97b2-16bc1fde1a08")
}
}
This takes the ID they gave, references the spreadsheet and then returns values it found by index. Now, I have in my server-side GS file, the following in doGet:
var urlValue = '';
function doGet(e) {
// Test Code
var ss = SpreadsheetApp.openById(id);
var ws = ss.getSheetByName("Options");
var list = ws.getRange(1, 1, ws.getRange("A1").getDataRegion().getLastRow(), 1).getValues();
var htmlListArray = list.map(function (r) { return '<option>' + r[0] + '</option>'; }).join('');
var title = "Please Work";
var vals = JSON.stringify(e);
if ('v' in e.parameter){
urlValue = String(e.parameter['v']);
//return HtmlService.createHtmlOutput(urlValue);
}
return render("page",{list: htmlListArray, title});
and the following:
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function render(file, argsObject) {
var tmp = HtmlService.createTemplateFromFile(file);
if (argsObject) {
var keys = Object.keys(argsObject);
keys.forEach(function (key) {
tmp[key] = argsObject[key]
});
}
return tmp.evaluate();
}
If I uncomment the return HtmlService.createHtmlOutput(urlValue); line, I can see that IF an ID is in the URL, it returns the correct parameter.
My problem is that I cannot get the HTML to read the urlValue variable and autorun getTID() when the user enters the url with the correct parameter. The correct functionality is that IF the parameter is found, it auto populates the HTML form. If it doesn't, it returns the blank form.
There is an error on
return render("page",{list: htmlListArray, title});
On {list: htmlListArray, title} the name of the second property is missing.
To "read the urlValue variable" there are two options:
pass the parameters from the URL using the event object of the doGet function. For this you have two options, create the HtmlService.HtmlOutput object from an html string generated using "vanilla" JavaScript or create it from a HtmlService.HtmlTemplate object.
get the parameters from the URL directly on the client-side code using google.script.url.getLocation .
If you go for the first option, then you should pass someway the urlValue to the render function. In the question code urlValue is a global variable, so you might add the following before the render's return statement.
tmp.urlValue = urlValue;
Then you have to add a scriptlet on the html file to handle this value and "autorun" getTID. Scriptlets is a feature of Templated HTML.

Functions are not being executed as intended. I expect the page to switch to 'setup' but that doesn't happen

I'm creating a very basic log-in function on GAS HTML. In my HTML, I have a form that collects and stores user input with assigned variables (fn, ln, em, pw). It then sends the info to the sheet (which has formulas that verify the credentials) then a certain cell ('E2') will display "Found" or "Not Found". Then it will print the name in cell ('E1') if the if statement is false. This part does execute correctly, but the 2 functions, render('setup') and notFound() will not execute (regardless of when the if statement is true or false, neither one works.
I know the render('setup') function works perfectly fine in the doGet and when it is called in other functions.
function signIn(fn,ln,em,pw) {
var url = 'www.somelink.com'
var ss = SpreadsheetApp.openByUrl(url);
var login = ss.getSheetByName('login');
var fname = login.getRange('F1');
var lname = login.getRange('G1');
var email = login.getRange('H1');
var pword = login.getRange('I1');
fname.setValue(fn);
lname.setValue(ln);
email.setValue(em);
pword.setValue(pw);
var check = login.getRange('E2').getDisplayValue();
var name = login.getRange('F2').getDisplayValue();
if (check === "Not Found"){
return notFound(); // THIS DOESN'T EXECUTE
} else {
login.getRange('E1').setValue(name);
return render('setup'); // AND THIS DOESN'T EXECUTE
}
}
EDIT: HTML for the call signIn function.
<div class="button" align="center">
<button id="submit" class="btn waves-effect waves-light" onclick="var fn = document.getElementById('first_name').value;
var ln = document.getElementById('last_name').value;
var em = document.getElementById('email').value;
var pw = document.getElementById('password').value;
google.script.run.signIn(fn,ln,em,pw)">Submit
<i class="material-icons right">send</i>
</button>
If I understand you correctly, you want your web app to redirect the user to another HTML page after submitting the values, if some conditions are met.
You cannot do this by just calling a server-side function from the client-side: google.script.run is used to retrieve data that is not available on the client, but not to render a different HTML file.
What you could do instead is the following:
Instead of returning a function (like render or notFound), return a parameter that can be used on the client-side. For example, you could do:
function signIn(fn,ln,em,pw) {
// Rest of your code
if (check === "Not Found") {
return "notFound";
} else {
return "setup";
}
}
Your client-side function (which I've called callSignIn) can then pass this returned parameter to another function called redirect, thanks to withSuccessHandler(function) (this handler is necessary since google.script.run.signIn(fn,ln,em,pw) by itself will always return void):
function callSignIn() {
var fn = document.getElementById('first_name').value;
// Rest of your code
google.script.run.withSuccessHandler(refresh).signIn(fn,ln,em,pw);
}
Next, the value returned by signIn is passed as an argument of the function refresh, which refreshes the web app with window.open if the argument is setup, all the while passing setup as a query parameter (pageRedirect), which can later be used by the event parameter (you can retrieve the web app URL dynamically from the server-side instead of writing it manually — see this answer):
function refresh(redirectPage) {
if (redirectPage === "setup") {
window.open("https://script.google.com/macros/s/{your-web-app-id}/exec?pageRedirect=" + redirectPage, "_top");
} else {
// Whatever you want to do if "notFound"
}
}
Finally, back to the server-side, the function doGet gets called by the GET request from window.open. This function can check if there is a query parameter in the request via e.parameter, thanks to the event object, and render a different page depending on this parameter:
function doGet(e) {
if (!e.parameter.pageRedirect) {
return HtmlService.createHtmlOutputFromFile("index");
} else {
return HtmlService.createHtmlOutputFromFile(e.parameter.pageRedirect)
}
}
Reference:
Class google.script.run (Client-side API)
Window.open()
Web Apps: Request parameters

How to pass variables from xs javascript to ABAP function

I have been give the task of passing ABAP to xs Javascript.
This will use and ODATA service where a POST request with body.data will have an object with headers and an array of objects that I will be able to use in the following format:
{
"IvCompCode":"1710",
"IvDocType":"NB",
"IvPurchOrg":"1710",
"IvPurGroup":"002",
"IvVendor":"17386001",
"ItemsSet":[
{
"Ebelp":"00010",
"Matnr":"MZ-RM-M500-09",
"Werks":"1710",
"Lgort":"171S",
"Menge":"5.000",
"Netpr":"100.000"
},
{
"Ebelp":"00020",
"Matnr":"MZ-RM-M500-07",
"Werks":"1710",
"Lgort":"171S",
"Menge":"4.000",
"Netpr":"105.000"
}
]
}
I can call the ABAP function inside the javascript function, however, I have no idea how to pass the variables from the ODATA service and then simply put them inside the ABAP function, while also keeping them in the javascript function so they can be used in another logic. Here's what I have right now:
<script language="JavaScript">
function callABAPMethod()
{
<%
DATA: po_header LIKE bapimepoheader,
poheaderx LIKE bapimepoheaderx,
tab_poitem TYPE STANDARD TABLE OF bapimepoitem,
tab_poitemx TYPE STANDARD TABLE OF bapimepoitemx,
tab_poitem_struct LIKE bapimepoitem,
tab_poitemx_struct LIKE bapimepoitemx,
ebelp_num(5) TYPE n,
ebelp_char(5) TYPE c.
po_header-comp_code = iv_comp_code.
po_header-doc_type = iv_doc_type.
po_header-vendor = iv_vendor.
po_header-purch_org = iv_purch_org.
po_header-pur_group = iv_pur_group.
poheaderx-comp_code = 'X'.
poheaderx-doc_type = 'X'.
poheaderx-vendor = 'X'.
poheaderx-purch_org = 'X'.
poheaderx-pur_group = 'X'.
LOOP AT zpoitems INTO DATA(item).
CLEAR: tab_poitem_struct, tab_poitemx_struct.
tab_poitem_struct-po_item = item-ebelp.
tab_poitem_struct-material = item-matnr.
tab_poitem_struct-plant = item-werks.
tab_poitem_struct-stge_loc = item-lgort.
tab_poitem_struct-quantity = item-menge.
tab_poitem_struct-net_price = item-netpr.
tab_poitemx_struct-po_item = item-ebelp.
tab_poitemx_struct-po_itemx = 'X'.
tab_poitemx_struct-material = 'X'.
tab_poitemx_struct-plant = 'X'.
tab_poitemx_struct-stge_loc = 'X'.
tab_poitemx_struct-quantity = 'X'.
tab_poitemx_struct-net_price = 'X'.
APPEND tab_poitem_struct TO tab_poitem.
APPEND tab_poitemx_struct TO tab_poitemx.
ENDLOOP.
IF iv_update EQ 'X'.
IF iv_po_number IS NOT INITIAL.
CALL FUNCTION 'BAPI_PO_CHANGE'
EXPORTING
purchaseorder = iv_po_number
poheader = po_header
poheaderx = poheaderx
TABLES
return = return
poitem = tab_poitem
poitemx = tab_poitemx.
IF sy-subrc = 0.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT' .
ENDIF.
ENDIF.
ELSE.
CALL FUNCTION 'BAPI_PO_CREATE1'
EXPORTING
poheader = po_header
poheaderx = poheaderx
IMPORTING
exppurchaseorder = po_numer
TABLES
return = et_return
poitem = tab_poitem
poitemx = tab_poitemx.
IF sy-subrc = 0.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'.
ENDIF.
ENDIF.
%>
}
</script>
You can create custom oData service for cathing ABAP codes in backend which are sending from js. Use SEGW t-code for creating custom service and implement your own ABAP code for catching request. Some of sample blog here.
On backend side you can create dynamic program in run time on ABAP side but generally not approved by system admins. Your request may be catched by code scan tools. It has risk because of, someone manipulate your code and change your codes to access system without privileges.
Check the GENERATE SUBROUTINE POOL statement for running dynamic ABAP code in runtime.

Dynamic EventSource with flask and javascript

I am currently building a tool using flask that does various actions using ssh. One of those actions is using DD to copy from X to Y.
I currently have the following javascript set up on my page
<script>
var source == new EventSource("/dd");
source.onmessage = function(event){
$('.determinate'.css('width', event.data+'%')
if(event.data == 100){
source.close()
}
}
Which calls the following flask generator which parse's the stdout of DD to return a % value for the current progress.
#app.route('/dd')
def progress():
def generate():
ssh.SSHClient('127.0.0.1', username, password)
chan = ssh.get_transport().open_session()
chan.settimeout(10800)
try:
ssh.do('dd if=/dev/sda of=/test.img')
while data:
data = chan.recv_stderr(1024)
try:
yield "data: " + str(data) + "\n\n\"
return Response(generate(), mimetype='text/event-stream')
The above is pseudo code, but the things i want to be able to change are the DD command (dd if=/dev/sda of=/test/img) from variables i get from the form that triggers this page, as well as the hostname from the ssh.connect function with request.remote_addr.
When i try to replace '127.0.0.1' with request.remote_addr i get an out of context error.
Is there anyway to pass flask/jinja2 variables such as {{ image.path }} to my generator view?. The pseudo code i want would be this, where hostname, and dd is dynamic ( changes are in curly brackets )
#app.route('/dd')
def progress():
def generate():
ssh.SSHClient({{ request.remote_addr }}, username, password)
chan = ssh.get_transport().open_session()
chan.settimeout(10800)
try:
ssh.do('dd if={{ device }} of={{ image.path }}')
while data:
data = chan.recv_stderr(1024)
try:
yield "data: " + str(data) + "\n\n\"
return Response(generate(), mimetype='text/event-stream')
Just figured it out, didn't read the last paragraph of the docs.
If You want to keep the context of the previous request in the generator just change
return Response(generate(), mimetype='text/event-stream')
to
return Response(stream_with_context(generatE()), mimetype='text/event-stream')
For passing data i just used the "session" object to pass data and then remove it when im done using it.

Web API post parameter

I currently have an issue with a webapi call. I want to download and open a logfile with my ApiController.
I use a javascript function to post a filename to my controller.
Here is a helper function to post the parameter (answer from dystroy):
How to replace window.open(...) with a POST
Now when I use a simple string as parameter in my controller I can’t get the parameter, it is always null.
public HttpResponseMessage PostDownloadLogFile([FromBody]string psFileName)
{
//psFileName is always null
But if I use HttpReqeustMessage as parameter and read the form data from my request it is no problem and it works.
public HttpResponseMessage PostDownloadLogFile(HttpRequestMessage poRequest)
{
var loFormData = poRequest.Content.ReadAsFormDataAsync().Result;
string psFileName = loFormData["psFileName"]; //psFileName is set correct
Is there a solution to get the parameter with a simple parameter in my controller?
Update
This is my javascript helper function:
var loOpenWindow = function (psMethode, psUrl, poData, psTarget) {
var loForm = document.createElement("form");
loForm.action = psUrl;
loForm.method = psMethode;
loForm.target = psTarget || "_self";
if (poData) {
for (var lsKey in poData) {
var loInput = document.createElement("textarea");
loInput.name = lsKey;
loInput.value = typeof poData[lsKey] === "object" ? JSON.stringify(poData[lsKey]) : poData[lsKey];
loForm.appendChild(loInput);
}
}
loForm.style.display = "none";
document.body.appendChild(loForm);
loForm.submit();
};
Call it:
helper.openWindow("POST", apiRoutes.URLS.ApiPostDownloadLogFile, { "psFilename": $scope.data.showLogEntry.FullName });
There should be no problem from the client side code, because the controller methode with HttpReqeustMessage works without problems.
Here is the browser request:
Probably the problem is in your client code sending the data.
[FromBody] parameters must be encoded as =value
then, this does not work:
// Value will be null.
$.post('api/values', value);
// Value will be null.
$.post('api/values', { key: value });
But this work:
$.post('api/values', "=" + value);
Try to change your client code to send just =/path/to/your/file in the body.
Reference: http://encosia.com/using-jquery-to-post-frombody-parameters-to-web-api/
Ok I found a solution.
If I use a class as parameter and a property with the given name, it seems to work.
public class Param
{
public string psFileName { get; set; }
}
And
public HttpResponseMessage PostDownloadLogFile(Param poParam)
{
string psFileName = poParam.psFileName; //psFileName is set correct
This is not really a simple parameter but I can live with this solution.

Categories