0%

Web Developer Bootcamp

Web Developer Bootcamp

1. 快捷键

ctrl shift p : format document

a tab

lorem

ctrl /

inp tab

! 标准html初始化

2. HTML5

HTML is not a version, but a standard.

You can’t turn off HTML5!

  • inline elements

    • a anchor

    • span

      generic element

  • block elements

    • p paragraph

    • div division

      generic element

hr: a segment line

br: a line break

sup: superscript

sub: subscript

HTML Entities:start with ampersand&, end with semicolon;

  • greater than:
  • less than:

Semantic Markup: meaningful markup accessibility

not div, but

  • main
  • nav
  • section
  • article
  • aside
  • header
  • footer
  • time
  • figure
    • figcaption

Emmet:

  • child: >
  • sibling: +
  • multiplication: *
  • item numbering: $

table:

  • tr : table row
  • td : single data cell
  • th : single header:
    • rowspan=”2”
    • colspan=”2”
  • thead
  • tbody
  • tfoot

form:

  • empty container for data inputs

  • specify how and where the data should be sent

  • action: where the http request should be sent

  • method: “get” “post”

  • input:

    • type:
      • text
      • password
      • email: pattern
      • url: pattern
      • color: color picker
      • file
      • number:
        • min
        • max
        • step
      • time
      • range:
        • id + label for
        • name
        • min
        • max
        • value: initial value
        • step
      • radio:
        • one name for a group of radio
        • id
        • label for
        • value 绑定 name
      • checkbox:
        • name 选中后属性为on,不选中则没有该属性
        • id
        • label for 绑定 id
        • checked
      • submit:
        • value
    • placeholder
    • label + input
      • input id name
      • label for
  • button:

    • type:
      • submit (default)
      • button
  • select:

    • name
    • id
    • option:
      • value
      • selected
  • textarea:

    • id + label for
    • name
    • placeholder
    • rows
    • cols

validation:

  • browser side validation:
    • html built-in :
      • required
      • minlength
      • maxlength
      • min
      • max
      • pattern : regular expression: a pattern
      • type:
        • url
        • email
        • tel
    • javascript
  • server side validation

3. CSS

cascading style sheets

selector {

​ property: value;

}

where:

  1. inline style=”color: green” bad!
  2. head style
  3. css file + link :
    • href
    • rel=”stylesheet”

color: text color

background-color

font-size:

  • px : not recommended for responsive websites

CSS selector:

  • universal selector:

  • h1 ,h2

  • id selector:

    • #signup
  • class selector:

    • .tag
  • descendant selector: (generic descendant)

    • li a
    • span a
    • .tag a
  • adjacent selector: (immediately after)

    • h1 + p
  • direct child: (direct descendant)

    • div > li
  • attribute selector:

    • input[type=”password”]
    • section[class=”post”]
    • a[href*=”google”]
      • contains
  • pseudo class:

    • button:hover
    • .post a:hover
    • button:active
    • .post:nth-of-type(2n)
    • .post:nth-of-type(2n-1)
  • pseudo element:

    • h2::first-letter
    • p::first-line
    • p::selection
    • ::selection

“cascade”: order matters!

specificity:

  • The more specific selector wins!

  • id > class, attribute, pseudo class > element, pseudo element

  • !important

  • inherit

CSS box model

  • content box:
    • width
    • height
  • padding
  • margin
  • border

4.JS

loop:

  • for of: 遍历iterable

  • for in: object

scope:

  • var 只限定function scope, 不限定block scope( loop, conditional)
  • let 限定block scope
  • const 限定block scope

callback;

A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
const nums=[1,2,3,4,5];

//array.forEach(function)
nums.forEach(function(num){
if(num%2==1){
console.log(num);
}
});

//array.map(function)
const doubles=nums.map(function(num){
return num*2;
});

//arrow function
const double=nums.map((num)=>{
return num*2;
});

//arrow function implicit return
const double1=nums.map((num)=>{
num*2
});
const double2=nums.map(num=>num*2);

//setTimeOut(function,t)
console.log("Hello");
setTimeOut(()=>{
console.log("...Are you still there?")
},3000);
console.log("I SHOULD BE THE SECOND LINE");

//setInterval(function,t)
const id=setInterval(()=>{
console.log(Math.random());
},2000);
clearInterval(id);

//array.filter(function)
const filters=nums.filter(num=>num%2==0);

//"sum of all"
const total=nums.reduce((total,price)=>total+price)

//"product of all"
const total=nums.reduce((total,price)=>total*price)

//"minimum of all"
const min=nums.reduce((min,num)=>{
//the return value becomes the next min
//num start from nums[1] to nums[n-1]
if(min<num){
return num;
}
return min;
})

//reduce with initial value 100
const total=nums.reduce((total,num)=>total+num,100)

synchronous callback:

1
2
3
4
5
6
7
8
9
10
function greeting(name) {
alert(`Hello, ${name}`);
}

function processUserInput(callback) {
const name = prompt('Please enter your name.');
callback(name);
}

processUserInput(greeting);

asynchronous callback:

https://developer.mozilla.org/en-US/docs/Glossary/Callback_function

this:

  • “this” in regular function:

    • whoever calling it; object before “.”
    • how it is executed!
  • “this” in arrow function:

    • the scope that it was created in
    • where it is created!

spread:

  1. iterables:
  • array
  • string
  1. objects
1
2
3
4
5
6
7
Math.max(...[10,5,7])

console.log(..."Happy")

const arr1=[5,8]
const arr2=[1,6]
const arr=[...arr1,...arr2]

arguments:

like an array but not an array

rest:

an actual array

