文号
824024
阅读
7165
回复
6
收藏
3
动态网页挑战
2016/08/04
IP:广东

人们不断地争辩:A语言比B语言好,C框架比D框架好……所以我从PG的网站上借鉴了这样一个挑战。

这个挑战非常简单,任何编程语言都能参与。然后我们可以看看,在每一种语言中分别需要多少行代码。

请写一个程序,令某个url(比如 http://localhost:port/abc)返回带有一个输入框和一个按钮的一张网页。当用户按下按钮的时候,将得到第二张网页,上面有一个链接写着“请点这里”。当用户点击这个链接,他将进入第三个网页,上面写着用户在第一页的输入框中输入的内容。

第三页只允许显示用户在第一页中实际输入的内容,也就是说输入框中的内容不准通过url传递,即无法通过编辑url来改变第三页的内容。

这个挑战虽然看上去简单,但并非故弄玄虚。每天,无数的Web应用(包括博客、网店、论坛、邮件)都在不断地重复这类工作。

更重要的是,这个挑战不需要借助任何稀有的库;但凡可以进行TCP通信的语言,都可以完成这个挑战。

[ 修改于10年4个月前 - 2016/08/04 13:03:56 ]
10年4个月前修改于 10年4个月前IP:江西
824049
1楼
JS

//访问地址:http://localhost:8080/
//貌似其实可以全部写成一行的...  -_-||
var http=require('http');
var url=require('url');
var express=require('express');
var session=require('express-session');
var bodyParser=require("body-parser");  
var app = express();
var server = app.listen(8080, function () {});
app.use(bodyParser.urlencoded({ extended: false }));  
app.use(session({secret: '123456',name: 'session',cookie: {maxAge: 3600000},resave: false,saveUninitialized: true}));
app.get('/', function (req, res) {
         res.writeHead(200, {'Content-Type': 'text/html'});	
         res.write('<html><meta charset="UTF-8"><body><form action="/sub" method="post"><input name="t" type="text"> <input type="submit"></form></body></html>');		
         res.end();
});
app.post('/sub',function(req,res){
	req.session.text=req.body.t;
	res.writeHead(200, {'Content-Type': 'text/html'});	
        res.write('<html><meta charset="UTF-8"><body>请点这里</body></html>');		
        res.end();
});
app.get('/text',function(req,res){
	res.writeHead(200, {'Content-Type': 'text/html'});	
	if(req.session.text!=null)
		res.write('<html><meta charset="UTF-8"><body>'+req.session.text+'</body></html>');
	else
		res.write('<html><meta charset="UTF-8"><body>....</body></html>');
	res.end();	
});
引用
评论
10年4个月前修改于 10年4个月前IP:湖北
824057
2楼
用php做这样的事是相当简单的,不考虑安全等情况,仅作为演示:

<?php
    $form1 =  '<form method="post" action="index.php?tips"><input type="text" name="your_input"><input type="submit" value="提交">';
    $form2 = '<form method="post" action="index.php?show"><input type="submit" value="请点击这里"></form>';
    
   switch($_SERVER["QUERY_STRING"])
   {
       case '':
            echo '<script>document.write(\''.$form1.'\');</script>';
            break;
       case 'tips':
            session_start();
            $_SESSION['input'] = $_POST['your_input'];
            echo '<script>document.write(\''.$form2.'\');</script>';
            break;
       case 'show':
            session_start();
            echo '<script>document.write(\''.$_SESSION['input'].'\');</script>';
            break;
   }
引用
评论
novakon作者
10年4个月前修改于 10年4个月前IP:广东
824158
3楼

因为我本人是JS hacker, 就用JS做一次吧。

首先,我复制了radio的代码,并作了一些细微的重构。功能完全一致。

var http=require('http');
var express=require('express');
var session=require('express-session');
var bodyParser=require("body-parser");
var app = express();

app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({secret: '123456',name:'session',cookie: {maxAge: 3600000},resave: false,saveUninitialized: true}));

//tag generator
var tg = (name,content,props)=>`<${name} ${props||''}${content?'':' '}>${content||''}${content?'':''}`

var htmlWrap = (content)=>tg('html',tg('body',content))

var textInput = (name)=>tg('input',null,'name="'+name+'" type="text"')
var submit = tg('input',null,'type="submit"')

