node之HTTP 从服务端感受ajax和form

611 阅读4分钟

ajax发送请求,需要传输数据,数据有不同的类型,所以也是靠请求头告诉后台处理逻辑

传输数据的方式有很多种

http: 1.0,1.1(keep-alive) 复用连接, 2.0 https 新特点双向通信,多路复用

数据交互的方式

客户端主动发送请求给服务端,服务端返回数据,我们叫单向通信

  • 表单 (会页面跳转,如果是get会加个?,如果是post就会跳转业面,页面重新渲染,重新请求服务器,获取页面,这样就会浪费很多流量,对服务器压力很大) 但是一般提交我们用表单 支持跨域
  • ajax 同步体验不好,可以无刷新页面 1.0,2.0 ajax基于表单请求http发过去的,只是发送方式不一样 不支持跨域
  • jsop xss攻击,逐渐接口暴露出去,大家都可以访问,大多数网站在废弃中, 逐渐被cors代替
  • cors 跨域资源共享,不需要前台,直接更改服务端配置
  • nginx 反向代理
  • webpack proxy 反向代理

双向通信 连接以后,不需要客户端主动发送请求,服务端可以主动告诉他,会实时吧最新的信息提交给客户端,之前我们的做法是setInterval,但是这就有个问题,假设我们10分钟内没有更新呢,而用websokct当服务端变了之后会主动推送给客户端,我们经常应用例如聊天室

  • websocket (基于tcp)

我们通过node实现服务端,接收前端请求

表单 (会页面跳转,如果是get会加个?,如果是post就会跳转业面,页面重新渲染,重新请求服务器,获取页面,这样就会浪费很多流量,对服务器压力很大) 但是一般提交我们用表单

get post
放到url里,有限制 不限制大小

get的url形式方便分享 post通过请求体

<!-- form支持什么格式提交get post 不识别默认get -->
    <form  action="http://localhost:3000/form" method="get">
        用户名 <input type="text" name="username"><br>
        密码 <input type="text" name="password"><br>
        <input type="submit">
    </form>

跳转之后的链接会有http://127.0.0.1:5500/?username=123&password=321form默认支持跨域

//服务端
let http = require('http');
let url = require('url');
let server = http.createServer(function(req,res){
    let {pathname,query} = url.parse(req.url,true);
    if(pathname === '/form'){
        res.end(JSON.stringify(query))
    }
})
server.listen(3000)

把方法改成post

let http = require('http');
let url = require('url');
let querystring = require('querystring');

// let str = "username==123&&password==321";
// // 指定字段之间的分隔符 和 key、value之间的分隔符-》对象
// let obj = querystring.parse(str,'&&','==');
// console.log(obj);

let server = http.createServer(function(req,res){
    let {pathname,query} = url.parse(req.url,true);
    let method = req.method.toLowerCase();//在node中取得的方法名永远是大写的
    if(pathname === '/form'){
        if(method === 'get'){
            res.end(JSON.stringify(query))
        }else{
            let buffers = [];
            //接收请求体
            req.on('data',function(data){
                buffers.push(data);
            })
            req.on('end',function(){
                //表单格式接受的都是a=b&c=d  
                //headers[  'content-type': 'application/x-www-form-urlencoded']
                let str = Buffer.concat(buffers).toString();
                res.end(JSON.stringify(querystring.parse(str)));
            })
        }
    }
})
server.listen(3000)

表单提交的文件比较特殊

    <!-- form支持什么格式提交get post 不识别默认get   enctype="multipart/form-data" 多文本,内容类型:多段form表单格式 -->
    <form  action="http://localhost:3000/form" method="get">
        用户名 <input type="text" name="username"><br>
        密码 <input type="text" name="password"><br>
        <input type="submit">
    </form>

多段form表单格式会用分隔符将内容分离开

我们需要将内容解析,或者需要安装npm install formidable

let http = require('http');
let url = require('url');
let querystring = require('querystring');
let formidable = require('formidable')
let path = require('path')
// let str = "username==123&&password==321";
// // 指定字段之间的分隔符 和 key、value之间的分隔符-》对象
// let obj = querystring.parse(str,'&&','==');
// console.log(obj);

let server = http.createServer(function(req,res){
    let {pathname,query} = url.parse(req.url,true);
    let method = req.method.toLowerCase();//在node中取得的方法名永远是大写的
    if(pathname === '/form'){
        if(method === 'get'){
            res.end(JSON.stringify(query))
        }else{
            var form = new formidable.IncomingForm();
            //fields a=b&c=d文本  files文件
            form.keepExtensions = true;//保留后缀
            form.encoding = 'utf-8';
            form.uploadDir = path.join(__dirname,'./myDir');
            form.parse(req, function(err, fields, files) {
            });
            form.on('end', function() {
                res.end('上传成功');
            });
        }
    }
})
server.listen(3000)