1
2
3
function(...nums){
return nums.reduce((total,el)=>total+el);
}

destructuring:

1
2
3
4
const scores=[100,99,98,67,59]
const [first,second,...everyoneElse]=scores

const {born:birthYear, deathYear:dead ='N/A'}=user //destructuring object; rename variable; with default value

5.DOM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
console.dir(document)

document.all[10].innerText="New"

//select the JavaScript object
const toc=document.getElementById("toc")
console.dir(toc)

const allImages=document.getElementsByTagName("img") //a HTML Collection, consisting of HTML Elements, not an array
console.dir(allImages[0])
for(let image of allImages){
console.log(image.src)
}
const squareImages=document.getElementsByClassName("square")

//querySelector
const firstPTag=document.querySelector('p')
const firstPId=document.querySelector('#p')
const firstPClass=document.querySelector('.p')

document.querySelector("a[title='Java']")

document.querySelectorAll("a")
document.querySelectorAll("p a")
  • innerText

  • textContent

  • innerHTML

    1
    document.querySelector("h1").innerHTML += "<sup>superscript</sup>"
  • attribute

  • style

1
2
3
4
5
6
7
8
9
10
11
12
const allLinks=document.querySelectorAll("a")

for(let link of allLinks){
link.style.color="magenta"
link.style.textDecorationColor="olive"
link.style.textDecorationStyle="wavy"
}

//
const h1=document.querySelector("h1")
window.getComputedStyle(h1).fontSize

  • classList
1
2
3
4
5
6
7
8
9
const h2=document.querySelector("h2")
h2.classList.add("border")
h2.classList.remove("purple")
h2.classList.contains("green") //false

//on and off
h2.classList.toggle("purple")
h2.classList.toggle("purple")
h2.classList.toggle("purple")
  • parentElement
  • children //HTMLCollection
  • nextElementSibling
  • previousElementSibling
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//appendChild
const newH3=document.createElement("h3")
newH3.innerText="I AM NEW"
document.body.appendCHild(newH3)

//append

//prepend

//insertAjacentElement
const h4=document.createElement("h4")
h4.append("hhhh")
newH3.insertAjacentElement("afterend",h4) //newH3.nextElementSibling

//removeChild()
img.parentElement.removeChild(img)
//remove()
img.remove()

6.DOM events

button

  • onclick
  • ondblclick
  • onmouseenter
  • onmouseleave
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const btn=document.querySelector("button")

btn.onclick=function(){
console.log("HAHAHA!!")
}

//recommended, generic way
btn.addEventListener("click",()=>{
alert("You clicked me!!!")
})

btn.addEventListener("click",shout)
btn.addEventListener("click",twist) //can coexist!

//not recommended
btn.onclick=shout
btn.onclick=twist //shout is lost, can not coexit

(keyboard) event objects

  • keydown
  • keyup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const input=document.querySelector("input")
input.addEventListener("keydown",function(e){
console.log(e.key) //q
console.log(e.code) //KeyQ
})

window.addEventListener("keydown",function(e){
console.log(e.code) //ArrowUp, ArrowLeft, ArrowRight, ArrowDown
switch(e.code){
case "ArrowUp":
console.log("UP")
break
case "ArrowDown":
console.log("DOWN")
break
default:
console.log("IGNORED!")
}
})

form event

form action: send form data to the url, and switch to that url

1
2
3
4
5
6
7
8
9
10
11
12
<form action="/url">	
<input type="text" id="catName"/>
<button>
Submit
</button>
</form>
<h2>
Available Cats
</h2>
<ul id="cats">

</ul>

to stay in the same page after submitting form:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const form=document.querySelector("form")
const input=document.querySelector("#catName") //input.value
const list=document.querySelector("#cats")

form.addEventListener("submit",function(e){
e.preventDefault();
console.log("Submitted the form!")
const name=input.value;
const newLi=document.CreateElement("li")
newLi.innerText=name
list.append(newLi) //list.appendChild(newLi)
input.value=""
})

input event

  • change event
  • input event
1
2
3
4
5
const h1=document.querySelector("h1")
const input=docuemnt.querySelector("input")
input.addEventListener("input",function(){
h1.innerText=input.value
})

event bubbling

1
2
3
4
5
6
7
8
9
10
<style>
.hide{
display: none;
}
</style>
<div id="container">
<button id="changeColor">
change color
</button>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const container=docuemnt.querySelector("#container")
const button=document.querySelector("#changeColor")
container.addEventListener("click",function(){
container.classList.toggle("hide")
})
const changeColor=()=>{
const r=Math.floor(Math.random()*255)
const g=Math.floor(Math.random()*255)
const b=Math.floor(Math.random()*255)
return `rgb(${r},${g},${b})`
}
button.addEventListener("click",(e)=>{
container.style.backgroundColor=makeRandColor()
e.stopPropagation(); //stop bubbling!
})

event delegation

1
2
3
<ul id="list">

</ul>
1
2
3
4
5
6
const container=document.querySelector("#list")
container.addEventListener("click",function(e){
//console.log("Click on ul")
//e.target.remove()
e.target.nodeName==="LI" && e.target.remove()
})

7. Asynchronous JavaScript

call stack

js is single-threaded!

promise

  • pending
  • resolved
  • rejected
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
const request=fakeRequestPromise("yelp.com/api/coffee")
request.then(()=>{
console.log("IT WORKED!!!!")
}).catch(()=>{
console.log("OH NO, ERROR!!")
})

//
fakeRequestPromise("yelp.com/api/coffee/page1").then(()=>{
console.log("IT WORKED!!!!(page1)")
fakeRequestPromise("yelp.com/api/coffee/page2").then(()=>{
console.log("IT WORKED!!!!(page2)")
}).catch(()=>{
console.log("OH NO, ERROR!!")
})
}).catch(()=>{
console.log("OH NO, ERROR!!")
})

