server端和前端的区别
nodejs和JavaScript的区别
node引入/导出语法
http模块
fs模块
path模块
url模块
querystring模块
nodejs处理http请求
express
express-router
express中间件
redis存储session
nginx反向代理
morgan日志
安全

server端和前端的区别

  • 服务的稳定性
  • 需要考虑内存和cpu的优化
    • 客户端独占一个浏览器,内存和cpu都不是问题
    • server端承载很多请求,内存和cpu都是稀缺资源
    • stream写日志是比较节省内存的(优化),redis存储session(扩展)
  • 日志记录
    • server端要记录日志,存储日志,分析日志,前端不用关心
  • 安全
    • server端要随时准备接受各种恶意攻击:越权操作,数据库攻击
    • 预防xss攻击和sql注入
  • 集群和服务拆分
    • 产品发展速度快,承载流量大

nodejs和JavaScript的区别

  • javascript

    • 使用ESMAScript语法规范,外加web API
    • web API : BOM DOM,事件绑定,ajax等
  • nodejs
    • 使用ESMAScript语法规范,外加nodejs API
    • nodejs API:处理http请求,处理文件等

node引入/导出语法

  • 引入模块 require('模块名称')

    • 带路径的: 一般我们自己封装的模块 require('../xxx/yyy'),js文件后缀名可以省略不写。 路径还可以写到文件夹的那层,但文件夹下一定要有一个index.js 系统会默认找index.js require('../xxx/文件夹名称') === > require('../xxx/文件夹名称/index.js')
    • 不带路径的: require('koa') 其实就是找 node_modules文件夹 系统node_modules
  • 导出模块语法 module.exports /exports
    • 每一个Nodejs的执行文件都会自动地创建一个module对象,同时module.exports会创建一个叫exports的属性,初始值为空对象{}exportsmodule.exports指向同一个内存,但require()返回的是module.exports而不是exports
    • module.exports 可以直接等于一个对象 但exports只能给其赋值新属性 不能直接等于一个新对象
//module.exports的形式
module.exports = {str,arr,targetObj
}
//exports的形式
exports.str = str;
exports.arr = arr;
exports.targetObj = targetObj;

http模块

nodejs的运用基础是起码要创建一个服务,在该服务的基础上处理业务操作,对于创建一个服务器最原始的莫过于是使用http模块。
http模块主要用于创建http server服务,其中封装了高效的http服务器和http客户端
http.server是一个基于事件的HTTP服务器,内部是由c++实现的,接口由JavaScript封装
http.request是一个HTTP客户端工具。用户向服务器发送数据。

var http = require('http');
var server = http.createServer((req,res)=>{//解决跨域问题res.setHeader('Access-Control-Allow-Origin',"*")res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");//设置 HTTP 头部,状态码是 200,文件类型是 html,字符集是 utf-8res.writeHead(200,{"Content-Type":"text/html;charset='utf-8'"});res.write('aaa');/*如果没有res.end(),服务器会一直等待,运转到一定的时间,页面会返回一个错误。提示服务器运行时间超长。*/res.end(); // 结束下你的请求
});
server.listen(8080)
  • var http = require('http'); 引入模块
  • http.createServer(callback) request:获取客户端发送的请求数据的信息(请求的地址,请求的方式等),response:返给客户端的相应数据
  • server.listen(post,host,callback) host可以不写 默认为localhost/127.0.0.1

fs模块

异步和同步

Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync()。
异步的方法函数最后一个参数为回调函数,回调函数的第一个参数包含了错误信息(error)。
建议大家使用异步方法,比起同步,异步方法性能更高,速度更快,而且没有阻塞。

// 属于异步操作 不会耽误其他的执行  fs.writeFile(path,data,callback);fs.readFile(path,callback);
// 同步形式也有 很少用  太慢了fs.writeFileSync();fs.readFileSync();
var fs = require('fs');
//异步获取
fs.readFile('input.txt',(err,data)=>{if(err){return console.error(err)}// data返回的是原始的二进制数据(Buffer) 浏览器是可以解析的 自己看的时候需要toString()console.log('异步读取:'+data.toString());
})
var fs = require('fs');
//异步写入
fs.writeFile('./a.txt','xxasxssx',(err)=>{if(err){console.log('写入失败',err);}else{console.log('写入成功',err);}
});
var fs = require('fs');
//同步获取
var data = fs.readFileSync('input.txt');
console.log("同步读取: " + data.toString());
var fs = require('fs');
//同步写入
var data = fs.writeFileSync('input.txt');
console.log("同步读取: " + data.toString());