ajax 同步体验不好,可以无刷新页面 1.0,2.0 ajax基于表单请求http发过去的,只是发送方式不一样

 <!-- 不支持跨域,协议,主机名,端口号 表单的好处可以加自己的校验-->
    <form  onsubmit="login(event)" id="form">
        用户名:
        <input type="text" name="username" required>
        <br> 密码:
        <input type="text" name="password">
        <br>
        <input type="submit" value="提交">
    </form>
function login(e) {
        let $ = document.querySelector.bind(document);
        function serialized(ele) {
          let arr = [];
          let elements = ele.elements;
          for (let i = 0; i < elements.length; i++) {
            let { type, name, value } = elements[i];
            switch (type) {
              case 'text':
              case 'password':
                arr.push(`${name}=${value}`);
                break
              default:
            }
          }
          return arr.join('&');
        }
        function login(e) {
            //阻止默认行为
            e.preventDefault();
            //表单序列化 username=123&password=456
            let qs = serialized($('#form'));
            //ajax4步
            let xhr = new XMLHttpRequest();
            xhr.open('GET', `/2.html?${qs}`, true); // true代表是否异步    
            xhr.responseType = 'json';// 服务返回的应该是一个对象
            xhr.timeout = 3000; // 3000过去了还没有数据返回就是超时
            xhr.ontimeout = function () {
                console.log('超时')
            }
            xhr.onerror = function (err) {//断网
                console.log(err);
            }
            xhr.setRequestHeader('Content-Type', 'application/www-x-form-urlencoded');
            // xhr.setRequestHeader('Content-Type','application/json')
            //readyState 0 表示还没有open 1代表open了 2 代表发送求求 3.接收到了部分请求
            xhr.onreadystatechange = function () {
                console.log(xhr.readyState);
                console.log(xhr.status)
                if (xhr.readyState === 4) {
                    if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
                        console.log(xhr.response);
                    }
                }
            }
            xhr.send(qs); 
        }

需要启动服务否则跨域

//服务代码
let http = require('http');
let url = require('url');
let fs = require('fs');
let path = require('path');
let querystring = require('querystring');
let server = http.createServer(function (req,res) {
    let {pathname,query} = url.parse(req.url,true);
    if(pathname === '/2.html'){
        let method = req.method.toLowerCase();
        if(method === 'get'){ //get方法
            res.end(JSON.stringify(query));
        }else{//post方法
            let buffers = [];
            req.on('data', function (data) {
                buffers.push(data);
            });
            req.on('end', function () {
                let str = Buffer.concat(buffers).toString();
                console.log(str);
                res.end(JSON.stringify(querystring.parse(str)));
            })
        }
        return 
    }
    if(pathname === '/'){
        return fs.createReadStream(path.join(__dirname,'./1.html')).pipe(res);
    }
    let p = path.join(__dirname, pathname);
    fs.stat(p,function (err,stat) {
        if(!err){
        fs.createReadStream(p).pipe(res);
        }else{
        res.statusCode = 404;
        res.end(`NotFound`);
        }
    })
});
server.listen(3000);

post请求和get形似

我们队上述ajax做个简单的封装

function login(e) {
    //阻止默认行为
    e.preventDefault();
    //表单序列化 username=123&password=456
    let qs = serialized($('#form'));
    //ajax4步
    let xhr = new XMLHttpRequest();
    ajax({
        url: '/2.html',
        method: 'post',
        dataType: 'json',
        contentType:'application/www-x-form-urlencoded',//只有post需要传
        data: qs
    }).then(data=>{
        console.log(data);
    }).catch(e=>{
        console.log(e);
    });
}
function ajax(options) {
    return new Promise((resolve,reject)=>{
        let {
            url = new Error('url must a string'),
            method = "get",
            dataType = "text",
            data = null,
            success,
            contentType,
            error
        } = options;
        let xhr = new XMLHttpRequest();
        if (method == 'get') {
            xhr.open(method, `${url}?${data}`, true);//如果是get请求,要将数据拼接到url
        } else {
            xhr.open(method, url, true);
            xhr.setRequestHeader('ContentType', contentType);//设置请求头
        }
        xhr.responseType = dataType;//响应类型
        xhr.onerror = function (err) {
            error && error(err);
            reject(err)
        }
        xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
                console.log(xhr.response)
                success && success(xhr.response);
                resolve(xhr.response)
            }
        }
        }
        data = method === 'get' ? null : data;
        xhr.send(data);
    })
}