//promise chain: dependent asynchronous functions
fakeRequestPromise("yelp.com/api/coffee/page1")
.then((data)=>{
console.log("IT WORKED!!!!(page1)")
console.log(data)
return fakeRequestPromise("yelp.com/api/coffee/page2")
})
.then((data)=>{
console.log("IT WORKED!!!!(page2)")
console.log(data)
return fakeRequestPromise("yelp.com/api/coffee/page3")
})
.then((data)=>{
console.log("IT WORKED!!!!(page3)")
console.log(data)
})
.catch((err)=>{
console.log("OH NO, A REQUEST FAILED!!")
console.log(err)
})



//create a promise
const fakeRequest=(url)=>{
return new Promise((resolve,reject)=>{
const rand=Math.random()
setTimeout(()=>{
if(rand>0.7){
resolve("Your fake data here")
}
reject("Request error")
}, 1000)
})
}

fakeRequest('/dogs/1')
.then((data)=>{
console.log("DONE WITH REQUEST")
console.log("Your data is: ",data)
})
.catch((err)=>{
console.log("OH NO, ERROR"+err)
})


//example: rainbow
const delayedColorChange=(color, delay)=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
document.body.style.backgroundColor=color
resolve()
}, delay)
})
}
delayedColorChange("red",1000)
.then(()=>delayedColorChange("orange",1000))
.then(()=>delayedColorChange("yellow",1000))
.then(()=>delayedColorChange("green",1000))
.then(()=>delayedColorChange("blue",1000))
.then(()=>delayedColorChange("indigo",1000))
.then(()=>delayedColorChange("violet",1000))

async

  • syntax sugar
  • async function always returns Promise()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const sing= async()=>{
throw "OH NO" //a rejected Promise
//after throw, becomes a rejected Promise
return "LA LA LA" //a resolved Promise
}

sing().then((data)=>{
console.log(data)
})


const login = async(username, password)=>{
if(!username || !password) throw "Missing Credentials"
if(password === "ivnwth") return "Welcome"
throw "Invalid Password"
}

login("wrong input")
.then(msg=>{

})
.catch(err=>{

})

await

only valid when inside async functions