fs.writeFile(path,data,callback); //写入文件 路径,要写入的数据,回调函数 (接受一个err参数 )
写入一旦成功 即使没有这个文件 也会在目录中创建这个文件 并把数据写入到文件中 回调中的 err 返回为 null
写入失败 回调中的err会返回一个错误对象 。

fs.readFile(path,callback); // 用来读文件 不需要data 路径,回调函数(接受err,data参数 data是读取到的数据)
data返回的是原始的二进制数据(Buffer) 浏览器是可以解析的 只是你自己看的时候看不懂。通常我们可以data.toString()转为字符串来查看数据。


path模块

path 模块提供用于处理文件路径和目录路径的实用工具。

const path = require('path');let str = '/root/a/b/1.txt';//path.dirname(str) 获取目录名称  /root/a/b
console.log(path.dirname(str)); //path.extname(str) 获取扩展名 .txt
console.log(path.extname(str));//path.basename(str) 获取文件名 1.txt
console.log(path.basename(str));//path.resolve(str) 对路径的解析 类似于命令行的操纵  \root\a\b\c\d\e
console.log(path.resolve('/root/a/b','c','d','e'));
// 常用的是解析当前的绝对路径
console.log(path.resolve(__dirname,'build'));

url模块

url 模块用于处理与解析 URL。

url.parse(url) 这个方法可以将一个url的字符串解析并返回一个url的对象

const url = require('url');url.parse("http://user:pass@host.com:8080/p/a/t/h?query=string#hash");
/*
返回值:
{protocol: 'http:',slashes: true,auth: 'user:pass',host: 'host.com:8080',port: '8080',hostname: 'host.com',hash: '#hash',search: '?query=string',query: 'query=string',pathname: '/p/a/t/h',path: '/p/a/t/h?query=string',href: 'http://user:pass@host.com:8080/p/a/t/h?query=string#hash'}
没有设置第二个参数为true时,query属性为一个字符串类型
*/

url.parse(url,true);

url.parse("http://user:pass@host.com:8080/p/a/t/h?query=string#hash",true);
/*
返回值:{protocol: 'http:',slashes: true,auth: 'user:pass',host: 'host.com:8080',port: '8080',hostname: 'host.com',hash: '#hash',search: '?query=string',query: { query: 'string' }, // 第二个参数 为true时 pathname: '/p/a/t/h',path: '/p/a/t/h?query=string',href: 'http://user:pass@host.com:8080/p/a/t/h?query=string#hash'}
返回的url对象中,query属性为一个对象
*/

url.format(urlObj) 这个方法是将传入的url对象编程一个url字符串并返回。

url.format({protocol:"http:",host:"182.163.0:60",port:"60"
});
/*
返回值:
'http://182.163.0:60'
*/

url.resolve(from,to) resolve这个方法返回一个格式为"from/to"的字符串

url.resolve("http://whitemu.com","gulu");
/*
返回值:
'http://whitemu.com/gulu'
*/

querystring模块

从字面上的意思就是查询字符串,一般是对http请求所带的数据进行解析。

querystring.parse(str) 将字符串解析为一个对象。
querystring.stringify(strObj) 将字符串对象解析为一个字符串。

const querystring = require('querystring');console.log(querystring.parse('a=12&b=11&c=11')); //{ a: '12', b: '11', c: '11' }
console.log(querystring.stringify({ a: '12', b: '11', c: '11' })) //a=12&b=11&c=11

querystring.escape(str) escape可使传入的字符串进行编码

querystring.escape("name=慕白");
/*
return:
'name%3D%E6%85%95%E7%99%BD'
*/

querystring.unescape(str) unescape方法可将含有%的字符串进行解码

querystring.unescape('name%3D%E6%85%95%E7%99%BD');
/*
return:
'name=慕白'
*/

nodejs处理http请求

http请求概述

  • DNS解析,建立TCP连接,发送http请求。
  • server接收到http请求,处理,并返回。
  • 客户端接收到返回数据,处理数据(如渲染页面,js)

nodejs处理http请求

  • get请求和querystring
  • post请求和postdata
  • 路由

