List files from a static folder in FastAPI - filesystems

I know how to serve static files in FastAPI using StaticFiles, how can I enable
directory listing like in Apache web server?
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
app = FastAPI(...)
app.mount("/samples", StaticFiles(directory='samples'), name="samples")
# GET http://localhost:8000/samples/path/to/file.jpg -> OK
# GET http://localhost:8000/samples -> not found error

I am not sure FastAPI/Starlette exposes functionality to do that out of the box.
But it shouldn't be too hard to implement something yourself. This could be a starting point:
import os
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
#app.get("/static", response_class=HTMLResponse)
def list_files(request: Request):
files = os.listdir("./static")
files_paths = sorted([f"{request.url._url}/{f}" for f in files])
print(files_paths)
return templates.TemplateResponse(
"list_files.html", {"request": request, "files": files_paths}
)
list_files.html
<html>
<head>
<title>Files</title>
</head>
<body>
<h1>Files:</h1>
<ul>
{% for file in files %}
<li>{{file}}</li>
{% endfor %}
</ul>
</body>
</html>
When you hit /static you would see:
Additional notes
Note that list_files.html is an html template and should sit in the directory templates/ as #Michel Hua pertinently mentioned in his comment. For more info on templates check the templates docs.

Related

How to read Spring Boot application.properties in ReactJs app?

We have 2 configuration files: one is in our Spring Boot application (application.properties) and another in our ReactJs app (we use .env in create-react-app). It was decided to use Spring Boot application.properties also in ReactJs app. Can anyone please guide me how can I achieve this?
I have read about "properties-reader" and tried to use it, but I don't have webpack.config.js file in our ReactJs app.
Thymeleaf provides the easiest way to pass data from application.properties file to Javascript via the template (index.html) file.
Alternatively, it can be done using normal JSP also.
Here are the working examples:
Option 1: Thymeleaf
Step 1: Define the interesting data attributes as hidden elements in the index.html file
<div id="root"></div> ---> Div to be updated by react
....
<span id="myData" hidden></span>
<!-- Script to set variables through Thymeleaf -->
<script th:inline="javascript">
var myData = "[${myData}]]";
document.getElementById("myData").innerHTML = myData;
</script>
Important note:
Make sure that the same index.html file exists in the '/public' folder of Reactjs project as well as in the /src/main/resources/templates/ folder of the spring boot project.
Step 2: Use model.addAttribute() method in Spring Boot to invoke Thymeleaf for setting data in the index.html file
#GetMapping("/")
public String index(Model model) {
// Get the value of my.data from application.properties file
#Value("${my.data}")
private String myData;
// Call Thymeleaf to set the data value in the .html file
model.addAttribute("myData", myData);
return "index";
}
Step 3: Update the ReactJS code to read the interesting attribute using document.getElementById
let myData = document.getElementById("myData").innerHTML
More information:
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#javascript-inlining
https://attacomsian.com/blog/thymeleaf-set-javascript-variable
Option 2: JSP
Step 1: Define the interesting data attributes as hidden elements in the index.jsp file
Location of index.jsp: src/main/webapp/WEB-INF/jsp/index.jsp
<!DOCTYPE html>
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html lang="en">
<head>
<!-- Head section here -->
</head>
<body>
<!-- Div to be updated by react -->
<div id="root">
</div>
<!-- Include the interesting attribute as a hidden field -->
<span id="myData" hidden>${myData}</span>
</body>
</html>
Important note:
Make sure that the /public/index.html file of reactjs project has the same content (<body>...</body>) as that of the src/main/webapp/WEB-INF/jsp/index.jsp file of spring boot project)
Step 2: Use map.put() in Spring Boot controller to update data in JSP
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class HomePageController{
// Read data from application.properties
#Value("${my.data}")
private String myData;
// Update data attributes of JSP using map.put
#GetMapping("/")
public String index( Map<String, Object> map ) {
map.put("myData", myData);
return "index";
}
}
Step 3: Update the ReactJS code to read the interesting attribute using document.getElementById
let myData = document.getElementById("myData").innerHTML
More information:
https://mkyong.com/spring-boot/spring-boot-hello-world-example-jsp/

Pass data from `index.html` to react components

