Skip to content

Latest commit

 

History

History
1079 lines (777 loc) · 34.6 KB

README.md

File metadata and controls

1079 lines (777 loc) · 34.6 KB
title tags use languages dependences
CS50 - Week 9
programação, cs50
Documentation
Flask
CS50

This material is distribured by Harvard © 2023 edX LLC. It was copied during the execution of the Course, and have been modified due to my undersantding and integrated to the previous Data Structure of Programing Studies.

Table of Contents

Lecture 9

Keywords, lookup in transcript
  • persing
  • framework
  • Jinja, Flask, Django
  • web-server
  • route
  • layout
  • block
  • get, post
  • validation
  • redirect
  • cookie
  • session, login
  • API, interact
  • AJAX (asynchronous)
  • await fetch()
  • JSON

Lecture 9

Welcome! #

  • In previous weeks, you have learned numerous programming languages, techniques, and strategies.
  • Indeed, this class has been far less of a C class or Python class and far more of a programming class, such that you can go on to follow future trends.
  • In these past several weeks, you have learned how to learn about programming.
  • Today, we will be moving from HTML and CSS into combining HTML, CSS, SQL, Python, and JavaScript so you can create your own web applications.

Static to Dynamic

Up until this point, all HTML you saw was pre-written and static.

In the past, when you visited a page, the browser downloaded an HTML page, and you were able to view it.

Dynamic pages refer to the ability of Python and similar languages to create HTML files on-the-fly. Accordingly, you can have web pages that are generated by options selected by your user.

You have used http-server in the past to serve your web pages. Today, we are going to utilize a new server that can parse out a web address and perform actions based on the URL provided.

Flask

Flask is a third-party library that allows you to host web applications using the Flask framework within Python.

You can run flask by executing flask run. And to do so, you will need a file called app.py and a folder called templates/ as convention. static/ is to store images, CSS and JS files.

To get started, create a folder called templates and create a file called index.html with the following code:

<!DOCTYPE html>

<html lang="en">
	<head>
		<meta name="viewport" content="initial-scale=1, width=device-width">
		<title>hello</title>
	</head>
	<body>
		hello, {{ name }} <!-- using Jinja -->
	</body>
</html>

Notice the double {{ name }} that is a placeholder for something that will be later provided by our Flask server. In this case the server receives manually in the URL address the key-value pair.

Then, in the same folder that the templates folder appears, create a file called app.py. This code defines:

  • app as the Flask application.
  • the / route of app as returning the contents of index.html with the argument of name.
  • use the request.args to retrieve the arguments provided in the URL or by HTTP request.
# Greets user

from flask import Flask, render_template, request

app = Flask(__name__)


@app.route("/")
def index():
	return render_template("index.html", name=request.args.get("name", "world"))

Flask(__name__), using the dunder element that represents the file itself will define the instance of the server application. By default, the request.args.get function will look for the name being provided by the user. If no name is provided, it will default to world. If a default value isn't provided and the application can't find the key, it will return None.

In the same folder as app.py called requirements.txt that has only a single line of code:

Flask

Notice only Flask appears in this file.

You can run this file by typing flask run in the terminal window. If Flask does not run, ensure that your syntax is correct in each of the files above. Further, if Flask will not run, make sure your files are organized as follows:

/templates
	index.html
app.py
requirements.txt

Once you get it running, you will be prompted to click a link. Once you navigate to that webpage, try adding ?name=[Your Name] to the base URL in your browser’s URL bar.

Improving upon our program, we know that most users will not type arguments into the address bar. Instead, programmers rely upon users to fill out forms on web pages.

So the server will interact with two different pages: index.html and greet.html. Accordingly, we can modify index.html as follows:

<!DOCTYPE html>

<html lang="en">
	<head>
		<meta name="viewport" content="initial-scale=1, width=device-width">
		<title>hello</title>
	</head>
	<body>
		<form action="/greet" method="get">
			<input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
			<button type="submit">Greet</button>
		</form>
	</body>
</html>

The form is now created and takes the user’s input (name) and then passes it off to a route called /greet. Further, we can change app.py as follows:

# Adds a form, second route

from flask import Flask, render_template, request

app = Flask(__name__)


@app.route("/")
def index():
	return render_template("index.html")


@app.route("/greet")
def greet():
	return render_template("greet.html", name=request.args.get("name", "world"))

The default path will display a form for the user to input their name. The second @app.route(...) will change the current location and additionally receive the argument name from the first web page.