wait for a Promise to be resolved

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function rainbow(){
await delayedColorChange("red",1000))
//without await, won't see red color because codes happen at the same time!!
//with await, first see red, then see orange, ...
await delayedColorChange("orange",1000))
await delayedColorChange("yellow",1000))
await delayedColorChange("green",1000))
await delayedColorChange("blue",1000))
await delayedColorChange("indigo",1000))
await delayedColorChange("violet",1000))
return "ALL DONE"
}
rainbow().then(()=>{
console.log("End of rainbow")
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const fakeRequest = (url0 =>{
return new Promise((resolve, reject)=>{
const delay = Math.floor(Math.random()*(4500))+500;
setTimeout(()=>{
if(delay>4000){
reject("Connection Timeout:(")
}else{
resolve(`Here is your fake data from ${url}`)
}
}, delay)
})
}

async makeTwoRequests(){
let data1=await fakeRequest("/page1")
console.log(data1)
let data2=await fakeRequest("/page2")
console.log(data2)
}
1
2
3
4
5
6
7
8
9
10
async function makeTwoRequest(){
try{
let data1=await fakeRequest("/page1")
console.log(data1)
let data2=await fakeRequest("/page2")
console.log(data2)
}catch(err){
console.log(err)
}
}

8.AJAX

  • request a web API, and get a JSON string in return, instead of HTML, CSS and JavaScript
    • JSON.parse(string)
    • JSON.stringify(object)
  • AJAX? AJAJ!
  • JSON: JavaScript Object Notation

HTTP Verbs

  • get //get data through APIs
    • a GET request cannot have req.body, only req.query
  • post //send data and store somewhere
  • put //completely update
  • patch //partly update data
  • delete

HTTP Status Codes

1:

2: success

3: redirection

4: client error

5: server error

Query string

Headers

  • request header
    • Accept:
      1. text/html
      2. application/json
      3. text/plain
  • response header

XHRs

old, unrecommended way

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const req=new XMLHttpRequest();

req.onload=function(){
console.log("IT LOADED!!");
console.log(JSON.parse(this.responseText));
}

req.onerror=function(){
console.log("ERROR!!");
console.log(this);
}

req.open("GET","https://swapi.dev/api/people/1");
req.send();

fetch

return a Promise

res.json() also returns a Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
fetch("https://swapi.dev/api/people/1")
.then((res)=>{
console.log("RESOLVED!",res);
return res.json();
})
.then((data)=>{
console.log("JSON DONE",data);
})
.catch((e)=>{
console.log("ERROR!",e);
})


//a series of request
const loadPeople= async()=>{
try{
const res1=await fetch("https://swapi.dev/api/people/1");
const data1=await res1.json();
console.log(data1);
const res2=await fetch("https://swapi.dev/api/people/2");
const data2=await res2.json();
console.log(data2);
}catch(e){
console.log("ERROR!",e);
}
};

loadPeople();

axios

axios.get() returns a data object

1
2
3
4
5
6
axios.get("https://swapi.dev/api/people/1")
.then((res)=>{
console.log("RESPONSE:",res);
}).catch((e)=>{
console.log("ERROR!",e);
})

axios.get(api, config_object)

1
2
3
4
5
6
7
8
9
const getDadJoke= async()=>{
try{
const config={header:{Accept: "application/json"}};
const res=await axios.get("https://icanhazdadjoke.com/",config);
console.log(res.data.joke);
}catch(e){
console.log(e);
}
}

9.OOP

Array.prototype 和 String.prototype 可定义自定义方法,可override默认方法

1
2
3
4
5
6
7
8
9
10
11
String.prototype.grumpus=()=>alert("GO AWAY!!");
const cat="cat";
cat.grumpus();

String.prototype.yell=()=>{
return `OMG!!!${this.toUpperCase()}!!!!!!`
};
"i love you".yell();

Array.prototype //actual template object
[3,4,5].__proto__ //reference to Array.prototype, no need to use it

syntactic sugar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Color{
constructor(r,g,b,name){
this.r=r;
this.g=g;
this.b=b;
this.name=name;
this.calcHSL();
}


greet(){
return `HELLO FROM COLOR ${this.name}`;
}

rgb(){
const {r,g,b}=this;
return `rgb(${r},${g},${b})`;
}

hex(){
const {r,g,b}=this;
return '#'+((1<<24)+(r<<16)+(g<<8)+b).toString(16).slice(1);
}

calcHSL(){
...
//adding new values to this object
this.h=h;
this.s=s;
this.l=l;
}
}



//
const c1=new Color(0,0,255,"blue");
const c2=new Color(255,255,255,"white");
c1.hex===c2.hex //true

hsl: hew([1,360]), saturation, lightness

extends

super

调用父类的constructor

10.Terminal

  • faster than GUI
  • all-access pass

terminal:

  • a text-based interface to your computer
  • originally a physical object, but now we use software terminals

shell:

  • the program running on the terminal

bash:

  • one of the most popular shells

  • ls

  • pwd

  • cd

  • ~ means home directory

  • / means root directory

  • cd .. 后退一级

  • cd /

  • cd ~

  • mkdir Frogs/TreeFrogs SugarGliders

  • touch index.html app.css app.js

  • rm index.html

  • rmdir 只能删空directory

  • rm -rf

    • can delete (non-empty) directory
    • recursive
    • force

man

  • zsh 支持 man ls
  • bash 不支持

11.NodeJS

Node REPL

Read Evaluate Print Loop

  • .exit //ctrl+d
  • no window, but global
1
node firstScript.js

process

1
process.argv

fs

  • sync: try, catch
  • async: callback function

module.exports

  1. export a single file

    • export an object that other files can require

    • in the same directory:

      1
      const math=require("./math")
  2. export a directory

    • 该directory的index.js module.exports什么,其他文件就require到什么

npm

Node Package Manager

  1. a free library
  2. a command line tool
  3. normally and by default install locally
  4. or, installed globly, and npm link

package.json

  • npm init

    npm init -y

    创建package.json

  • npm install

    根据package.json中的dependencies安装所有依赖包

12.Express

listen&use

  • libraries: You are in charge
  • framework: The framework is in charge!

inversion of control

  • app.use(callback)

    • express将html request/response变成两个JS对象,传入callback

    • req: based on the incoming HTTP request

    • res: going to generate a HTTP response to the client

      can only have one request for one response

    • res.send()

      • 可以传回html
      • 也可以传回json
  • app.listen(8080,callback)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const express=require("express");
const app=express();

//anytime we have an incoming request
app.use((req,res)=>{
console.log("WE GOT A NEW REQUEST!!");
//console.dir(req);
//res.send("HELLO, WE GOT YOUR REQUEST! THIS IS A RESPONSE!!");
res.send({ name: "ivn", message: "HELLO WORLD!" });
})

//start listening!
app.listen(8080,()=>{
console.log("LISTENING ON PORT 8080");
})

routing

app.get() only match get request

browser地址栏输入都是get request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
app.get("/", (req, res) => {
res.send("<h1>IVN<h1>");
})


app.get("/r/:sub", (req, res) => {
//console.log(req.params);
res.send(`Welcome to subpage: ${req.params.sub}`);
})

app.get("/r/:sub/:postId", (req, res) => {
const { sub, postId } = req.params;
res.send(`<h1>Viewing PostID: ${postId} on ${sub}`);
})

//必须放最后
app.get("*", (req, res) => {
res.send("I don't know that path!!")
})

query string

/search?q=cat

request.query

nodemon

automatic restart after change

13.Templating

fill in the blank to create HTML

similar to string template literal

1
2
3
4
5
6
7
8
9
const path = require("path");


app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "/views"));

app.get("/", (req, res) => {
res.render("home.ejs"); //.ejs可以省略
})

EJS syntax

  • Embedded JS
1
2
3
<h1>
Page <%=5+6-1 %>
</h1>

res.render还可以传对象

1
2
3
4
5
6
app.get("/", (req, res) => {
const num = Math.floor(Math.random() * 10) + 1;
//res.render("home",{allNum:num});
//in ejs template: allNum
res.render("home", { num });
})
  • show data
  • conditions
  • loops
1
2
3
4
5
6
7
8
<h2>Random number: <%= num %>


<%if (num%2==0){ %>
<h2>
This is an even number!
</h2>
<%} %>

express.static()

middleware

1
2
//规定路径
app.use(express.static(path.join(__dirname,'public')));

ejs partials

1
<%- include("partials/head")%>

14.Bootstrap

svg fontawesome

15.RESTful

  • get:
    • query string
    • retrieve data, not creating anything
  • post:
    • request body
    • create data
1
2
3
4
5
6
7
8
9
10
11
//built-in parsing middlewares	
//form data
app.use(express.urlencoded({extended: true}));

//json data
app.use(express.json());

app.post("/tacos",(req,res)=>{
const {meat, qty}=req.body;
res.send(`OK, here are your ${qty} ${meat} tacos!`);
})

REpresentational State Transfer

  • client-server architecture

​ Can make requests from browser or Postman. Doesn’t matter where the request is coming from.

  • statelessness

​ Every request is on its own.

  • uniform interface

    combine URLs with HTTP methods to expose full CRUD operations over HTTP

Create

  1. app.get(“/comments/new”) 一个包含form的页面,submit调用app.post()完成新建

  2. app.post(“/comments”)

  3. post完毕后,app.redirect(“/comments”)

    redirect默认为get

show/detail route

nested route

uuid

v4

uuidv4()

Update

html form only supports GET and POST

however, method-override

16.MongoDB

database

database management system

  • effiecency
  • security
  • scalability

Structured Query Language

  • predefined schema/tables

  • relational/referencing

NoSQL

  • document
  • key-value
  • graph

mongod.exe

get the server running

cmd打开后不要关!!

mongo.exe

enter the shell

http://127.0.0.1:27017/

1
2
3
4
show dbs
use loginDemo
show collections
db.users.find()

BSON

db

show collections

db.dogs.insertOne({name:”hhh”, age:3})

db.dogs.insert([{name:”iii”, age:5},{age:9}]) //single or multiple

db.dogs.find()

db.dogs.find({age:9}) //return the cursors

db.dogs.findOne({age:9}) //return the actual object

db.cats.updateOne({} ,{})

db.cats.updateMany({}, {})

db.replaceOne()

db.deleteOne({})

db.deleteMany({})

17.Mongoose

npm package

Object Data/Document Mapper

nosql

Object Relation Mapper

sql

schema

类比mybatis .xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const mongoose=require("mongoose");
mongoose.connect("mongodb://localhost:27017/moviewApp",{useNewUrlParser: true, useUnifiedTopology: true})
.then(()=>{
console.log("CONNECTION OPEN!!!")
})
.catch((err)=>{
console.log("OH NO ERROR!!!")
console.log(err)
})

//schema
const movieSchema=new mongoose.Schema({
title: String,
year:Number,
score:Number
rating:String
});
//name of model, schema
const Movie=mongoose.model("Movie", movieSchema);
//create a db collection called movies

//make a new JS object
const amadeus = new Movie({title:"Amadeus", year: 1986, score: 9.2, rating: "G"});

//save to database collection!
amadeus.save();

//insert many
Movie.insertMany([
{},
{},
{},
])

.load

node

.load index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//return an array; zero, one or many
Movie.find({year: {$lt: 1990}}).then(data=>{
console.log(data)
})

//findById
Movie.findById("")

//updateOne
//updateMany
Movie.updateMany({title: {$in:["Amadeus","Stand By Me"]}}, {score: 10}).then(res=>console.log(res))

//findOneAndUpdate
Movie.findOneAndUpdate()

//remove is deprecated
Movie.remove({})
Movie.deleteMany({})
Movie.findOneAndDelete({})

Operation Buffering

schema constraints

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
const productSchema = new mongoose.Schema({
name:{
type: String,
required: true,
maxlength: 20
},
price:{
type: Number,
required: true,
//min: 0
//constraint and error message
min:[0,"Price must be positive!"]
},
onSale:{
type: Boolean,
default: false
},
categories: [String], //attempt to cast input into String
qty: {
online:{
type: Number,
default: 0
},
inStore:{
type: Number,
default: 0
}
},
size:{
type:String,
//all valid values
enum: ["S", "M", 'L']
}
});

const Product = mongoose.model("Product", productSchema);

const bike = new Product({name: "Bike Helmet", price: 9.5}, categories: ["Cycling", "Safety", 123]);

bike.save()
.then(data=>{
console.log("IT WORKED!")
})
.catch(err=>{
console.log("ERROR!")
})

//When updating, schema constraint by default Not working
Product.findOneAndUpdate({name: "Bike Helmet"}, {price: 99}, {new:true, runValidators: true} )
.then(data=>{
console.log("IT WORKED!")
})
.catch(err=>{
console.log("ERROR!")
})

model instance method

1
2
3
4
5
6
7
8
9
10
11
productSchema.methods.toggleOnSale=function(){
this.onSale = !this.onSale;
return this.save();
}

const findProduct = async()=>{
const foundProduct = await Product.findOne({name:"hhh"});
console.log(foundProduct);
await foundProduct.toggleOnSale();
console.log(foundProduct);
}

model static method

fancy ways of CRUD

1
2
3
productSchema.statics.fireSale=function(){

}

virtuals

  • behave as if it is a virtual property

  • not in Mongo database, but only in JS

  • get

  • set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const personSchema = new mongoose.Schema({
first:String,
last:String
})

personSchema.virtual("fullName").get(function(){
return `${this.first} ${this.last}`
})

const Person = mongoose.model("Person", personSchema);

const tammy = new Person({first:"Tammy", last:"Chow"});

console.log(tammy.fullName)

middleware

do things before and after something(CRUD)

  • pre hook
  • post hook
1
2
3
4
5
6
7
8
9
personSchema.pre("save", async function(){
this.first="first"
this.last="last"
console.log("ABOUT TO SAVE!!")
})

personSchema.post("save", async function(){
console.log("JUST SAVED!!")
})

18.Express Middleware

  • Express middleware are functions that run during the req/res lifecycle.
  • Middleware can end the HTTP request by sending back a response with methods like res.send()
  • OR middleware can be chained together, one afte another by calling next()

morgan

logger

npm i morgan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const morgan=require("morgan")

//app.use whenever a request comes in
app.use((req,res)=>{
res.send("HIJACKED BY MY APP.USE()")
})

app.use(morgan("tiny")) //inject in the middle
//app.use(morgan("common"))

app.use((req,res,next)=>{
console.log("THIS IS MY FIRST MIDDLEWARE")
next()
console.log("THIS IS MY FIRST MIDDLEWARE - AFTER CALLING NEXT()")
})

app.use((req,res,next)=>{
console.log("THIS IS MY SECOND MIDDLEWARE")
next()
})

//to make sure nothing happens after next, return next
app.use((req,res,next)=>{
console.log("THIS IS MY FIRST MIDDLEWARE")
return next()
console.log("THIS IS MY FIRST MIDDLEWARE - AFTER CALLING NEXT()")
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
app.use((req,res,next)=>{
req.method="GET"
req.requestTime=Date.now()
console.log(req.path)
next()
})

app.use((req,res,next)=>{
const {password}=req.query
if(password==="chickennugget"){
next()
}
res.send("SORRY YOU NEED A PASSWORD!!!")
})

app.use("/secret",(req,res)=>{
res.status(404).send("NOT FOUND!")
})
1
2
3
4
5
6
7
8
9
10
11
12
13
const verifyPassword=(req,res,next)=>{
const {password}=req.query
if(password==="chickennugget"){
next()
}
res.send("SORRY YOU NEED A PASSWORD!!!")
}

//can have multiple callbacks
//not all callbacks will necessarily be executed
app.get("/secret",verifyPassword,(req,res)=>{
res.send("My secret: ")
})

ejs -mate

basic index

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<% layout("layouts/boilerplate") %>
<h1>All Campgrounds</h1>
<div>
<a href="/campgrounds/new">Add Campground</a>
</div>
<ul>
<% for( let campground of campgrounds ) { %>
<li>
<a href="/campgrounds/<%= campground._id %> ">
<%=campground.title %>
</a>
</li>
<% } %>
</ul>

basic new

1
2
3
4
5
6
7
8
9
10
11
12
13
<% layout("layouts/boilerplate") %>
<form action="/campgrounds" method="post">
<div>
<label for="title">Title</label>
<input type="text" id="title" name="campground[title]">
</div>
<div>
<label for="location">Location</label>
<input type="text" id="location" name="campground[location]">
</div>
<button>Add Campground</button>
</form>
<a href="/campgrounds">Back</a>

basic show

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<h1>
<%= campground.title %>
</h1>
<h2>
<%= campground.location %>
</h2>
<img src="<%= campground.image %> " alt="">
<p><%= campground.description %> </p>
<p><a href="/campgrounds/<%= campground._id %>/edit ">Edit</a></p>
<p>
<form action="/campgrounds/<%= campground._id %>?_method=DELETE" method="post">
<button>Delete Campground</button>
</form>
</p>
<footer>
<p><a href="/campgrounds">All Campgrounds</a></p>
</footer>

error-handling

  • next() calling next non-error-handling middleware
  • next(err) calling next(default) error-handling middleware
1
2
3
4
5
6
7
8
9
10
//custom error handling middleware
//if custom error handler is defined, the default one will no longer work, except we add next(err)
app.use((err,req,res,next)=>{
console.log("====================")
console.log("=======ERROR========")
console.log("====================")
console.log(err)
res.status(500).send("OH BOY, WE GOT AN ERROR!!")
next(err)
})

19. Mongo Relationships

SQL relationships tables!

  • one-to-many
    • primary key User[id]
    • foreign key Post[user_id]
  • many-to-many
    • a third table
    • Movie[id], Actor[id]
    • Role[movie_id,actor_id]

One to Few

  • example: users/accounts => addresses

  • embed the data directly in the document

  • mongo add a new _id for each document/schema

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const userSchema = new mongoose.Schema({
first: String,
last: String,
addresses:[
{
//by default, each address will also have a _id
//can be turned off
_id: {id: false},
street: String,
city: String,
state: String,
country: String
}
]
})

One to Many

  • store refenrences(object id) to document inside the parent
  • similar to SQL one-to-many
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const productSchema=new mongoose.Schema({
name: String,
price: Number,
season:{
type: String,
enum: ["Spring","Summer","Fall","Autumn"]
}
});

const Product=mongoose.model("Product",productSchema);

Product.insertMany([
{name:"Goddess Melon",price: 4.99,season:"Summer"},
{name:"Sugar Baby Melon",price: 4.99,season:"Summer"},
{name:"Asparagus",price: 3.99,season:"Spring"}
])

//const Schema=mongoose.Schema;
const {Schema}=mongoose;
//the ref option tells Mongoose which model to use during population, or the name of the model we are referencing
const farmSchema=new Schema({
name:String,
city:String,
products:[{type: Schema.Types.ObjectId, ref: "Product"}]
})

const Farm=mongoose.model("Farm",farmSchema);


const farm=new Farm({name: "Fully Belly Farms", city: "Guinda, CA"});
const melon1=Product.findOne({name: "Goddess Melon"});
const melon2=Product.findOne({name: "Asparagus"});
//not pushing the entire object, but just the id
farm.products.push(melon1);
farm.products.push(melon2);

Farm.findOne({name: "Fully Belly Farms"})
.populate("products")
.then(farm=>console.log(farm))

One to Bajillions

  • example: users => tweets

  • store reference to parent in child

  • ref: Model

  • populate: field

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const userSchema=new Schema({
username: String,
age:Number
})

const tweetSchema=new Schema({
text:String,
likes:Number,
user:{type:Schema.Type.ObjectId, ref:"User"}
})

const User=mongoose.model("User",userSchema)
const Tweet=mongoose.model("Tweet",tweetSchema)

const user = new User({name:"chickenfan99",age:63})
const tweet1=new Tweet({text:"omg I love my chicken family!", likes:0});
tweet1.user=user;
user.save();
tweet1.save();

//populate: name of the field, not name of the model
console.log(Tweet.findOne({}).populate("user"))
//only want user.username
console.log(Tweet.findOne({}).populate("user","username"))

paradox of choice: rule of thumb

  • favor embedding unless there is compelling reason not to
  • needing to access an object on its own is a compelling reason not to embed it
  • Arrays should not grow without bound.
  • Don’t be afraid of application-level joins.
  • Consider the write/read ratio when denormalizing.
    • denormal: duplicate and store some requently accessed field together with objectId
  • As always with MongoDB, how you model your data depends - entirely - on your particular application’s data access patterns.
    • query?
    • update?

deletion mongoose middleware

  • query middleware
  • document middleware
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//before "findByIdAndDelete", have no access to data deleted
farmSchema.pre("findOneAndDelete",async function(data){
console.log("PRE MIDDLEWARE")
console.log(data)
})

//after "findByIdAndDelete", have access to data deleted
farmSchema.post("findOneAndDelete",async function(farm){
//console.log("POST MIDDLEWARE")
//console.log(data)
if(farm.products.length){
const res =await Product.deleteMany({_id:{$in: farm.products}})
console.log(res)
}
})

20. express router

  • better handle nested routes

  • separate and group different routes

  • assign middlewares to specific groups

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
const express=require("express")
const router=express.Router()

//middleware
router.use((req,res,next)=>{
if(req.query.isAdmin){
next();
}
res.send("SORRY NOT AN ADMIN!")
})

//router.get("/shelters",(req,res)=>{

})

router.get("/",(req,res)=>{

})

//router.post("/shelters",(req,res)=>{

})