I recently got started with web development. And I am stuck with sth that's probably a trivial problem. I am trying to figure out how I can pass data from my dynamically created index.html to my (typescript) react frontend (created via create-react-app).
Suppose we have a flask web server that, when the '/' resource is requested, gathers some initial user data, instantiates a page template with it and returns that page:
# flask webserver
from flask import Flask
from flask import render_template
app = Flask(__name__)
#app.route('/')
def index():
initial_user_data = {"foo":"bar",
"baz":"biz"}
return render_template('index.html', initial_data=initial_user_data)
if __name__ == '__main__':
app.run(debug=True)
For the sake of simplicity initial_user_data stores hard-coded data here. In my actual use case the dictionary gets populated with various user-specific data items that are read from files etc.
Next, let's assume index.html uses the initial_data.
<!DOCTYPE html>
<html lang="en">
<head>
...
<title>React App</title>
</head>
<body>
<script>
initial_data = {{initial_data | tojson}}
console.log(initial_data)
</script>
<div id="root"></div>
...
</body>
</html>
When we now start the webserver and open a browser to navigate to the page when can see the initial_data being logged to the browser's console output. So far, so good.
Now my problem: how can I pass initial_data to my (typescript) react components? Conceptually I want to do sth like this:
// App.tsx
import React from 'react';
const App: React.FC = () => {
// make use of 'initial_data'
const init_data = initial_data;
return (
<div ...
</div>
);
}
But yarn build will give me
Cannot find name 'initial_data'. TS2304
4 |
5 | const App: React.FC = () => {
> 6 | const init_data = initial_data;
| ^
7 | return (
8 | <div className="App">
9 | <header className="App-header">
How can I make initial_data accessible to my react components?
Edit: If this pattern of passing something from the index.html (that gets created on the backend when a clients connects) to my typescript react components is flawed then I'd also accept an answer that points me to the correct pattern in this case.
Something along the lines of (obviously just making sth up, just trying to illustrate what I mean)
Define a typescript data type that stores the user data that can be accessed from all your components
in your main react component use a life-cycle method like 'componendDidMount' to send a request to the backend to fetch the initial_data
When the response comes back store it in 1)
I'd accept an answer that adds shows some sample code for 1) 2) 3)
Many thanks for your help!
When you pass global variables inside a react component, it's always a better way to pass it using the window object.
In this case, you need to pass it as window.initial_data. This informs the linter and react that it's a global variable. As it is not defined inside the file.

Displaying Python database on Flask web server

I'm working on a project that requires that I create a create a database, import data into it, and display it on a FLASK local server. I've created the database, but I'm confused as to what I need to do in order to display its tables on the server. I have it set up to display information via HTML through render_template and believe I've established a connection via config.py, but I'm not sure where to go from here. I've read the following guides, but I don't quite understand them. If anybody could assist, I would appreciate it.
http://flask.pocoo.org/docs/0.12/patterns/sqlalchemy/
http://flask-sqlalchemy.pocoo.org/2.3/config/
Below are the relevant files in the project root.
/project
config.py
database_insert.py
server.py
/app
/templates
index.html
__init__.py
models.py
routes.py
config.py
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config(object):
SECRET_KEY = 'you-will-never-guess'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////project/app.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
init.py
from flask import Flask, request, render_template
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
from app import routes, models
models.py
from app import db
class Lot(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(64), index=True)
spots = db.relationship('Spot', backref='author', lazy='dynamic')
def __repr__(self):
return '<Lot {}>'.format(self.username)
class Spot(db.Model):
id = db.Column(db.Integer, primary_key=True)
availability = db.Column(db.String(140))
spot_num = db.Column(db.String(140))
lot_id = db.Column(db.Integer, db.ForeignKey('lot.id'))
def __repr__(self):
return '<Spot {}>'.format(self.body)
routes.py
from app import app
from flask import Flask, request, render_template
#app.route('/')
#app.route('/index')
def index():
lot_details = {
'id': 'TEST_ID', #placeholder for testing purposes
'title': 'TEST_TITLE' #placeholder for testing purposes
}
return render_template('index.html', lot=lot_details)
index.html
<!DOCTYPE html>
<html>
<head>
<title>test</title>
</head>
<body>
<p>Hello, World!</p>
<p>{{lot.id}}</p>
<p>{{lot.title}}</p>
</body>
At first, define helper method in your Spot model class.
class Spot(db.Model):
id = db.Column(db.Integer, primary_key=True)
availability = db.Column(db.String(140))
spot_num = db.Column(db.String(140))
lot_id = db.Column(db.Integer, db.ForeignKey('lot.id'))
def __repr__(self):
return '<Spot {}>'.format(self.body)
def dump(self):
return dict(id=self.id, title=self.title)
Then, in your flask route, you may query:
#app.route('/')
#app.route('/index')
def index():
lot_details = Lot.query.first().dump() # query first item
return render_template('index.html', lot=lot_details)

How to use __webpack_public_path__ variable in a Webpack configuration?

I am currently working on a web application using React, TypeScript and Webpack. I want Webpack to generate images URLs according to a subdomain that I only know on runtime and not during the compile time.
I've read this on Webpacks's documentation:
http://webpack.github.io/docs/configuration.html#output-publicpath
Note: In cases when the eventual publicPath of of output files isn’t known at compile time, it can be left blank and set dynamically at runtime in the entry point file. If you don’t know the publicPath while compiling you can omit it and set webpack_public_path on your entry point.
webpack_public_path = myRuntimePublicPath
// rest of your application entry
But I can't get it working.
I've set the webpack_public_path variable in my app entry point. But how can I use its value in my Webpack config?
I need to use it here:
"module": {
"rules": [
{
"test": /.(png|jpg|gif)(\?[\s\S]+)?$/,
"loaders": [url?limit=8192&name=/images/[hash].[ext]]
}
]
}
I need to do something like this:
"loaders": ['url?limit=8192&name=__webpack_public_path__/images/[hash].[ext]']
ANSWER
I've managed to make it work. So in my entry point file (start.tsx), I declare de __webpack_public_path__ free var before the imports and I assign its value after the imports.
/// <reference path="./definitions/definitions.d.ts" />
declare let __webpack_public_path__;
import './styles/main.scss';
/* tslint:disable:no-unused-variable */
import * as React from 'react';
/* tslint:enable:no-unused-variable */
import * as ReactDOM from 'react-dom';
import * as Redux from 'redux';
import { Root } from './components/root';
__webpack_public_path__ = `/xxxx/dist/`;
Now, the public path is being used when I have an img tag:
<img src={require('../../images/logo.png')} />
Turns into:
<img src='/xxxx/dist/images/125665qsd64134fqsf.png')} />
Here's my basic setup in terms of the generated HTML:
<script>
window.resourceBaseUrl = 'server-generated-path';
</script>
<script src="path-to-bundle" charset="utf-8"></script>
My main entry point script:
__webpack_public_path__ = window.resourceBaseUrl;
In my case, it was the order I was loading my scripts in my index.html. Ensure the last <script> tag in your index.html has a src attribute.
I discovered this by reading through the generated webpack code:
var scriptUrl;
if (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + "";
var document = __webpack_require__.g.document;
if (!scriptUrl && document) {
if (document.currentScript)
scriptUrl = document.currentScript.src
if (!scriptUrl) {
var scripts = document.getElementsByTagName("script");
if(scripts.length) scriptUrl = scripts[scripts.length - 1].src
}
}
// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration
// or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.
if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");
scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\?.*$/, "").replace(/\/[^\/]+$/, "/");
__webpack_require__.p = scriptUrl;
It just looks through your scripts and picks the last one to set as the scriptUrl which it then uses to figure out if you are using a "publicPath" or not. Since my last script did not have a src attribute, all changes to __webpack_public_path__ or webpack.config.js { ..., output: { publicPath: "" }} were being ignored.
My original index.html:
<body>
<div id="root">
${html}
</div>
<script src="/static/runtime.js" type="module"></script>
<script src="/static/polyfills.js" type="module"></script>
<script src="/static/vendor.js" type="module"></script>
<!-- the below is the offending script -->
<script>
// WARNING: See the following for security issues around embedding JSON in HTML:
// https://redux.js.org/usage/server-rendering#security-considerations
window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(
/</g,
'\\u003c'
)}
</script>
</body>
My index.html after my changes:
<body>
<div id="root">
${html}
</div>
<script>
// WARNING: See the following for security issues around embedding JSON in HTML:
// https://redux.js.org/usage/server-rendering#security-considerations
window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(
/</g,
'\\u003c'
)}
</script>
<script src="/static/runtime.js" type="module"></script>
<script src="/static/polyfills.js" type="module"></script>
<script src="/static/vendor.js" type="module"></script>
</body>
This took me several days to fix.
Not a big webpack expert, but I'm not sure you are using that loader in the right way. The url-loader is there to help you load files data that are required/imported in your code.
So if in your entry point you write something like:
var imageData = require("path/to/my/file/file.png");
Webpack will see that you are trying to import something different than a .js file and then will search in your configured loader list, to see if it can use any loader to load that resource.
Since you had set up a loader whith a test property that matches your required resource type (extension .png), it will use that configured loader (url-loader) to try loading that resource into your bundle.
You can also tell webpack what loader he needs to use by prepending the loader (and some query strings if you wish) in the require path:
var imageData = require("url-loader?mimetype=image/png!path/to/my/file/file.png");
Also, I'm not sure there is even a name parameter you can pass to the url-loader.