To finalize this implementation, you will need another template for greet.html as follows:

<!DOCTYPE html>

<html lang="en">
	<head>
		<meta name="viewport" content="initial-scale=1, width=device-width">
		<title>hello</title>
	</head>
	<body>
		hello, {{ name }}
	</body>
</html>

Notice that this route will now render the greeting to the user, followed by their name.

Layout

GET

Both of our web pages, index.html and greet.html, have much of the same data. Wouldn’t it be nice to allow the body to be unique, but copy the same layout from page to page?

  • First, create a new template called layout.html and write code as follows:

    <!DOCTYPE html>
    
    <html lang="en">
        <head>
            <meta name="viewport" content="initial-scale=1, width=device-width">
            <title>hello</title>
        </head>
        <body>
            {% block body %}{% endblock %}
        </body>
    </html>

This method resembles the JSX of the ReactJS.

The {% block body %}{% endblock %} allows for the insertion of other code from other HTML files. A major point is that the indentation of the (template) block will be pasted as well.

  • Then, modify your index.html as follows:

    {% extends "layout.html" %}
    
    {% block body %}
    
        <form action="/greet" method="get">
            <input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
            <button type="submit">Greet</button>
        </form>
    
    {% endblock %}

Notice that the line {% extends "layout.html" %} tells the server where to get the layout of this page. Then, the {% block body %}{% endblock %} tells what code to be inserted into layout.html.

  • Finally, change greet.html as follows:

    {% extends "layout.html" %}
    
    {% block body %}
        hello, {{ name }}
    {% endblock %}

Notice how this code is shorter and more compact.

POST

You can imagine scenarios where it is not safe to utilize get, as usernames and passwords would show up in the URL.

In order to avoid an 405 ERROR, we can utilize the method post to help with this problem by modifying app.py as follows:

# Switches to POST

from flask import Flask, render_template, request

app = Flask(__name__)


@app.route("/")
def index():
	return render_template("index.html")


@app.route("/greet", methods=["POST"])
def greet():
	return render_template("greet.html", name=request.form.get("name", "world"))

POST is added to the /greet route methods, and we use request.form rather than request.args.

This tells the server to look deeper in the virtual envelope and not reveal the items in post in the URL.

This information can still be found in Payload section of Network tab.

Still, this code can be advanced further by utilizing a single route for both get and post. To do this, modify app.py as follows:

# Uses a single route

from flask import Flask, render_template, request

app = Flask(__name__)


@app.route("/", methods=["GET", "POST"])
def index():
	if request.method == "POST":
		return render_template("greet.html", name=request.form.get("name", "world"))
	return render_template("index.html")

Notice that both get and post are done in a single routing. However, request.method is utilized to properly route based upon the type of routing requested by the user.

Frosh IMs

Frosh IMs or froshims is a web application that allows students to register for intermural sports.

Create a folder by typing mkdir froshims in the terminal window. Then, type cd froshims to browse to this folder. Within, create a directory called templates by typing mkdir templates. Finally, type code app.py and write code as follows:

# Implements a registration form using a select menu

from flask import Flask, render_template, request

app = Flask(__name__)

SPORTS = [
	"Basketball",
	"Soccer",
	"Ultimate Frisbee"
]


@app.route("/")
def index():
	return render_template("index.html", sports=SPORTS)


@app.route("/register", methods=["POST"])
def register():
	
	# Validate sport submission
	if not request.form.get("name") or request.form.get("sport") not in SPORTS:
		return render_template("failure.html")
	
	# Confirm registration
	return render_template("success.html")

Notice that a failure option is provided, such that a failure message will be displayed to the user if the name or sport field is not properly filled out.

Optionally the name value can be sent to a variable or a data structure to manipulate the information.

# ...
name = request.form.get("name")
sport = request.form.get("sport")
registers[name] = sport
# ...
  • Next, create a file in the templates folder called index.html by typing code templates/index.html and write code as follows:

    {% extends "layout.html" %}
    
    {% block body %}
        <h1>Register</h1>
        <form action="/register" method="post">
            <input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
            <select name="sport">
                <option disabled selected>Sport</option>
                {% for sport in sports %}
                    <option value="{{ sport }}">{{ sport }}</option>
                {% endfor %}
            </select>
            <button type="submit">Register</button>
        </form>
    {% endblock %}