nodejs处理get请求

  • get请求,即客户端要向server端获取数据
  • 通过querystring来传递数据,如a.html?a=100&b=200
  • 浏览器直接访问,就发送get请求。
const http = require('http');
const querystring = require('querystring');const server = http.createServer((req,res)=>{console.log('method',req.method); //getconst url = req.url;console.log('url',url);req.query = querystring.parse(url.split('?')[1]);console.log('query',req.query);res.end(JSON.stringify(req.query));
})server.listen(8080,()=>{console.log(`server is running `)
});

打印如下:

method GET
url /api/list?author=111
query [Object: null prototype] { author: '111' }
method GET
url /favicon.ico
query [Object: null prototype] {}

nodejs处理post请求

  • post请求,即客户端要向server端传递数据
  • 通过postdata来传递数据
  • 浏览器无法直接模拟,需要手写js,或者用postman
const http = require('http');const server = http.createServer((req,res)=>{if(req.method == 'POST'){console.log('req content-type',req.headers['content-type']);let postData = '';req.on('data',chunk =>{postData+=chunk.toString();})req.on('end',() =>{console.log('postData:',postData);console.log('postData:',typeof postData); //stringreq.body = JSON.parse(postData); //将post获取到的参数 放到 req.body上res.end('hello world')})}
});server.listen(8000,()=>{console.log('ok')
})

express

安装脚手架

npm i express-generator -g

创建项目

express blog_express
cd blog-express
npm i
npm start

配置package.json

npm i nodemon cross-env -D
 "scripts": {"start": "node ./bin/www", "dev":"cross-env NODE_ENV=development nodemon ./bin/www"},

介绍app.js

var createError = require('http-errors'); 对错误页面的处理
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser'); 对cookie的解析 通过req.cookies就可以直接访问cookie
var logger = require('morgan');  记录日志var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');var app = express();// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');app.use(logger('dev')); 使用logger日志
app.use(express.json()); 可以获取到post json的数据 req.body获取
app.use(express.urlencoded({ extended: false })); 获取post除了json之外格式的数据 都是绑定到req.body上
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public'))); app.use('/', indexRouter);
app.use('/users', usersRouter);// catch 404 and forward to error handler
app.use(function(req, res, next) {next(createError(404));
});// error handler
app.use(function(err, req, res, next) {// set locals, only providing error in developmentres.locals.message = err.message;res.locals.error = req.app.get('env') === 'development' ? err : {};// render the error pageres.status(err.status || 500);res.render('error');
});module.exports = app;

res相关

res.send() (*****)  // 万能方法  可以响应JSON 字符串 HTML script
res.json()  // 主要给前端响应 JSON 数据
res.jsonp() // 主要给前端响应 JSON 数据(针对跨域请求)
数据和模板在后端合并渲染 生成HTML 返回给前端
res.render('ejs模板文件', {JSON对象格式的数据})
res.download('要下载的文件的路径', '标题')
res.redirect("要跳转到的新的网址url")
res.status(状态码404).render("ejs模板名字", {JSON对象格式的数据})

req相关

接收 get 方式的请求的参数,req.query.参数的key值
接收 post 方式的请求的参数,req.body.参数的key值req.ip  获取浏览器的ip地址

express-router

//app.js
var blogRouter= require('./routes/blog.js');
app.use('/api/blog',blogRouter);

//routes---->blog.js
var express = require('express');
var router = express.Router();router.get('/list', function (req, res, next) {const { username, password } = req.query;res.json({errno: 0,data: {username,password}})
})
router.post('/detail', function (req, res, next) {const { username, password } = req.body;res.json({errno: 0,data: {username,password}})
})module.exports = router;
  • post请求的数据在req.body
  • get请求的数据在req.query

express-中间件

在 Node.js 中被广泛使用,它泛指一种特定的设计模式、一系列的处理单元、过滤器和处理程序,以函数的形式存在,连接在一起,形成一个异步队列,来完成对任何数据的预处理和后处理。

其实中间件就是请求req和响应res之间的一个应用,请求浏览器向服务器发送一个请求后,服务器直接通过request定位属性的方式得到通过request携带过去的数据,就是用户输入的数据和浏览器本身的数据信息,这中间就一定有一个函数将这些数据分类做了处理,最后让request对象调用使用,这个或者多个的处理函数就是我们所说得中间件。

原理分析

  • app.use用来注册中间件,先收集起来。
  • 遇到http请求,根据path和method判断触发那些。
  • 实现next机制,即上一个通过next触发下一个

实现

  • 创建存放所有路由的对象(all,get,post.。。。),后期对不同的请求处理都会放到这里
  • 声明对应处理的函数use(),get(),post(),register函数统一处理path信息,返回{path:'xxx',stack:['每个path对应中间件函数数组']}
  • use(),get(),post(),将自己的info信息存储到 this.routes
  • listen监听端口
  • callbackcreateServer回调,获取当前url的路由和method
  • match 返回当前url所需要执行的所有中间件集合
  • handle核心方法 实现 next()调用各个中间件
const http = require('http');
const slice = Array.prototype.slice;class LikeExpress{constructor(){// 静态属性this.routes = {all:[],get:[],post:[]}}// 原型上定义的函数//中间件公用的地方 定义在register里面register(path){const info = {};if(typeof path === 'string'){//如果第一个参数是路由的话info.path = path;// stack 以数组的形式存放路由后面的中间件info.stack = slice.call(arguments,1);}else{// 如果是app.use((req,res,next)=>{}) 没有第一个参数的形式info.path = '/';info.stack = slice.call(arguments,0);}console.log(`info is ${info}`);return info;}use(){const info = this.register.apply(this,arguments);this.routes.all.push(info);}get(){const info = this.register.apply(this,arguments);this.routes.get.push(info);}post(){const info = this.register.apply(this,arguments);this.routes.post.push(info);}// 监听事件listen(...args){const server = http.createServer(this.callback());server.listen(...args);}// 定义server的回调函数callback(){return (req,res)=>{// 定义res.json的使用res.json = (data)=>{res.setHeader('Content-type','application/json');res.end(JSON.stringify(data))}const url = req.url;const method = req.method.toLowerCase();const resultList = this.match(method,url);this.handle(req, res, resultList)}}// match 当前url所需要执行的所以中间件集合match(method,url){let stack = [];if(url === '/favicon.ico'){return stack;}// 获取routeslet curRoutes =[];// 当前路由匹配到的中间件:所有的 all 以及当前url的method对应所有中间件curRoutes = curRoutes.concat(this.routes.all);curRoutes = curRoutes.concat(this.routes[method]);curRoutes.forEach(routeInfo =>{if(url.indexOf(routeInfo.path) === 0){// url === '/api/get-cookie' 且 routeInfo.path === '/'// url === '/api/get-cookie' 且 routeInfo.path === '/api'// url === '/api/get-cookie' 且 routeInfo.path === '/api/get-cookie'// 当前路由在curRoutes 中匹配对应自己的所有中间件stack = stack.concat(routeInfo.stack)}})return stack}// 核心的 next机制handle(req,res,stack){const next = ()=>{const middleware = stack.shift();if(middleware){middleware(req, res, next);}}next();}
}module.exports = () => {return new LikeExpress()
}

调用

const express = require('./like-express2');const app = express();app.use((req,res,next)=>{console.log('请求开始....',req.method,req.url);next();
})app.use((req,res,next)=>{console.log('处理 cookie');req.cookie = {userId:'abc123'}next();
})app.use('/api',(req,res,next)=>{console.log('处理 /api 路由');next();
})app.get('/api',(req,res,next)=>{console.log('get 处理 /api 路由');next();
})// 模拟登录验证function loginCheck(req,res,next){setTimeout(() => {console.log('模拟登录成功'); next();});
}app.get('/api/get-cookie',loginCheck,(req,res,next)=>{console.log('get 处理 /api/get-cookie 路由');res.json({errno:0,data:req.cookie})
})app.listen(8000,()=>{console.log(`server listen running http://localhost:8000`);
})//访问 http://localhost:8000/api/get-cookie//请求开始.... GET /api/get-cookie
//处理 cookie
//处理 /api 路由
//get 处理 /api 路由
//模拟登录成功
//get 处理 /api/get-cookie 路由

redis存储session

为什么session适合用redis?
  • session访问频繁,对性能要求极高
  • session不用担心断电丢失数据的问题
  • session数据量不会很大(相比于mysql中存储的数据)
为什么网站数据不适合raduis
  • 操作频率不是太高(相比于session操作)
  • 断电不能丢失,必须保留
  • 数据量太大,内存成本太高

redis默认端口和地址

REDIS_CONF = {port:6379,host:'127.0.0.1'}
demo
const redis = require('redis');// 创建客户端const redisClient = redis.createClient(6379,'127.0.0.1');redisClient.on('error',err =>{console.log('err',err);})//  测试
redisClient.set('myname','zhangsan2',redis.print);
redisClient.get('myname',(err,val) => {if(err){console.log('err2',err);return;  }console.log('val is',val);// 退出redisClient.quit();
});
封装demo
const redis = require('redis');
const { REDIS_CONF } = require('../conf/db');// 创建客户端
const redisClient = redis.createClient(REDIS_CONF.port, REDIS_CONF.host);redisClient.on('error', err => {console.log('err', err);
})function set(key, val) {if (typeof val == 'object') {val = JSON.stringify(val);}redisClient.set(key, val, redis.print);
}
function get(key) {const promise = new Promise((resolve, reject) => {return redisClient.get(key, (err, val) => {if (err) {reject(err)return;}// resolve(val)if(val == null){resolve(null)}try{resolve(JSON.parse(val))}catch(ex){resolve(val)}// 退出// redisClient.quit();});})return promise
}
module.exports = {set,get}

nginx反向代理

nginx的配置

C:\nginx\conf --> nginx.conf
 server {listen       8080;server_name  localhost;#charset koi8-r;#access_log  logs/host.access.log  main;# location / { #root   html;#index  index.html index.htm;#}location /api/ {// nodejs 接口访问地址proxy_pass http://localhost:8000; proxy_set_header Host $host; }location / { //项目访问地址proxy_pass http://localhost:5500;  }

proxy_pass 设置代理地址

proxy_set_header 设置头部信息

网站输入localhost:8080—代理到localhost:5500,当访问接口/api的时候 请求的是 http://localhost:8000;上的接口.


morgan日志

' dev 开发模式  combined线上模式 'const ENV = process.env.NODE_ENV
if(ENV !== 'production'){// 开发环境app.use(logger('dev',{stream:process.stdout //默认值 }));
}else{// 线上环境const logFileName = path.join(__dirname,'logs','access.log');const writeStream = fs.createWriteStream(logFileName,{flags:'a'})app.use(logger('combined',{stream:writeStream}));
}

安全

防止sql注入

主要通过mysql.escape,对代码中特殊字符等进行转义处理。

const mysql = require('mysql');
const escape = mysql.escape;const login = (username,password)=>{username = escape(username); 防止sql注入,sql语句中不需要加单引号了password = escape(password);const sql = `select username,realname from users where username=${username} and password=${password}`return exec(sql).then((data)=>{return data[0] || {}})
}

防止xss攻击(脚本攻击)

require('xss'),对<>标签等进行转义;比如<转为&lt,这样就不会形成闭合的标签了

const xss = require('xss');const newBlog = (blogData = {}) => {const title = xss(blogData.title);;

密码加密

密码加密后即使窃取到了用户信息,密码也是加密处理的同样无法登录,我这里是通过require('crypto');来对密码进行加密处理。

const crypto = require('crypto');// 密匙const SECRET_KEY = 'wjas_1234#';// md5加密
function md5(content){let md5 = crypto.createHash('md5');return md5.update(content).digest('hex'); // digest('hex') 是转为16进制
}// 加密函数
function genPassword(password){const str = `password=${password}&key=${SECRET_KEY}`return md5(str)
}module.exports = { genPassword
} /const {genPassword} = require('../utils/cryp');const login = (username,password)=>{username = escape(username);password = genPassword(password);加密处理password = escape(password);const sql = `select username,realname from users where username=${username} and password=${password}`return exec(sql).then((data)=>{return data[0] || {}})
}

Nodejs学习---总结篇相关推荐

  1. NodeJS学习笔记: RESTful —— 为本系列做个小结

    前言 本人不是技术专家,该笔记只是从使用语言进行开发的层面上记录一些体会,不包含也不想尝试从源码或者更深的层次去讨论语言本身的优劣.文章内容是笔者的个人感悟,既不保证正确性,也不保证别人能看懂. 这是 ...

  2. Nodejs学习路线图

    Nodejs学习路线图 从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发.Nodejs框架是基于V8的引擎,是目前速度最快的Javascri ...

  3. MongoDB学习第一篇 --- Mac下使用HomeBrew安装MongoDB

    2019独角兽企业重金招聘Python工程师标准>>> MongoDB学习第一篇 --- Mac下使用HomeBrew安装MongoDB 0.确保mac已经安装了HomeBrew ( ...

  4. 深度学习实战篇-基于RNN的中文分词探索

    深度学习实战篇-基于RNN的中文分词探索 近年来,深度学习在人工智能的多个领域取得了显著成绩.微软使用的152层深度神经网络在ImageNet的比赛上斩获多项第一,同时在图像识别中超过了人类的识别水平 ...

  5. JNI学习开始篇 基础知识 数据映射及学习资料收集

    JNI学习开始篇 基础知识 数据映射及学习资料收集 JNI介绍 JNI(Java Native Interface) ,Java本地接口. 用Java去调用其他语言编写的程序,比如C或C++. JNI ...

  6. 好程序员web前端分享Nodejs学习笔记之Stream模块

    好程序员web前端分享Nodejs学习笔记之Stream模块 一,开篇分析 流是一个抽象接口,被 Node 中的很多对象所实现.比如对一个 HTTP 服务器的请求是一个流,stdout 也是一个流.流 ...

  7. java helloworld代码_java学习应用篇|逃不掉的HelloWorld

    本文知识点 1.表白不是发起进攻的冲锋号,而是吹响胜利的号角 2.除了爱情不讲道理,公理也不讲道理 3.这世界,离了javac,也是可以运行的! 4.Hello,寺水 写程序并不是写代码 看前面啰啰嗦 ...

  8. Mongodb学习(安装篇): 在centos下的安装

    安装篇 ###下载解压文件 [root@192 lamp]# wget http://fastdl.mongodb.org/linux/mongodb-linux-i686- 2.2.2.tgz ## ...

  9. [Django]模型学习记录篇--基础

    模型学习记录篇,仅仅自己学习时做的记录!!! 实现模型变更的三个步骤: 修改你的模型(在models.py文件中). 运行python manage.py makemigrations ,为这些修改创 ...

最新文章

  1. JAVA中的接口和抽象类的区别
  2. CSDN如何删除自己不用的分类(亲测有效!)
  3. mysql联合索引查找过程_(MYSQL)回表查询原理,利用联合索引实现索引覆盖
  4. linux+网卡驱动社区,Linux下如何确定网卡所使用的驱动程序
  5. Jenkins邮件配置,实现邮件发送策略(可实现每个Job对应不同的发送邮箱)
  6. [tarjan][树形dp] 洛谷 P2515 软件安装
  7. Socket编程实现简易聊天室
  8. E. Sign on Fence(整体二分 + 线段树维护区间最大连续 1 的个数)
  9. 小学生在家自学python_小学生都能学会的python(函数)
  10. 【转载】向量空间模型VSM及余弦计算
  11. 遇到了消息堆积,但是问题不大
  12. 将Maven项目发布到Nexus私服
  13. jquery中方法扩展 ($.fn $.extend) 学习笔记
  14. python设计模式2-工厂方法模式
  15. PHP错误提示的关闭方法详解
  16. kdchxue讲解V9父栏目调用子栏目的办法
  17. 牛客高级项目课(仿牛客网)笔记
  18. android 仿微信账单生成器手机版式,2020微信年度账单生成器
  19. python getsize函数,Python getsize函数
  20. 定点数一位乘法之Booth(布斯)算法

热门文章

  1. UIUC计算机的录取率,UIUC计算机专业全奖Offer申请总结
  2. 广东网络培训python
  3. 解决java.lang.IllegalArgumentException: Invalid column index (256). Allowable column range for BIFF8
  4. 多功能网页幻灯片jQuery Cycle
  5. 爬取女友淘宝已购买的宝贝数据,发现了她特殊的秘密...
  6. 统一内容安全技术厂商天空卫士完成1.5亿A轮融资
  7. Fidder Everywhere 下载和安装教程
  8. [源码和文档分享]基于VC++的MFC类库实现的住房贷款计算器
  9. 2021-第五届世界智能大会-「津门杯」国际网络安全创新大赛-Web-hate_php
  10. 我是色盲我怕谁(原创杂文)