GAE upload image then dynamically serve the image

I'm trying to make a simple app on GAE that allows a user to enter a url to an image and a name. The app then uploads this image to the Datastore along with its name.
After the upload the page self redirects and then should send the image back to the client and display it on their machine.
After running all I get is a Server error. Since I am new to GAE please could someone tell me if my code is at least correct.
I can't see what is wrong with my code. (I have checked for correct indentation and whitespace). Below is the code:
The python:
import jinja2 # html template libary
import os
jinja_environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
import urllib
import urllib2
import webapp2
from google.appengine.ext import db
from google.appengine.api import urlfetch
class Default_tiles(db.Model):
name = db.StringProperty()
image = db.BlobProperty(default=None)
class MainPage(webapp2.RequestHandler):
def get(self):
template = jinja_environment.get_template('index.html')
self.response.out.write(template.render())
class Upload(webapp2.RequestHandler):
def post(self):
# get information from form post upload
image_url = self.request.get('image_url')
image_name = self.request.get('image_name')
# create database entry for uploaded image
default_tile = Default_tiles()
default_tile.name = image_name
default_tile.image = db.Blob(urlfetch.Fetch(image_url).content)
default_tile.put()
self.redirect('/?' + urllib.urlencode({'image_name': image_name}))
class Get_default_tile(webapp.RequestHandler):
def get(self):
name = self.request.get('image_name')
default_tile = get_default_tile(name)
self.response.headers['Content-Type'] = "image/png"
self.response.out.write(default_tile.image)
def get_default_tile(name):
result = db.GqlQuery("SELECT * FROM Default_tiles WHERE name = :1 LIMIT 1", name).fetch(1)
if (len(result) > 0):
return result[0]
else:
return None
app = webapp2.WSGIApplication([('/', MainPage),
('/upload', Upload),
('/default_tile_img', Get_default_tile)],
debug=True)
The HTML:
<html>
<head>
<link type="text/css" rel="stylesheet" href="/stylesheets/main.css" />
</head>
<body>
<form action="/upload" method="post">
<div>
<p>Name: </p>
<input name="image_name">
</div>
<div>
<p>URL: </p>
<input name="image_url">
</div>
<div><input type="submit" value="Upload Image"></div>
</form>
<img src="default_tile_img?{{ image_name }}">
</body>
</html>
Any help at all will be so much appreciated. Thanks you!
UPDATE
Thanks to Greg, I know know how to view error logs. As Greg said I was missing a comma, I have updated the code above.
The app now runs, but when I upload an image, no image shows on return. I get the following message in the log:
File "/Users/jamiefearon/Desktop/Development/My Programs/GAE Fully functional website with css, javascript and images/mywebsite.py", line 53, in get
default_tile = self.get_default_tile(name)
TypeError: get_default_tile() takes exactly 1 argument (2 given)
I only passed one argument to get_default_tile() why does it complain that I passed two?
You're missing a comma after ('/upload', Upload) in the WSGIApplication setup.
use this python code
import jinja2 # html template libary
import os
jinja_environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
import urllib
import urllib2
import webapp2
from google.appengine.ext import db
from google.appengine.api import urlfetch
class Default_tiles(db.Model):
name = db.StringProperty()
image = db.BlobProperty(default=None)
class MainPage(webapp2.RequestHandler):
def get(self):
template = jinja_environment.get_template('index.html')
self.response.out.write(template.render())
class Upload(webapp2.RequestHandler):
def post(self):
# get information from form post upload
image_url = self.request.get('image_url')
image_name = self.request.get('image_name')
# create database entry for uploaded image
default_tile = Default_tiles()
default_tile.name = image_name
default_tile.image = db.Blob(urlfetch.Fetch(image_url).content)
default_tile.put()
self.redirect('/?' + urllib.urlencode({'image_name': image_name}))
class Get_default_tile(webapp2.RequestHandler):
def get_default_tile(self, name):
result = db.GqlQuery("SELECT * FROM Default_tiles WHERE name = :1 LIMIT 1", name).fetch(1)
if (len(result) > 0):
return result[0]
else:
return None
def get(self):
name = self.request.get('image_name')
default_tile = self.get_default_tile(name)
self.response.headers['Content-Type'] = "image/png"
self.response.out.write(default_tile)
app = webapp2.WSGIApplication([('/', MainPage),
('/upload', Upload),
('/default_tile_img', Get_default_tile)],
debug=True)

Resources