The elements that capture the user's input (input and select) must have the name="" attribute filled in order that the request.form.get capture the respective values. Other big point is that the HTML can be manipulated, so we want do retrieve from the server all the information that the user can manipulate, this means that the <option> elements

  • Next, create a file called layout.html by typing code templates/layout.html and write code as follows:

    <!DOCTYPE html>
    
    <html lang="en">
        <head>
            <meta name="viewport" content="initial-scale=1, width=device-width">
            <title>froshims</title>
        </head>
        <body>
            {% block body %}{% endblock %}
        </body>
    </html>
  • Fourth, create a file in templates called success.html as follows:

    {% extends "layout.html" %}
    
    {% block body %}
        You are registered!
    {% endblock %}
  • Finally, create a file in templates called failure.html as follows:

    {% extends "layout.html" %}
    
    {% block body %}
        You are not registered!
    {% endblock %}
  • You can imagine how we might want to accept the registration of many different registrants. We can improve app.py as follows:

    # Implements a registration form, storing registrants in a dictionary, with error messages
    
    from flask import Flask, redirect, render_template, request
    
    app = Flask(__name__)
    
    REGISTRANTS = {}
    
    SPORTS = [
        "Basketball",
        "Soccer",
        "Ultimate Frisbee"
    ]
    
    
    @app.route("/")
    def index():
        return render_template("index.html", sports=SPORTS)
    
    
    @app.route("/register", methods=["POST"])
    def register():
    
        # Validate name
        name = request.form.get("name")
        if not name:
            return render_template("error.html", message="Missing name")
    
        # Validate sport
        sport = request.form.get("sport")
        if not sport:
            return render_template("error.html", message="Missing sport")
        if sport not in SPORTS:
            return render_template("error.html", message="Invalid sport")
    
        # Remember registrant
        REGISTRANTS[name] = sport
    
        # Confirm registration
        return redirect("/registrants")
    
    
    @app.route("/registrants")
    def registrants():
        return render_template("registrants.html", registrants=REGISTRANTS)

Notice that a dictionary called REGISTRANTS is used to log the sport selected by REGISTRANTS[name], it is capitalized due to the existence of the registrants() function, they can't have the same symbol (name). Also, notice that registrants=REGISTRANTS passes the dictionary on to this template.

  • Further, create a new template called registrants.html as follows:

    {% extends "layout.html" %}
    
    {% block body %}
        <h1>Registrants</h1>
        <table>
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Sport</th>
                </tr>
            </thead>
            <tbody>
                {% for name in registrants %}
                    <tr>
                        <td>{{ name }}</td>
                        <td>{{ registrants[name] }}</td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    {% endblock %}

Notice that {% for name in registrants %}...{% endfor %} will iterate through each of the registrants. Very powerful to be able to iterate on a dynamic web page!

Executing flask run and entering numerous names and sports, you can browse to /registrants to view what data has been logged.

You now have a web application! However, there are some security flaws! Because everything is client-side, an adversary could change the HTML and hack a website. Further, this data will not persist if the server is shut down. Could there be some way we could have our data persist even when the server restarts?

Flask and SQL

Just as we have seen how Python can interface with a SQL database, we can combine the power of Flask, Python, and SQL to create a web application where data will persist!

To implement this, you will need to take a number of steps.

  • First, modify requirements.txt as follows:

    cs50
    Flask
  • Modify index.html as follows:

    {% extends "layout.html" %}
    
    {% block body %}
        <h1>Register</h1>
        <form action="/register" method="post">
            <input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
            {% for sport in sports %}
                <input name="sport" type="radio" value="{{ sport }}"> {{ sport }}
            {% endfor %}
            <button type="submit">Register</button>
        </form>
    {% endblock %}
  • Modify layout.html as follows:

    <!DOCTYPE html>
    
    <html lang="en">
        <head>
            <meta name="viewport" content="initial-scale=1, width=device-width">
            <title>froshims</title>
        </head>
        <body>
            {% block body %}{% endblock %}
        </body>
    </html>
  • Ensure failure.html appears as follows:

    {% extends "layout.html" %}
    
    {% block body %}
        You are not registered!
    {% endblock %}
  • Modify registrants.html to appear as follows:

    {% extends "layout.html" %}
    
    {% block body %}
        <h1>Registrants</h1>
        <table>
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Sport</th>
                    <th></th>
                </tr>
            </thead>
            <tbody>
                {% for registrant in registrants %}
                    <tr>
                        <td>{{ registrant.name }}</td>
                        <td>{{ registrant.sport }}</td>
                        <td>
                            <form action="/deregister" method="post">
                                <input name="id" type="hidden" value="{{ registrant.id }}">
                                <button type="submit">Deregister</button>
                            </form>
                        </td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    {% endblock %}

