Search code examples
pythonhtmlformsfastapi

How to send a FastAPI response without redirecting the user to another page?


I am creating an API using FastAPI, which receives form-data from an HTML page, process the data (requiring a few moments) and returns a message saying this task is complete.

This is my backend:

from cgi import test
from fastapi import FastAPI, Form, Request
from starlette.responses import FileResponse

app = FastAPI()

@app.post("/")
async def swinir_dict_creation(request: Request,taskname: str = Form(...),tasknumber: int = Form(...)):

    args_to_test = {"taskname":taskname, "tasknumber":tasknumber} # dict creation
    print('\n',args_to_test,'\n')
    # my_function_does_some_data_treatment.main(args_to_test)
    # return 'Treating...'
    return 'Super resolution completed! task '+str(args_to_test["tasknumber"])+' of '+args_to_test["taskname"]+' done'

@app.get("/")
async def read_index():
    return FileResponse("index.html")

This is my frontend code:

<html>
   <head>
      <h1><b>Super resolution image treatment</b></h1>   
      <body>
        <form action="http://127.0.0.1:8000/" method="post" enctype="multipart/form-data">

            <label for="taskname" style="font-size: 20px">Task name*:</label>
            <input type="text" name="taskname" id="taskname" />
    
            <label for="tasknumber" style="font-size: 20px">Task number*:</label>
            <input type="number" name="tasknumber" id="tasknumber" />

            <b><p style="display:inline"> * Cannot be null</p></b>
            <button type="submit" value="Submit">Start</button>
         </form>
      </body>
   </head>
</html>

So the frontend page looks like this:

enter image description here

When the processing is finished in the backend, after the user submitted some data, the return statement from FastAPI backend simply redirects the user to a new page showing only the return message. I was looking for a alternative that would keep the HTML form appearing and display the message returned from the server below this form. For example:

enter image description here

I searched in FastAPI documentation about requests, but I haven't found anything that could avoid modifying my original HTML page.


Solution

  • You would need to use a JavaScript interface/library, such as Fetch API, in order to make an asynchronous HTTP request. Also, you should use Templates to render and return a TemplateResponse, instead of FileResponse, as shown in your code.

    Related answers can also be found here and here, which show how to handle <form> submission on the submit event, and prevent the default action that causes the page to reload.

    If you would like to post JSON data instead, please have a look at this answer. For posting both Files and Form/JSON data, take a look at this answer.

    Working Example:

    app.py

    from fastapi import FastAPI, Form, Request
    from fastapi.templating import Jinja2Templates
    
    app = FastAPI()
    templates = Jinja2Templates(directory="templates")
    
    @app.post("/submit")
    async def submit(request: Request, taskname: str = Form(...), tasknumber: int = Form(...)):
        return f'Super resolution completed! task {tasknumber} of {taskname} done'
    
    @app.get("/")
    async def index(request: Request):
        return templates.TemplateResponse("index.html", {"request": request})
    

    templates/index.html

    <!DOCTYPE html>
    <html>
       <body>
          <h1>Super resolution image treatment</h1>
          <form method="post" id="myForm">
             <label for="taskname" style="font-size: 20px">Task name*:</label><br>
             <input type="text" name="taskname" id="taskname"><br>
             <label for="tasknumber" style="font-size: 20px">Task number*:</label><br>
             <input type="number" name="tasknumber" id="tasknumber">
             <p style="display:inline"><b>* Cannot be null</b></p><br><br>
             <input type="button" value="Start" onclick="submitForm()">
          </form>
          <div id="responseArea"></div>
          <script>
             function submitForm() {
                 var formElement = document.getElementById('myForm');
                 var data = new FormData(formElement);
                 fetch('/submit', {
                       method: 'POST',
                       body: data,
                     })
                     .then(resp => resp.text())  // or, resp.json(), etc.
                     .then(data => {
                       document.getElementById("responseArea").innerHTML = data;
                     })
                     .catch(error => {
                       console.error(error);
                     });
             }
          </script>
       </body>
    </html>