router.post("/",(req,res)=>{

})

router.get("/shelters/:id",(req,res)=>{

})

router.get("/:id",(req,res)=>{

})

//router.get("/shelters/:id/edit",(req,res)=>{

})

router.get("/:id/edit",(req,res)=>{

})

module.exports=router
1
2
3
4
5
6
7
8
9
10
const express=require("express")
const app=express()
const shelterRoutes=require("./routes/shelters")

//app.use("/",shelterRoutes)
app.use("/shelters",shelterRoutes)

app.listen(3000,()=>{
console.log("Serving app on localhost:3000")
})

21. http cookies

  • cookies are little bits of information that are stored in a user’s browser when browsing a particular website
  • once a cookie is set, a user’s browser will send the cookie on every subsequent request to the site
  • cookies add some “statefulness” to HTTP , between requests

send cookies

1
2
3
4
app.get("/setname",(req,res)=>{
res.cookie("name","steve chicks");
res.send("OK SENT YOU A COOKIE!!")
})

npm i cookie-parser

1
2
3
4
5
6
7
8
const cookieParser=require("cookie-parser")
app.use(cookieParser())

app.get("/greet",(req,res)=>{
//console.log(req.cookies)
const {name="No-name"} = req.cookies;
res.send(`HEY THERE, ${name}`)
})