Notice that a hidden value registrant.id is included such that it’s possible to use this id later in app.py, but this is an actual flaw in the code design, leading to potential threats.

  • Finally, modify app.py as follows:

    # Implements a registration form, storing registrants in a SQLite database, with support for deregistration
    
    from cs50 import SQL
    from flask import Flask, redirect, render_template, request
    
    app = Flask(__name__)
    
    db = SQL("sqlite:///froshims.db")
    
    SPORTS = [
        "Basketball",
        "Soccer",
        "Ultimate Frisbee"
    ]
    
    
    @app.route("/")
    def index():
        return render_template("index.html", sports=SPORTS)
    
    
    @app.route("/deregister", methods=["POST"])
    def deregister():
    
        # Forget registrant
        id = request.form.get("id")
        if id:
            db.execute("DELETE FROM registrants WHERE id = ?", id)
        return redirect("/registrants")
    
    
    @app.route("/register", methods=["POST"])
    def register():
    
        # Validate submission
        name = request.form.get("name")
        sport = request.form.get("sport")
        if not name or sport not in SPORTS:
            return render_template("failure.html")
    
        # Remember registrant
        db.execute("INSERT INTO registrants (name, sport) VALUES(?, ?)", name, sport)
    
        # Confirm registration
        return redirect("/registrants")
    
    
    @app.route("/registrants")
    def registrants():
        registrants = db.execute("SELECT * FROM registrants")
        return render_template("registrants.html", registrants=registrants)

The cs50 library is utilized. A route is included for register for the post method. This route will take the name and sport taken from the registration form and execute a SQL query to add the name and the sport to the registrants table. The deregister routes to a SQL query that will grab the user’s id and utilize that information to deregister this individual.

Session

While the above code is useful from an administrative standpoint, where a back-office administrator could add and remove individuals from the database, one can imagine how this code is not safe to implement on a public server.

For one, bad actors could make decisions on behalf of other users by hitting the deregister button – effectively deleting their recorded answer from the server.

Web services like Google use login credentials to ensure users only have access to the right data.

We can actually implement this itself using cookies. Cookies are small files that are stored on your computer, such that your computer can communicate with the server and effectively say, “I’m an authorized user that has already logged in.”

Using cookies to verify user sessions in a backend framework can potentially lead to various security threats. While cookies are commonly used for session management, it's important to implement them securely to mitigate potential risks. Here are some potential threats associated with using cookies for session verification:
  1. Session hijacking: If an attacker manages to steal a user's session cookie, they can impersonate that user and gain unauthorized access to their account and sensitive information.

  2. Session fixation: Attackers might try to force users to use a known session ID by tricking them into clicking on a malicious link or by other means, allowing the attacker to take control of the user's session.

  3. Cross-Site Scripting (XSS): If the application is vulnerable to XSS attacks, attackers can inject malicious scripts that steal session cookies from other users or manipulate the cookies to gain unauthorized access.

  4. Insufficient cookie attributes: Insecure configurations of cookies, such as not setting the "HttpOnly" attribute, may expose them to client-side attacks, making it easier for attackers to steal cookies using JavaScript.

  5. Weak cookie generation: If session cookies are generated with weak algorithms or have insufficient entropy, attackers may be able to predict or brute-force session IDs, leading to session compromise.

  6. Excessive session duration: If sessions have long lifetimes or are not properly invalidated after logout, attackers have a greater window of opportunity to exploit stolen session cookies.

To mitigate these threats, developers should follow best practices for secure session management:

  1. Use secure cookie attributes: Set the "HttpOnly" attribute to prevent client-side script access and the "Secure" attribute to ensure cookies are only transmitted over HTTPS connections.

  2. Employ session regeneration: Generate new session IDs upon significant events (e.g., successful login, privilege escalation) to reduce the risk of session fixation attacks.

  3. Implement session expiration: Set appropriate session timeout periods to minimize the exposure of active sessions.

  4. Use strong session ID generation: Utilize cryptographically secure random number generators to create session IDs with sufficient entropy.

  5. Validate user input and sanitize output: Implement proper input validation and output encoding to prevent XSS attacks.

  6. Monitor and log session activities: Implement logging mechanisms to detect suspicious activities and potential session-based attacks.

  7. Educate users: Raise awareness among users about the risks associated with sharing their session information and encourage them to log out from public or shared devices.