//form generator
var fg = (content)=>tg('form',content,'action="sub" method="post"')

var formHTML = htmlWrap(fg(textInput('t')+submit))
var clickHTML = htmlWrap(tg('a','请点这里','href="/text"'))

app.get('/', function(req, res) {
  res.send(formHTML);
});

app.post('/sub',function(req,res){
  req.session.text=req.body.t;
  res.send(clickHTML)
});

var textToHTML = (text)=>htmlWrap(text)

app.get('/text',function(req,res){
  res.send(textToHTML(req.session.text||'....'))
});

app.listen(9001,()=>{console.log('listening on 9001');});

</${name}>

但是我觉得:这不清真。表达仍然太冗长、太不自然了。我准备写个清真的版本。

引用
评论
novakon作者
10年4个月前修改于 10年4个月前IP:广东
824159
4楼
可以,终于清真了

采用了异步Promise写法:

//tag generator
var tg = (name,content,props)=>`<${name} ${props||''}${content?'':' '}>${content||''}${content?'':''}`

var htmlWrap = (content)=>tg('html',tg('body',content))

var textInput = (name)=>tg('input',null,'name="'+name+'" type="text"')
var submit = tg('input',null,'type="submit"')

//form generator
var fg = (content)=>tg('form',content,'action="" method="post"')

var formHTML = (content)=>htmlWrap(fg(content))
var linkHTML = (address)=>htmlWrap(tg('a','请点这里','href="'+address+'"'))
var textHTML = (text)=>htmlWrap(text)

var ee = (require('events').EventEmitter);

var nextVisitPromise = function(){
  var nvp = {}
  var e = new ee;
  nvp.trig = (req,res)=>{
    e.emit('trig',{req,res,waitForNextVisit:nvp.waitForNextVisit})
  }

  nvp.waitForNextVisit = ()=>{
    return new Promise((resolve,reject)=>{
      e.on('trig',resolve)
    })
  }
  return nvp
}

var nvprecords = {}

var bindOp = (path,op)=>{
  app.all(path,(req,res)=>{
    var s = req.session
    var key = 'p'+path

    if(s[key]){
      console.log(s[key]);
      var nvp = nvprecords[s.id]
      nvp.trig(req,res)
    }else{
      s[key] = true
      var nvp = nextVisitPromise()
      nvprecords[s.id]=nvp

      nvp.waitForNextVisit()
      .then((ctx)=>{
        return op(ctx)
      })
      .then(()=>{
        console.log('clearing session...');
        req.session.destroy()
        nvprecords[s.id]=undefined
      })

      nvp.trig(req,res)
    }
  })
}

app.listen(9001,()=>{console.log('listening on 9001');});

bindOp('/abc',(ctx)=>{
  var userinput
  ctx.res.send(formHTML(textInput('cde')+submit))
  return ctx.waitForNextVisit()
  .then(ctx=>{
    userinput = ctx.req.body.cde
    ctx.res.send(linkHTML('/abc'))
    return ctx.waitForNextVisit()
  })
  .then(ctx=>{
    ctx.res.send(textHTML(userinput||'....'))
  })
})

</${name}>

上面这段代码,访问/abc路径后,效果符合题目要求。 每次向用户返回HTML之后,直接Promise等待用户下一次访问,并通过闭包使用userinput变量。使用这种设计,就可以很短的代码,设计任意复杂的跳转流程,而不需要使用大量的路径以及状态机。

坏处:相当于把HTTP硬生生地变成了有状态协议,宕机的时候状态会丢失

好处:写起来无负担

引用
评论
novakon作者
10年4个月前修改于 10年4个月前IP:广东
824160
5楼

稍作修改,现在可以接受两个输入,并且在最后一页显示。

bindOp('/abc',(ctx)=>{
  var userinput
  ctx.res.send(formHTML(textInput('user')+textInput('pass')+submit))
  return ctx.waitForNextVisit()
  .then(ctx=>{
    userinput = ctx.req.body
    ctx.res.send(linkHTML('/abc'))
    return ctx.waitForNextVisit()
  })
  .then(ctx=>{
    ctx.res.send(textHTML(userinput.user+'\'s password is '+ userinput.pass||'....'))
  })
})
引用
评论
笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的