signed?

  • analogy: old wax seal on letter, proving its validity/integrity
  • analogy: seal on peanut butter jar, if it’s broken, don’t eat it
  • is way to make sure it is not tampered with, not to make it a secret/encrypted!!

basic idea:

  1. server send signed cookie to client
  2. client send plain cookie to server on every request
  3. client able to tell whether the cookie has been tampered with

​ cookie-parser

1
2
3
4
5
6
7
8
9
10
11
12
const cookieParser=require("cookie-parser")
app.use(cookieParser("thisismysecret"))

app.get("/getsignedcookie",(req,res)=>{
res.cookie("fruit","grape",{signed: true})
res.send("sent you a signed cookie!")
})

app.get("/verifyfruit",(req,res)=>{
console.log(req.cookies) //won't contain signed cookie
res.send(req.signedCookies)
})

HMAC

Hash-based Message Authentication Code

hashing function: SHA256

22. session

  • server side data store to make requests somewhat stateful

  • example: shopping cart

    1. session contains the whole shopping carts of different id’s
    2. cookie only contains the session id, connect.sid
  • server send back the session id as cookie

  • default store: MemoryStore

    when restart the browser, everything is reset

  • impermenant data store: Redis

  1. limited size
  2. insecure

express-session

npm i express-session

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const express=require("express")
const app=express();
const session=require("express-session")