In the simplest form, we can implement this by creating a folder called login and then adding the files following this steps:

  1. create a file called requirements.txt that reads as follows:

    Flask
    Flask-Session

Notice that in addition to Flask, we also include Flask-Session, which is required to support login sessions.

  1. in a templates folder, create a file called layout.html that appears as follows:

    <!DOCTYPE html>
    
    <html lang="en">
        <head>
            <meta name="viewport" content="initial-scale=1, width=device-width">
            <title>store</title>
        </head>
        <body>
            {% block body %}{% endblock %}
        </body>
    </html>

Notice this provides a very simple layout with a title and a body.

  1. create a file in the templates folder called index.html that appears as follows:

    {% extends "layout.html" %}
    
    {% block body %}
    
        {% if session["name"] %}
            You are logged in as {{ session["name"] }}. <a href="/logout">Log out</a>.
        {% else %}
            You are not logged in. <a href="/login">Log in</a>.
        {% endif %}
    
    {% endblock %}

This file looks to see if session["name"] exists. If it does, it will display a welcome message. If not, it will recommend you browse to a page to log in.

  1. create a file called login.html and add the following code, this is the layout of a basic login page:

    {% extends "layout.html" %}
    
    {% block body %}
    
        <form action="/login" method="post">
            <input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
            <button type="submit">Log In</button>
        </form>
    
    {% endblock %}
  2. create a file in the login folder called app.py and write code as follows:

    from flask import Flask, redirect, render_template, request, session
    from flask_session import Session
    
    # Configure app
    app = Flask(__name__)
    
    # Configure session
    app.config["SESSION_PERMANENT"] = False
    app.config["SESSION_TYPE"] = "filesystem"
    Session(app)
    
    
    @app.route("/")
    def index():
        if not session.get("name"):
            return redirect("/login")
        return render_template("index.html")
    
    
    @app.route("/login", methods=["GET", "POST"])
    def login():
        if request.method == "POST":
            session["name"] = request.form.get("name")
            return redirect("/")
        return render_template("login.html")
    
    
    @app.route("/logout")
    def logout():
        session["name"] = None
        return redirect("/")

The modified imports at the top of the file, including session, which will allow for you to support sessions. Most important, notice how session["name"] is used in the login and logout routes. The login route will assign the login name provided and assign it to session["name"]. However, in the logout route, the logging out is implemented by simply setting session["name"] to None.

Store

Moving on to a final example of utilizing Flask’s ability to enable a session.

We examined the following code for store in app.py. The following code was shown:

from cs50 import SQL
from flask import Flask, redirect, render_template, request, session
from flask_session import Session

# Configure app
app = Flask(__name__)

# Connect to database
db = SQL("sqlite:///store.db")

# Configure session
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)


@app.route("/")
def index():
	books = db.execute("SELECT * FROM books")
	return render_template("books.html", books=books)


@app.route("/cart", methods=["GET", "POST"])
def cart():

	# Ensure cart exists
	if "cart" not in session:
		session["cart"] = []

	# POST
	if request.method == "POST":
		id = request.form.get("id")
		if id:
			session["cart"].append(id)
		return redirect("/cart")

	# GET
	books = db.execute("SELECT * FROM books WHERE id IN (?)", session["cart"])
	return render_template("cart.html", books=books)

Notice that cart is implemented using a list. Items can be added to this list using the Add to Cart buttons in books.html. When clicking such a button, the post method is invoked, where the id of the item is appended to the cart. When viewing the cart, invoking the get method, SQL is executed to display a list of the books in the cart.

API

An application program interface or API is a series of specifications that allow you to interface with another service. For example, we could utilize IMDB’s API to interface with their database. We might even integrate APIs for handling specific types of data downloadable from a server. We looked at an example called shows.

Looking at app.py, we saw the following:

# Searches for shows using Ajax

from cs50 import SQL
from flask import Flask, render_template, request

app = Flask(__name__)

db = SQL("sqlite:///shows.db")


@app.route("/")
def index():
	return render_template("index.html")


