I want to serve a JSON file with gin server. And set some customize values in the HTML file. Use JavaScript in it to call the JSON file.
My application structure:
.
├── main.go
└── templates
├── index.html
└── web.json
I put these basic source into main.go file:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
var router *gin.Engine
func main() {
router = gin.Default()
router.LoadHTMLGlob("templates/*")
router.GET("/web", func(c *gin.Context) {
c.HTML(
http.StatusOK,
"index.html",
gin.H{
"title": "Web",
"url": "./web.json",
},
)
})
router.Run()
}
Some code in templates/index.html file:
<!doctype html>
<html>
<head>
<title>{{ .title }}</title>
// ...
</head>
<body>
<div id="swagger-ui"></div>
// ...
<script>
window.onload = function() {
// Begin Swagger UI call region
const ui = SwaggerUIBundle({
url: "{{ .url }}",
dom_id: '#swagger-ui',
// ...
})
// End Swagger UI call region
window.ui = ui
}
</script>
</body>
</html>
When running the application, I got a fetch error:
Not Found ./web.json
So how should I serve the web.json file to be accessed in the Gin internal server?
Quoting the original gin docs: https://github.com/gin-gonic/gin#serving-static-files
func main() {
router := gin.Default()
router.Static("/assets", "./assets")
router.StaticFS("/more_static", http.Dir("my_file_system"))
router.StaticFile("/favicon.ico", "./resources/favicon.ico")
// Listen and serve on 0.0.0.0:8080
router.Run(":8080")
}
So basically you should define a route specific to your JSON file next to other routes you've defined. And then use that.
Related
That is the flow I want:
1 - Have a normal HTML file on one folder (HTML folder)
2 - Take this file, convert is to EJS file
3 - Insert the data I want on the EJS file
4 - Convert that EJS file back to HTML file, so now I will have the "same" HTML file, but now with all the data I want.
5 - Store this HTML on AWS S3 ( Or just locally, for now )
Render an EJS file into HTML markup
Source: Render EJS File with Node.js
EJS is a templating language that uses JavaScript to generate HTML. This post will illustrate how to use Node.js with TypeScript to render an EJS file into HTML markup. Please make sure you have Node.js and npm installed first. If you are unfamiliar with Typescript please read my post describing how to compile TypeScript with npm.
EJS
Begin by creating a new EJS file named index.ejs. This file will be the template used to generate index.html. If the model is passed into the template it will render the content as a paragraph.
<!-- Sample Page -->
<h1>Sample Page</h1>
<% if (model) { %>
<%= model.content %>
<% } %>
package.json
If you don't already have a package.json created you can create one by running the command npm init and following the prompts.
You will need your package.json to include these packages:
{
"name": "package-name-goes-here",
"version": "0.0.0",
"devDependencies": {
"#types/ejs": "^2.6.2",
"#types/node": "^11.9.4",
"ejs": "^2.6.1",
"typescript": "^3.3.3333"
}
}
You can also copy the devDependencies section and run the command npm install instead of installing one at a time.
Node.js
Create a new TypeScript file named render.ts. Then add the following code to import the modules that we will use.
//imports
import util = require("util");
import fs = require("fs");
import ejs = require("ejs");
//promisify
const mkdir = util.promisify(fs.mkdir);
const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);
The first import is the util module so that we can use the promisify function. Then import the fs module for file system access. Before using three of the functions from the fs module we can promisify them allowing for the use of async/await instead of nested callbacks. The last is for EJS, and since the render file function returns a promise by default we do not need to use promisify.
Below the import statements add an async function named render. This is where the HTML output will be generated and written to a file named index.html. It needs to be marked as an async function so that the keyword await can be used. Then make sure to call the function so the code that is about to be added will execute.
async function render() {
try {
} catch (error) {
console.log(error);
}
}
render();
Before rendering our EJS file we will need a folder to put the output. So add the following to our render function:
await mkdir("dist", { recursive: true });
This will create a new directory named dist where the html output will be saved. By passing the recursive property we can ensure parent folders are created even if none are necessary. After creating the dist folder we can use EJS to render the index.ejs template to HTML. The resulting HTML string is then written to a file named index.html in the dist folder.
At this point your index.ts file should look like this:
//imports
import util = require("util");
import fs = require("fs");
import ejs = require("ejs");
//promisify
const mkdir = util.promisify(fs.mkdir);
const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);
async function render() {
try {
//create output directory
await mkdir("dist", { recursive: true });
//render ejs template to html string
const html = await ejs
.renderFile("index.ejs", { model: false })
.then((output) => output);
//create file and write html
await writeFile("dist/index.html", html, "utf8");
} catch (error) {
console.log(error);
}
}
render();
In order to run this script we need to add a tsconfig.json file to configure the TypeScript compiler. This will compile the TypeScript into JavaScript so that it can be used by node.js. Add the tsconfig file to the same folder as the render.js script.
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"rootDir": "./",
"outDir": "./dist",
"sourceMap": true
},
"include": ["render.js"]
}
We also need to add a script to the package.json file created earlier. This script will compile render.ts and then run it using node. Your package.json should look like this:
{
"name": "package-name-goes-here",
"version": "0.0.0",
"scripts": {
"render": "tsc && node dist/render.js"
},
"devDependencies": {
"#types/ejs": "^2.6.2",
"#types/node": "^11.9.4",
"ejs": "^2.6.1",
"typescript": "^3.3.3333"
}
}
EJS render HTML
The render script can be run in a terminal window by typing the command npm run render. Make sure to run this command from the directory where your package.json is located. After running the render script you should now see a folder named dist containing a file named index.html.
The contents of index.html should look like this:
Sample Page
Notice that the conditional block containing the model content, in the index.ejs template, is not included in the html output. This is because in the render script the model was passed in as false. Now we'll create an object to pass in as the model with some sample content to the sample page.
In the render.ts file previously created, after the import statements, create an object and add a property to it called content with the value set to a sample of content.
const pageModel = {
content: "This is some sample content. Located on the sample page.",
};
Then pass this object in to the ejs.renderFile function instead of false. The render.ts file should look like this:
//imports
import util = require("util");
import fs = require("fs");
import ejs = require("ejs");
//promisify
const mkdir = util.promisify(fs.mkdir);
const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);
const pageModel = {
content: "<p>This is some sample content. Located on the sample page.</p>",
};
async function render() {
try {
//create output directory
await mkdir("dist", { recursive: true });
//render ejs template to html string
//pass pageModel in to render content
const html = await ejs
.renderFile("index.ejs", { model: pageModel })
.then((output) => output);
//create file and write html
await writeFile("dist/index.html", html, "utf8");
} catch (error) {
console.log(error);
}
}
render();
With the model object passed into the template we should now see the conditional block rendered in the index.html output file. Run the command npm run render once more.
The index.html file in the dist folder should now look like this:
<h1>Sample Page</h1>
<p>This is some sample content. Located on the sample page.</p>
The index.ejs template can now render dynamic HTML content according to the model object configured in the render.ts file and by running npm run render after each change to generate an updated index.html file.
I want to display a compiled angular project under a subpath (eg http://localhost:3000/app/) with go mux
It works as expected without the subpath "app/"
But when I add the subpath with http.StripPrexix("/app/, ..) Handler only index html is found and rendered but without all css, js files..
Test code
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
)
func main() {
mux := mux.NewRouter()
fs := http.FileServer(http.Dir("./static/"))
// Serve static files
mux.PathPrefix("/app/").Handler(http.StripPrefix("/app/", fs))
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>TestProject</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles.3ff695c00d717f2d2a11.css"></head>
<body>
<h1>Should work</h1>
<app-root></app-root>
<script src="runtime.359d5ee4682f20e936e9.js" defer></script><script src="polyfills.bf99d438b005d57b2b31.js" defer></script><script src="main.5dd083c1a27b2a7e410a.js" defer></script></body>
</html>
Working Project Download
This project also includes the static files to serve
https://filehorst.de/d/dubJmmsz
Goal
Serve different projects under different paths
Following on this question
What am I doing wrong?
How to serve the whole project under e.g.: localhost:3000/app/
The gorilla mux docs mention how to handle Angular SPA applications.
However since you want to base your SPA root to a sub directory, you also need to do the changes in HTML/JS. The base directory should be a variable that will be same as that of prefix of SPA. This is more of a design problem I believe
Otherwise since your rest of the files other than index are resolved at root you need to fallback to "/" for rest of the files & have both configured.
router.PathPrefix("/app").Handler(spa)
router.PathPrefix("/").Handler(spa)
So try like below. I tried your static folder with following:
package main
import (
"log"
"net/http"
"os"
"path/filepath"
"time"
"github.com/gorilla/mux"
)
type spaHandler struct {
staticPath string
indexPath string
}
func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// get the absolute path to prevent directory traversal
path, err := filepath.Abs(r.URL.Path)
if err != nil {
// if we failed to get the absolute path respond with a 400 bad request
// and stop
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// prepend the path with the path to the static directory
path = filepath.Join(h.staticPath, path)
// check whether a file exists at the given path
_, err = os.Stat(path)
if os.IsNotExist(err) {
// file does not exist, serve index.html
http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))
return
} else if err != nil {
// if we got an error (that wasn't that the file doesn't exist) stating the
// file, return a 500 internal server error and stop
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// otherwise, use http.FileServer to serve the static dir
http.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r)
}
func main() {
router := mux.NewRouter()
spa := spaHandler{staticPath: "static", indexPath: "index.html"}
router.PathPrefix("/app").Handler(spa)
srv := &http.Server{
Handler: router,
Addr: ":3000",
// Good practice: enforce timeouts for servers you create!
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}
This Application talks to MongoDB.
In Index.html , i have a button:
View
On click of "View" button , a function view() is executed ,which requests the data from mongodb and renders it to same page Index.html .
view() have ->
*customerVar.find({}, function(err, customers) {
if(!err){
res.send(customers);
}
else{
res.send('could not retrived data');
}
});*
Here res.send(customers); -> Sends it to index.html
How do i then redirect it to send to other html file . ?
I want "view.html" to display result , when i click button in "index.html".
You need to call res.render.
Assuming you've properly configured your view engine and have a similar project structure:
example
├── app.js
└── views
├── index.html
└── view.html
Then your code would be (ES2015 example with error handling omitted):
const Customer = require('./path/to/models/Customer')
exports.myHandler = async (req, res) => {
const customers = await Customer.find({}).exec()
if (!customers) {
res.render('index')
return
}
res.render('view', { customers })
}
If you had your routes configured, then you would just replace res.render with res.redirect.
I'm using gorilla serve mux to serve static html files.
r := mux.NewRouter()
r.PathPrefix("/").Handler(http.FileServer(http.Dir("./public"))).Methods("GET")
I do have a Index.html file inside the public folder as well as other html files.
When browsing the site I get all the content of the folder instead of the default Index.html.
I came from C# and I know that IIS takes Index.html as default but it is possible to select any page as a default.
I wanted to know if there's a proper way to select a default page to serve in Gorilla mux without creating a custom handler/wrapper.
Maybe using a custom http.HandlerFunc would be easier:
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Here you can check if path is empty, you can render index.html
http.ServeFile(w, r, r.URL.Path)
})
You do have to make a custom handler because you want a custom behavior. Here, I just wrapped the http.FileServer handler.
Try this one:
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
)
func main() {
handler := mux.NewRouter()
fs := http.FileServer(http.Dir("./public"))
handler.PathPrefix("/").Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
//your default page
r.URL.Path = "/my_default_page.html"
}
fs.ServeHTTP(w, r)
})).Methods("GET")
log.Fatal(http.ListenAndServe(":8080", handler))
}
So, from the code, if the visited path is the root (/) then you rewrite the r.URL.Path to your default page, in this case my_default_page.html.
After grabthefish mentioned it i decided to check the actual code of gorilla serve mux.
This code is taken from net/http package which Gorilla mux is based on.
func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string,
redirect bool) {
const indexPage = "/index.html"
// redirect .../index.html to .../
// can't use Redirect() because that would make the path absolute,
// which would be a problem running under StripPrefix
if strings.HasSuffix(r.URL.Path, indexPage) {
localRedirect(w, r, "./")
return
}
the code request the index file to be index.html in lower case so renaming my index file solved it.
thank you grabthefish!
My setup consists of a lot of .js files being used in my base.html template and the same list being repeated in my Gruntfile.js when concatenating all the files for production.
How can I have the list of files in a single JSON (or whatever other format) and be able to pass it to both Django and Grunt?
Here's a simplified example:
base.html:
<html lang="en">
<head>
<title></title>
</head>
<body>
<script src="static/some.js"></script>
<!-- a lot more js files go here -->
</body>
</html>
Gruntfile.js:
uglify: {
options:{},
build: {
options:{
report: 'min'
},
files: {
'static/some.min.js': [
'static/some.js',
// lots of other js files go here
]
}
}
I know that Grunt can load JSON files through grunt.file.readJSON('files.json') or require('files.json') but how can I do the same in Django templates?
In case anybody is interested, here's what I went for:
sample.json:
{
"files": [
"file1.js",
"file2.js"
]
}
Grunt task:
module.exports = function(grunt) {
var generalJS = prepare(grunt.file.readJSON('sample.json'));
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options:{},
build: {
options:{},
files: {
'sample.min.js': generalJS.files
}
}
}
});
grunt.registerTask('build', ['uglify']);
}
Django template tag:
#register.simple_tag
def render_js_files(file_location):
result = ""
static_base_path = settings.STATIC_DIR
json_js_files_path = os.path.join(static_base_path, file_location)
json_data = open(json_js_files_path)
data = json.load(json_data)
files_dirs = data['files']
json_data.close()
src_template = "<script src='%s'></script>\n"
for js_file_path in files_dirs:
result += src_template % urljoin(PrefixNode.handle_simple("STATIC_URL"), js_file_path)
return result
Usage in base.html:
{% render_js_files 'sample.json' %}