app.use(session({secret: "thisisnotagoodsecret", resave: false, saveUninitialized: false}))
//send back a signed cookie: connect.sid
//to the browser

app.get("/viewcount",(req,res)=>{
if(req.session.count){
req.session.count+=1;
}else{
req.session.count=1;
}
res.send(`YOU HAVE VIEWED THIS PAGE ${req.session.count} TIMES`)
})

app.get("/register",(req,res)=>{
const {username="Anonymous"} = req.query;
req.session.username=username;
res.redirect("/greet")
})

app.get("/greet",(req,res)=>{
const {username} = req.session;
res.send(`Welcome back, ${username}`)
})

app.listen(3000,()=>{
console.log("Serving app on localhost:3000")
})

connect-flash

  • after some action (create, delete, log in)
  • before redirection
  • depends on session

npm i connect-flash

1
2
3
const flash=require("connect-flash")

app.use(flash())

with the flash middleware in place, all requests will have a req.flash() function that can be used to flash messages

1
2
3
4
5
6
//(key, value), stored in session
//before redirect
req.flash("info","information I want to flash")

//when I want to use it, in the redirected page
res.render("index",{messages:req.flash("info")})
1
<%= messages %>

improvement using res.locals

1
2
3
4
app.use((req,res,next)=>{
res.locals.messages=req.flash("success")
next()
})