@app.route("/search")
def search():
	q = request.args.get("q")
	if q:
		shows = db.execute("SELECT * FROM shows WHERE title LIKE ? LIMIT 50", "%" + q + "%")
	else:
		shows = []
	return render_template("search.html", shows=shows)

Notice that the search route executes a SQL query.

Looking at search.html, you’ll notice that it is very simple, it provides a bulleted list:

{% for show in shows %}
	<li>{{ show["title"] }}</li>
{% endfor %}

Finally, looking at index.html, notice that AJAX code is utilized to power the search:

<!DOCTYPE html>

<html lang="en">
	<head>
		<meta name="viewport" content="initial-scale=1, width=device-width">
		<title>shows</title>
	</head>
	<body>

		<input autocomplete="off" autofocus placeholder="Query" type="search">

		<ul></ul>

		<script>

			let input = document.querySelector('input');
			input.addEventListener('input', async function() {
				let response = await fetch('/search?q=' + input.value);
				let shows = await response.text();
				document.querySelector('ul').innerHTML = shows;
			});

		</script>

	</body>
</html>

An event listener is utilized to dynamically query the server to provide a list that matches the title provided. This will locate the ul tag in the HTML and modify the web page accordingly to include the list of the matches.

JSON

JavaScript Object Notation or JSON is text file of dictionaries with keys and values. This is a raw, computer-friendly way to get lots of data. And is a very useful way of getting back data from the server. You can see this in action in the index.html we examined together:

<!DOCTYPE html>

<html lang="en">
	<head>
		<meta name="viewport" content="initial-scale=1, width=device-width">
		<title>shows</title>
	</head>
	<body>

		<input autocomplete="off" autofocus placeholder="Query" type="text">

		<ul></ul>

		<script>

			let input = document.querySelector('input');
			input.addEventListener('input', async function() {
				let response = await fetch('/search?q=' + input.value);
				let shows = await response.json();
				let html = '';
				for (let id in shows) {
					let title = shows[id].title.replace('<', '&lt;').replace('&', '&amp;');
					html += '<li>' + title + '</li>';
				}
				document.querySelector('ul').innerHTML = html;
			});

		</script>

	</body>
</html>

While the above may be somewhat cryptic, it provides a starting point for you to research JSON on your own to see how it can be implemented in your own web applications.

Summing Up

In this lesson, you learned how to utilize Python, SQL, and Flask to create web applications. Specifically, we discussed…

  • GET
  • POST
  • Flask
  • Session
  • AJAX
  • JSON

See you next time for our final lecture!


Section

Transcript


Shorts

Flask

Transc

Simple and lightweight framework that interacts with the HMTL and the Database doing the "job" of backend.

  • Initialization
  • Routes (decorators)
  • Running the server
    export FLASK_APP=app.py 
    export FLASK_DEBUG=1
    flask run
  • HTTP request (GET, POST)
  • Session
  • Jinja

AJAX

Transc

Ajax (formerly Asynchronous JavaScript and XML) allows us to dynamically update a webpage even more dynamically.

  • XMLHttpRequest()

After obtaining your new object, you need to define its onreadystatechange behaviour.

This is a function (typically an anonymous function) that will be called when the asynchronous HTTP request has completed, and thus typically defines what is expected to change on your site.

function ajax_request(arg) {
	var aj = new XMLHttpRequest();
	aj.onreadystatechange = function () {
		if (aj.readyState == 4 && aj.status == 200)
			// do ...
	};
	aj.open("GET", /* url */, true); // http request
	aj.send();
}

This could be done using JQuery to get and send the request and for DOM manipulation.

This opens the possibility to update certain elements/portions without refreshing the entire page.W


Exercises

Practice Problems

In addition to this week’s lab and problem set, you’re welcome to try any of these (optional!) practice problems:

  • Hello, Flask #, for practice writing a Flask application from scratch.

HTML code here Python code here

Problem Set 9

Collaboration on problem sets is not permitted except to the extent that you may ask classmates and others for help so long as that help does not reduce to another doing your work for you, per the course’s policy on academic honesty.

The staff conducts random audits of submissions to CS50x. Students found to be in violation of this policy will be removed from the course. Students who have already completed CS50x, if found to be in violation, will have their CS50 Certificate permanently revoked.

What to Do

Be sure you have completed Lab 9 before beginning this problem set.

  1. Log into code.cs50.io using your GitHub account
  2. Run update50 in your codespace’s terminal window to ensure your codespace is up-to-date and, when prompted, click Rebuild now
  3. Submit Finance #