Lab 02 - HTML, CSS & Flask

Objectives

  • Basic Web frontend coding (HTML + CSS)
  • Browser Development Tools (i.e., Web Inspector)
  • Backend: Basic Flask request & response concepts

Contents

Introduction

With the emergence of the Internet and its undeniable commercial importance, web development became a necessary software skill for an engineer to have.

A web site / application has two major components:

  • the frontend: the user interface, displayed with the help of a client-side browser; written in HTML + CSS, optionally employing JavaScript for better interactivity;
  • the backend: an optional server-side program used to provide additional web services to the users such as authentication, data persistence, database searching etc.

In the typical scenario, the user opens a website by using a known URL. After optionally doing the DNS resolution to obtain an IP address, the browser connects to the server using the HTTP protocol (optionally encrypted using TLS) and requests the web page using specific HTTP headers. The server software will then parse the message, identify the requested document or dynamic application, do optional processing (e.g., invoke a routine / server-side script / CGI program to generate the webpage's HTML contents) and send the results back to the client's browser for displaying (or download, in some cases).

Frontend Basics

On the client-side, HyperText Markup Language (HTML) is the de-facto standard language accepted by all browsers to describe the aspect and contents of a web page. A HTML document is built using nested elements (i.e., tags) describing the structure (layout) of the page, text / graphical content and, optionally, client-side scripts and metadata. Each HTML element may have a series of pre-defined properties (e.g., paragraph / line splitting, bigger/smaller font sizes, form input behavior etc.) which may (or may not) be altered using attributes specified between a tag's angle brackets:

<tag1 attribute1="attribute value" id="unique-name-here">
  <anothertag style="CSS properties">inside</anothertag>
  <p>paragraph <b>bold face</b></p>
</tag1>

HTML is often paired together with Cascading Style Sheets, a style definition language used to modify layout / content properties for multiple elements at once by using special pattern matching rules using selectors. The general syntax is the selector (note: there are multiple types / rules), followed by the list of style properties to apply (in { } brackets, separated by ;):

/* tag selector (matches all <tag1> elements) */
tag1 { property1: value; ... }
/* ID selector (matches <tag id="unique-name-here">) */
#unique-name-here { color: red; ... }
/* Class selectors (matches <tag class="normal-text gray-bold">) */
/* Note: an element may have multiple classes */
.normal-text { font-size: 14pt; ... }
.gray-bold { color: gray; font-weight: bold; }
/* AND-combined selectors: e.g. matches only <tag1> with class="special" */
tag1.special { ... }
/* Nested selectors (element contained in another element) */
#my-header h1 { ... }
/* or direct descentant rule: */
.nav > .nav-item { ... } 

Thus, it becomes possible to create re-usable page elements (e.g., menus, various font styles, context boxes). This has led to the emergence of many CSS frameworks (e.g., Bootstrap, Foundation) facilitating the creation of responsive (accessible to both desktop + mobile devices) designs.

Serverside: Python / Flask

On the server-side, software must be running and listening for HTTP connections, optionally do application-specific processing and serve the requested web pages or files.

There are many standalone web server programs available on the market, with open-source software being the norm (e.g., Apache httpd, nginx, lighttpd) that can readily serve static resources and can be configured to execute third party interpreters to do server-side processing (e.g., PHP).

Moreover, modern programming languages (e.g., NodeJS, Golang, Python) have built-in HTTP servers and third-party libraries that makes web development setup a breeze and well integrated with the web application's processing needs.

Today, we will introduce Flask, a web framework for the Python language. Flask uses Python decorators (e.g., @decorator) to enhance functions and register them to be executed whenever the web server receives a HTTP request:

from flask import Flask, request
 
# first, create a Flask application instance
app = Flask("my_website")
 
@app.route("/page.html")
def serve_page():
  """ Returns some basic HTML content. """
  return "<h1>hello world</h1>"

Of course, multiple URL patterns can also be captured by a single function, check the official Flask route documentation.

The routine must return a HTTP response which may either be HTML string, a rendered template, a redirection or a custom-built Response object:

from Flask import Flask, render_template, redirect, Response
@app.route("/")
def serve_template():
  return render_template("index.html", title="Hello World")
 
@app.route("/admin")
def serve_unauthorized():
  # Note: 303 is standard HTTP code for See Other redirect
  return redirect("/login.html", 303, "<h1>Redirecting, please wait...</h1>")
 
@app.route("/special.xml")
def serve_special_xml():
  return Response("<xml><author>Me</author></xml>", mimetype='text/xml')

Check Flask's Response object documentation for all available options.

Accessing HTTP request data

When Python is executing a Flask-decorated function, the request context is made available using the request member of the Flask package.

It contains all request data provided by the browser:

  • request.method: the requested HTTP method string (e.g., GET or POST);
  • request.args: a Python dict object with URL query string parameters, e.g. http://hostname/page.html?arg1=value&arg2=value;
  • request.form: HTML form data (for HTTP POST methods) as a dict object;
  • request.cookies: cookies stored by the browser (also a dict);
  • request.headers: other HTTP request headers;

Example code for printing data to the console:

from Flask import request # and many others
# ...
@app.route("/")
def my_request_handler():
  print("Method is", request.method)
  print("URL parameters:", request.args)
  # hint: access members using dict.get() method to have a default value:
  print(request.args.get("arg1", "default value"))
  if request.method == "POST":
    print("Any form data:", request.form)
  print("Cookies:", str(request.cookies))
  print("Headers:", str(request.headers))

Flask also parses many other request data formats (XML, JSON, multipart / file upload requests etc.) and provides helpers to manipulating them.

Finally, we note that the HTTP protocol is stateless: on its own, it doesn't retain anything from previous requests, e.g., the user's identity or navigation history.

Thus, it becomes the server's responsibility to use browser-assisted persistence mechanisms such as cookies to associate a HTTP request with a specific user, also called a Session. For security reasons, the server must specifically validate any data received from the user, often through cryptographic means.

Preparation

In order to solve the tasks, you will need a modern browser (duh), a code editor supporting HTML, CSS and Python (e.g., Visual Studio Code / LunarVim), a Python 3 distribution (you must also have pip installed).

Next, we will need to install the Flask Python package using the PIP package manager:

# NOTE: choose the most appropriate command:
# Option 1: install globally (requires root / admin)
python3 -m pip install flask
 
# Option 2: install for the current user only (inside ~/.local/lib/python/ on Linux)
python3 -m pip install --user flask
# (this has the advantage of not polluting the Python's system packages)
 
# there is also the virtual environment way, if you know how to do that ;)

Tasks

00. [10p] Getting Started

First, download the skeleton archive (.zip) and unzip it.

It has the following structure:

├── initial_design.html  # initial HTML template
├── public/
│   ├── bootstrap/ # bootstrap sources
│   ├── images/
│   └── style.css  # main stylesheet
├── server.py      # server-side application
└── templates/     # Jinja templates

To test, open initial_design.html in a browser. It should look similar (almost: minor text differences) to the following screenshot:

Our website uses the Bootstrap frontend library – a CSS framework for building responsive web designs! Check out its documentation for a list of features.

Also, there are other open-source CSS toolkits, google them before starting a website and choose something you like before starting a design project.

Also, it would be a good idea to test your Python / Flask setup now:

python3 server.py
# it should say that the server is running on http://127.0.0.1:5000/

Note: the initial Python Flask app doesn't display this webpage just yet, it will only give you a TODO message! You will make it yourself later this lab :P

01. [30p] Minor Design Changes

Our customer wants to make some changes to the website's design:

  • Add a header image with our logo at the top
    • Several candidates are present inside public/images/;
    • You could either use the CSS background property, or just the old-school <img> tag;
    • Hint: check out style.css for existing definitions!
  • Also change the background color of the <header> to match the chosen image on its margins (maybe something blue?);
  • Make the content box have rounded borders (try 15px);
    • border-radius *wink*
    • Use the browser's Web Developer tools to find the HTML tag to select!
  • Insert some dummy content text (e.g., use a Lorem Ipsum generator);

Hint: search for TODOs inside HTML and CSS!

For specifying URLs to other web resources (e.g., images for this task): recall Unix relative paths (./path/to/file.jpg)? HTML also uses them (those URLs will be relative to your current file – either the .html or the .css)!

In some cases (webpage has sub-paths, e.g., /account/details.html), you may also use absolute URLs (path begins with a / representing the server's root directory).

02. [20p] Hyper-linking multiple pages

Time to add a second HTML page:

  • As expected, copy + paste your initial HTML with a new name, e.g.: second.html;
  • Link together the two .html pages (in each HTML page, find the <a class=”…” href=”..”> links inside the #navbarToggle div and edit the hrefs to point to each-other);

Use relative paths, either pagename.html or ./pagename.html will do! DO NOT USE: absolute paths, e.g., C:\Users\…\pagename.html for obvious portability reasons!

  • Test by opening the HTML files in your browser. The user should be able to navigate between the two pages seamlessly!
  • One final touch: we want the menu to highlight (i.e., change foreground / background color and font style) the currently active menu button (different for each page)!

Hint: create a CSS class (e.g., .active) with your desired properties and append it to the appropiate menu link element: <a class=“nav-item nav-link” …> (also note: different class attribute values are separated by space!).

03. [30p] Firing up the Flask backend

First, let's understand how Flask serves files:

  • Download a random photo from the Internet (e.g., your favorite snake species) and place it into public/myimage.jpg (or use whatever name you wish, but keep this directory!);
  • Start up the Flask server (python3 server.py) and point your browser to http://localhost:5000/public/myimage.jpg, it should display your image.
  • Copy/rename or download another image directly to your project's root, alongside server.py; start the web server, point your browser to http://localhost:5000/myimage2.jpg – it won't work… why?

Now, we want to move the html page from earlier tasks to be served by the Flask web server:

  • Copy the initial_design.html to the templates/ directory (we're using the default Flask convention);
  • Modify server.py to call render_template (see official quickstart guide) and actually serve your HTML file.
  • Fire it up!

Don't forget to import the render_template symbol from the Flask library!

  • Finally for this task, make a /google endpoint which automatically redirects to your favorite (or not) search engine!

04. [10p] Serving dynamic content

Finally, we shall see how dynamic content can be interpolated

  • Edit the website's second HTML page, enter {{mycontent}} (this literal text!) as content somewhere inside the main content block:
    <div class="content-box main-content">
    ...
      {{mycontent}}
    ...
  • Now modify the server.py code (the second_page function) to take in the desired value as a request parameter and pass it to the mycontent template variable, e.g.:
    def second_page():
      render_template("second.html", mycontent=request.args.get("mycontent", "<not specified>"))
  • Open the endpoint URL and pass the mycontent value as a GET URL parameter (e.g., ?parameter=value) and see if the content changes dynamically after the request!
  • This is a template engine called Jinja, seamlessly integrated with Flask. More about this: next time!
ii/labs/s2/02.txt · Last modified: 2024/03/17 17:06 by florin.stancu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0