23. Authentication & Authorization

  • authentication is the process of verifying who a particular user is

    1. username/password combo
    2. security questions
    3. facial recognition
  • authorization is verifying what a specific user has access to

    “Now that we know who you are, here is what you are allowed to do and NOT allowed to do”

NEVER STORE PASSWORDS

  • Rather than storing a password in the database, wu run the password through a hashing function first and then store the result in the database

hashing function

  • functions that map input data of some arbitrary size to fixed-size output values

cryptographic hash functions

  • one-way function which is infeasible to invert

​ unable to get the input from the output

  • small change in input yields large change in the output

  • deterministic: same input yields same output

  • unlikely to find 2 outputs with same value

    collisions are rare

  • password hash functions are deliberately slow

password salts

  • an extra safeguard

  • facts:

    1. people use same passwords from one site to the next frequently
    2. people in general, use the same passwords from one person to the next
  • if hackers know the hash fuction is Bcrypt ahead of time, they can precompute, trying every common password

  • password salts:

    1. A salt is a **random value** added to the password before we hash it.
       + append
       + prepend
    
    1. It helps ensure unique hashes and mitigate common attacks.

Bcrypt

  • saltRounds: difficulty, amoun of time to hash

    recommended around 12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const bcrypt=require("bcrypt")

const hashPassword=async(pw)=>{
//const salt=await bcrypt.genSalt(10);
//const hash=await bcrypt.hash(pw,salt);
//console.log(salt)
const hash=await bcrypt.hash(pw,10)
console.log(hash)
}

const login=async(pw,hashedPassword)=>{
const result=await bcrypt.compare(pw,hashedPassword)
if(result){
console.log("LOGGED IN !!")
}else{
console.log("INCORRECT")
}
}


hashPassword("monkey")

use session to log in/out

On an incoming http request, Express middleware that supports the session checks a particular client cookie and if that particular cookie is found on the http request and is found in the global session object/database, then it adds that session’s stored info to the request object for the http request handler to later use.

So, here’s a typical sequence:

  1. Incoming HTTP request.
  2. Middleware checks for session cookie.
  3. If session cookie not there, then create one and, in the process created a unique id to identify this http client.
  4. In the persistent session store, initialize the session for this new client.
  5. If session cookie is there, then look in the session store for the session data for this client and add that data to the request object.
  6. End of session middleware processing
  7. Later on in the Express processing of this http request, it gets to a matching request handler. The session data from the session store for this particular http client is already attached to the request object and available for the request handler to use.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
app.post("/login",async(req,res)=>{
const {username,password}=req.body;
const user = await User.findOne({username})
const validPassword=await bcrypt.compare(password,user.password)
if(validPassword){
req.session.user_id=user._id
res.send("YAY WELCOME!!")
}else{
res.send("TRY AGAIN")
}
})

app.get("/secret",(req,res)=>{
if(!req.session.user_id){
res.redierct("/login")
}
res.send("This is a secret for logged in users!!")
})

app.post("logout",(req,res)=>{
//req.session.user_id=null;
req.session.destroy(); //destroy the entire session
res.redirect("/login")
})

refactor to model methods

1
2
3
4
5
6
7
8
9
10
11
userSchema.statics.findAndValidate=async function(username,password){
const foundUser=await this.findOne({username})
const isValid=await bcrypt.compare(password,foundUser.password)
return isValid ? foundUser : false
}

userSchema.pre("save",async function(next){
if(!this.isModified("password")) return next()
this.password=await bcrypt.hash(this.password,12)
next()
})

24. image upload

  • mongo document size limit: 16MB
  • GridFS
  1. set up a form that would accept images
  2. submit the form
  3. store image data in Cloudinary
  4. Cloudinary send back URLs
  5. store URLs in mongo

Cloudinary

  • like AWS
1
2
3
CLOUDINARY_CLOUD_NAME=
CLOUDINARY_KEY=
CLOUDINARY_SECRET=

enctype

  • application/x-www-form-urlencoded

    default

  • multipart/form-data

    if the form contains file inputs

Multer

  1. first upload

  2. then we can use req.body and req.file(s)

in order to parse mutlipart form

  • req.body

    other information, except for the file

  • req.file

1
2
3
4
5
6
7
8
9
10
11
12
const multer=require("multer")
const upload=multer({dest:"uploads/"})

router.route("/").post(upload.single("image"),(req,res)=>{
console.log(req.body,req.file)
res.send(req.file)
})

router.route("/").post(upload.array("image"),(req,res)=>{
console.log(req.body,req.files)
res.send(req.files)
})

dotenv

  • avoid embedding api credentials right inside applications
  • instead, store in .env files
1
2
SECRET=lololololol
API_KEY=fasdjfoiahfawe8
1
2
3
4
5
if(process.env.NODE_ENV !=="production"){
require("dotenv").config()
}

console.log(process.env.SECRET)

25. common security vulnerabilities

database injection

  • sql injection

    1
    ; DROP TABLE user
  • mongo injection

    1
    2
    3
    db.users.find({username:req.body.username})
    //
    db.users.find({username:{"$gt":""}})

Cross Site Scripting(XSS)

https://xss-game.appspot.com/

helmet

1
2
3
4
5
6
app.use(
helmet({
contentSecurityPolicy: false,
crossOriginEmbedderPolicy: false
})
);

26. heroku

heroku CLI

command line interface

1
2
git init
heroku git:remote -a <name of the heroku app>
1
2
3
4
5
6
7
heroku login

git add .
git commit -m "new message"
git push heroku master

heroku restart