|
Groovy/FAQ/CGI программирование
Материал из Wiki.crossplatform.ru
Introduction
//----------------------------------------------------------------------------------
// URLs have the same form as in Perl
// Invoking dynamic content is done through the same standard urls:
// http://mox.perl.com/cgi-bin/program?name=Johann&born=1685
// http://mox.perl.com/cgi-bin/program
// Groovy has Groovelets and GSP page support built-in. For a full
// web framework, see Grails: http://grails.codehaus.org/
//----------------------------------------------------------------------------------
Writing a CGI Script
//----------------------------------------------------------------------------------
// as a plain groovelet
param = request.getParameter('PARAM_NAME')
println """
<html><head>
<title>Howdy there!</title>
</head>
<body>
<p>
You typed: $param
</p>
</body>
</html>
"""
// as a groovelet using markup builder
import groovy.xml.MarkupBuilder
writer = new StringWriter()
builder = new MarkupBuilder(writer)
builder.html {
head {
title 'Howdy there!'
}
body {
p('You typed: ' + request.getParameter('PARAM_NAME'))
}
}
println writer.toString()
// as a GSP page:
<html><head>
<title>Howdy there!</title>
</head>
<body>
<p>
You typed: ${request.getParameter('PARAM_NAME')}
</p>
</body>
</html>
// Request parameters are often encoded by the browser before
// sending to the server and usually can be printed out as is.
// If you need to convert, use commons lang StringEscapeUtils#escapeHtml()
// and StringEscapeUtils#unescapeHtml().
// Getting parameters:
who = request.getParameter('Name')
phone = request.getParameter('Number')
picks = request.getParameterValues('Choices') // String array or null
// Changing headers:
response.setContentType('text/html;charset=UTF-8')
response.setContentType('text/plain')
response.setContentType('text/plain')
response.setHeader('Cache-control', 'no-cache')
response.setDateHeader('Expires', System.currentTimeMillis() + 3*24*60*60*1000)
//----------------------------------------------------------------------------------
Redirecting Error Messages
//----------------------------------------------------------------------------------
// The Java Servlet API has a special log() method for writing to the
// web server log.
// To send errors to custom HTML pages, update the web.xml deployment
// descriptor to include one or more <error-page> elements, e.g.:
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
<error-page>
<exception-type>java.lang.NullPointerException</exception-type>
<location>/NpeError.gsp</location>
</error-page>
// Another trick is to catch an exception within the servlet/gsp code
// and print it out into the HTML as a comment.
//----------------------------------------------------------------------------------
Fixing a 500 Server Error
//----------------------------------------------------------------------------------
// 500 errors could occur if you have compile errors in your script.
// Pre-compile with your IDE or groovyc.
// You can use an expando, mock or map to run your scripts outside
// the web container environment. If you use Jetty as your container
// it has a special servlet tester, for more details:
// http://blogs.webtide.com/gregw/2006/12/16/1166307599250.html
//----------------------------------------------------------------------------------
Writing a Safe CGI Program
//----------------------------------------------------------------------------------
// Web servers should be invoked with an appropriate Java security policy in place.
// This can be used to limit possible actions from hacking attempts.
// Normal practices limit hacking exposure. The JDBC API encourages the use
// of Prepared queries rather than encouraging practices which lead to SQL
// injection. Using system or exec is rarely used either as Java provides
// cross-platform mechanisms for most operating system level functionality.
// Other security measures should be complemented with SSL and authentication.
//----------------------------------------------------------------------------------
Making CGI Scripts Efficient
//----------------------------------------------------------------------------------
// Within the servlet element of your web.xml, there is a <load-on-startup> element.
// Use that on a per servlet basis to pre-load whichever servlets you like.
//----------------------------------------------------------------------------------
Executing Commands Without Shell Escapes
//----------------------------------------------------------------------------------
// As discussed in 19.3 and 19.4:
// Web servers should be invoked with an appropriate Java security policy in place.
// This can be used to limit possible actions from hacking attempts.
// Normal practices limit hacking exposure. The JDBC API encourages the use
// of Prepared queries rather than encouraging practices which lead to SQL
// injection. Using system or exec is rarely used either as Java provides
// cross-platform mechanisms for most operating system level functionality.
// In addition, if authentication is used, security can be locked down at a
// very fine-grained level on a per servlet action or per user (with JAAS) basis.
//----------------------------------------------------------------------------------
Formatting Lists and Tables with HTML Shortcuts
//----------------------------------------------------------------------------------
import groovy.xml.*
// using a builder:
Closure markup = {
ol {
['red','blue','green'].each{ li(it) }
}
}
println new StreamingMarkupBuilder().bind(markup).toString()
// => <ol><li>red</li><li>blue</li><li>green</li></ol>
names = 'Larry Moe Curly'.split(' ')
markup = {
ul {
names.each{ li(type:'disc', it) }
}
}
println new StreamingMarkupBuilder().bind(markup).toString()
// <ul><li type="disc">Larry</li><li type="disc">Moe</li>
// <li type="disc">Curly</li></ul>
//-----------------------------
m = { li("alpha") }
println new StreamingMarkupBuilder().bind(m).toString()
// <li>alpha</li>
m = { ['alpha','omega'].each { li(it) } }
println new StreamingMarkupBuilder().bind(m).toString()
// <li>alpha</li> <li>omega</li>
//-----------------------------
states = [
"Wisconsin": [ "Superior", "Lake Geneva", "Madison" ],
"Colorado": [ "Denver", "Fort Collins", "Boulder" ],
"Texas": [ "Plano", "Austin", "Fort Stockton" ],
"California": [ "Sebastopol", "Santa Rosa", "Berkeley" ],
]
writer = new StringWriter()
builder = new MarkupBuilder(writer)
builder.table{
caption('Cities I Have Known')
tr{ th('State'); th(colspan:3, 'Cities') }
states.keySet().sort().each{ state ->
tr{
th(state)
states[state].sort().each{ td(it) }
}
}
}
println writer.toString()
// =>
// <table>
// <caption>Cities I Have Known</caption>
// <tr>
// <th>State</th>
// <th colspan='3'>Cities</th>
// </tr>
// <tr>
// <th>California</th>
// <td>Berkeley</td>
// <td>Santa Rosa</td>
// <td>Sebastopol</td>
// </tr>
// <tr>
// <th>Colorado</th>
// <td>Boulder</td>
// <td>Denver</td>
// <td>Fort Collins</td>
// </tr>
// <tr>
// <th>Texas</th>
// <td>Austin</td>
// <td>Fort Stockton</td>
// <td>Plano</td>
// </tr>
// <tr>
// <th>Wisconsin</th>
// <td>Lake Geneva</td>
// <td>Madison</td>
// <td>Superior</td>
// </tr>
// </table>
import groovy.sql.Sql
import groovy.xml.MarkupBuilder
dbHandle = null
dbUrl = 'jdbc:hsqldb:...'
def getDb(){
if (dbHandle) return dbHandle
def source = new org.hsqldb.jdbc.jdbcDataSource()
source.database = dbUrl
source.user = 'sa'
source.password = ''
dbHandle = new Sql(source)
return dbHandle
}
def findByLimit(limit) {
db.rows "SELECT name,salary FROM employees where salary > $limit"
}
limit = request.getParameter('LIMIT')
writer = new StringWriter()
builder = new MarkupBuilder(writer)
builder.html {
head { title('Salary Query') }
h1('Search')
form{
p('Enter minimum salary')
input(type:'text', name:'LIMIT')
input(type:'submit')
}
if (limit) {
h1('Results')
table(border:1){
findByLimit(limit).each{ row ->
tr{ td(row.name); td(row.salary) }
}
}
}
}
println writer.toString()
//----------------------------------------------------------------------------------
Redirecting to a Different Location
//----------------------------------------------------------------------------------
// The preferred way to redirect to resources within the web application:
dispatcher = request.getRequestDispatcher('hello.gsp')
dispatcher.forward(request, response)
// Old versions of web containers allowed this mechanism to also redirect
// to external resources but this was deemed a potential security risk.
// The suggested way to external sites (less efficient for internal resources):
response.sendRedirect("http://www.perl.com/CPAN/")
// set cookie and forward
oreo = new Cookie('filling', 'vanilla creme')
THREE_MONTHS = 3 * 30 * 24 * 60 * 60
oreo.maxAge = THREE_MONTHS
oreo.domain = '.pleac.sourceforge.net'
whither = 'http://pleac.sourceforge.net/pleac_ruby/cgiprogramming.html'
response.addCookie(oreo)
response.sendRedirect(whither)
// forward based on user agent
dir = 'http://www.science.uva.nl/%7Emes/jargon'
agent = request.getHeader('user-agent')
menu = [
[/Mac/, 'm/macintrash.html'],
[/Win(dows )?NT/, 'e/evilandrude.html'],
[/Win|MSIE|WebTV/, 'm/microslothwindows.html'],
[/Linux/, 'l/linux.html'],
[/HP-UX/, 'h/hpsux.html'],
[/SunOS/, 's/scumos.html'],
]
page = 'a/aportraitofj.randomhacker.html'
menu.each{
if (agent =~ it[0]) page = it[1]
}
response.sendRedirect("$dir/$page")
// no response output
response.sendError(204, 'No Response')
//----------------------------------------------------------------------------------
Debugging the Raw HTTP Exchange
//----------------------------------------------------------------------------------
// Consider TCPMON or similar: http://ws.apache.org/commons/tcpmon/
//----------------------------------------------------------------------------------
Managing Cookies
//----------------------------------------------------------------------------------
// helper method
import javax.servlet.http.Cookie
import groovy.xml.MarkupBuilder
def getCookieValue(cookies, cookieName, defaultValue) {
if (cookies) for (i in 0..<cookies.length) {
if (cookieName == cookies[i].name) return cookies[i].value
}
return defaultValue
}
prefValue = getCookieValue(request.cookies, 'preference_name', 'default')
cookie = new Cookie('preference name',"whatever you'd like")
SECONDS_PER_YEAR = 60*60*24*365
cookie.maxAge = SECONDS_PER_YEAR * 2
response.addCookie(cookie)
cookname = 'fav_ice_cream'
favorite = request.getParameter('flavor')
tasty = getCookieValue(request.cookies, cookname, 'mint')
writer = new StringWriter()
builder = new MarkupBuilder(writer)
builder.html {
head { title('Ice Cookies') }
body {
h1('Hello Ice Cream')
if (favorite) {
p("You chose as your favorite flavor '$favorite'.")
cookie = new Cookie(cookname, favorite)
ONE_HOUR = 3600 // secs
cookie.maxAge = ONE_HOUR
response.addCookie(cookie)
} else {
hr()
form {
p('Please select a flavor: ')
input(type:'text', name:'flavor', value:tasty)
}
hr()
}
}
}
println writer.toString()
//----------------------------------------------------------------------------------
Creating Sticky Widgets
//----------------------------------------------------------------------------------
import groovy.xml.MarkupBuilder
// On Linux systems replace with: "who".execute().text
fakedWhoInput = '''
root tty1 Nov 2 17:57
hermie tty3 Nov 2 18:43
hermie tty4 Nov 1 20:01
sigmund tty2 Nov 2 18:08
'''.trim().split(/\n/)
name = request.getParameter('WHO')
if (!name) name = ''
writer = new StringWriter()
new MarkupBuilder(writer).html{
head{ title('Query Users') }
body{
h1('Search')
form{
p('Which User?')
input(type:'text', name:'WHO', value:name)
input(type:'submit')
}
if (name) {
h1('Results')
lines = fakedWhoInput.grep(~/^$name\s.*/)
if (lines) message = lines.join('\n')
else message = "$name is not logged in"
pre(message)
}
}
}
println writer.toString()
// if you need to escape special symbols, e.g. '<' or '>' use commons lang StringEscapeUtils
//----------------------------------------------------------------------------------
Writing a Multiscreen CGI Script
//----------------------------------------------------------------------------------
// frameworks typically do this for you, but shown here are the manual steps
// even when doing it manually, you would probably use session variables
// setting a hidden field
input(type:'hidden', value:'bacon')
// setting a value on the submit
input(type:'submit', name:".State", value:'Checkout')
// determining 'mode'
page = request.getParameter('.State')
if (!page) page = 'Default'
// forking with if chain
if (page == "Default") {
frontPage()
} else if (page == "Checkout") {
checkout()
} else {
noSuchPage()
}
// forking with map
states = [
Default: this.&frontPage,
Shirt: this.&tShirt,
Sweater: this.&sweater,
Checkout: this.&checkout,
Card: this.&creditCard,
Order: this.&order,
Cancel: this.&frontPage,
]
// calling each to allow hidden variable saving
states.each{ key, closure ->
closure(page == key)
}
// exemplar method
def tShirt(active) {
def sizes = ['XL', 'L', 'M', 'S']
def colors = ['Black', 'White']
if (!active) {
hidden("size")
hidden("color")
return
}
p("You want to buy a t-shirt?");
label("Size: "); dropDown("size", sizes)
label("Color: "); dropDown("color", colors)
shopMenu()
}
// kicking off processing
html{
head{ title('chemiserie store') }
body {
if (states[page]) process(page)
else noSuchPage()
}
}
//----------------------------------------------------------------------------------
Saving a Form to a File or Mail Pipe
//----------------------------------------------------------------------------------
// get request parameters as map
map = request.parameterMap
// save to file
new File(filename).withOutputStream{ fos ->
oos = new ObjectOutputStream(fos)
oos.writeObject(map)
oos.close()
}
// convert to text
sb = new StringBuffer()
map.each{ k,v -> sb << "$k=$v" }
text = sb.toString()
// to send text via email, see 18.3
//----------------------------------------------------------------------------------
Program: chemiserie
//----------------------------------------------------------------------------------
// you wouldn't normally do it this way, consider a framework like Grails
// even when doing it by hand, you would probably use session variables
import groovy.xml.MarkupBuilder
page = param('.State', 'Default')
states = [
Default: this.&frontPage,
Shirt: this.&shirt,
Sweater: this.&sweater,
Checkout: this.&checkout,
Card: this.&creditCard,
Order: this.&order,
Cancel: this.&frontPage,
]
writer = new StringWriter()
b = new MarkupBuilder(writer)
b.html{
head{ title('chemiserie store') }
body {
if (states[page]) process(page)
else noSuchPage()
}
}
println writer.toString()
def process(page) {
b.form{
states.each{ key, closure ->
closure(page == key)
}
}
}
def noSuchPage() {
b.p('Unknown request')
reset('Click here to start over')
}
def shopMenu() {
b.p()
toPage("Shirt")
toPage("Sweater")
toPage("Checkout")
reset('Empty My Shopping Cart')
}
def frontPage(active) {
if (!active) return
b.h1('Hi!')
b.p('Welcome to our Shirt Shop! Please make your selection from the menu below.')
shopMenu()
}
def shirt(active) {
def sizes = ['XL', 'L', 'M', 'S']
def colors = ['Black', 'White']
def count = param('shirt_count',0)
def color = param('shirt_color')
def size = param('shirt_size')
// sanity check
if (count) {
if (!(color in colors)) color = colors[0]
if (!(size in sizes)) size = sizes[0]
}
if (!active) {
if (size) hidden("shirt_size", size)
if (color) hidden("shirt_color", color)
if (count) hidden("shirt_count", count)
return
}
b.h1 'T-Shirt'
b.p '''What a shirt! This baby is decked out with all the options.
It comes with full luxury interior, cotton trim, and a collar
to make your eyes water! Unit price: $33.00'''
b.h2 'Options'
label("How Many?"); textfield("shirt_count")
label("Size?"); dropDown("shirt_size", sizes)
label("Color?"); dropDown("shirt_color", colors)
shopMenu()
}
def sweater(active) {
def sizes = ['XL', 'L', 'M']
def colors = ['Chartreuse', 'Puce', 'Lavender']
def count = param('sweater_count',0)
def color = param('sweater_color')
def size = param('sweater_size')
// sanity check
if (count) {
if (!(color in colors)) color = colors[0]
if (!(size in sizes)) size = sizes[0]
}
if (!active) {
if (size) hidden("sweater_size", size)
if (color) hidden("sweater_color", color)
if (count) hidden("sweater_count", count)
return
}
b.h1("Sweater")
b.p("Nothing implies preppy elegance more than this fine " +
"sweater. Made by peasant workers from black market silk, " +
"it slides onto your lean form and cries out ``Take me, " +
"for I am a god!''. Unit price: \$49.99.")
b.h2("Options")
label("How Many?"); textfield("sweater_count")
label("Size?"); dropDown("sweater_size", sizes)
label("Color?"); dropDown("sweater_color", colors)
shopMenu()
}
def checkout(active) {
if (!active) return
b.h1("Order Confirmation")
b.p("You ordered the following:")
orderText()
b.p("Is this right? Select 'Card' to pay for the items" +
"or 'Shirt' or 'Sweater' to continue shopping.")
toPage("Card")
toPage("Shirt")
toPage("Sweater")
}
def creditCard(active) {
def widgets = 'Name Address1 Address2 City Zip State Phone Card Expiry'.split(' ')
if (!active) {
widgets.each{ hidden(it) }
return
}
b.pre{
label("Name: "); textfield("Name")
label("Address: "); textfield("Address1")
label(" "); textfield("Address2")
label("City: "); textfield("City")
label("Zip: "); textfield("Zip")
label("State: "); textfield("State")
label("Phone: "); textfield("Phone")
label("Credit Card #: "); textfield("Card")
label("Expiry: "); textfield("Expiry")
}
b.p("Click on 'Order' to order the items. Click on 'Cancel' to return shopping.")
toPage("Order")
toPage("Cancel")
}
def order(active) {
if (!active) return
b.h1("Ordered!")
b.p("You have ordered the following items:")
orderText()
reset('Begin Again')
}
def orderText() {
def shirts = param('shirt_count')
def sweaters = param('sweater_count')
if (shirts) {
b.p("""You have ordered ${param('shirt_count')}
shirts of size ${param('shirt_size')}
and color ${param("shirt_color")}.""")
}
if (sweaters) {
b.p("""You have ordered ${param('sweater_count')}
sweaters of size ${param('sweater_size')}
and color ${param('sweater_color')}.""")
}
if (!sweaters && !shirts) b.p("Nothing!")
b.p("For a total cost of ${calcPrice()}")
}
def label(text) { b.span(text) }
def reset(text) { b.a(href:request.requestURI,text) }
def toPage(name) { b.input(type:'submit', name:'.State', value:name) }
def dropDown(name, values) {
b.select(name:name){
values.each{
if (param(name)==it) option(value:it, selected:true, it)
else option(value:it, it)
}
}
b.br()
}
def hidden(name) {
if (binding.variables.containsKey(name)) v = binding[name]
else v = ''
hidden(name, v)
}
def hidden(name, value) { b.input(type:'hidden', name:name, value:value) }
def textfield(name) { b.input(type:'text', name:name, value:param(name,'')); b.br() }
def param(name) { request.getParameter(name) }
def param(name, defValue) {
def val = request.getParameter(name)
if (val) return val else return defValue
}
def calcPrice() {
def shirts = param('shirt_count', 0).toInteger()
def sweaters = param('sweater_count', 0).toInteger()
return (shirts * 33 + sweaters * 49.99).toString()
}
//----------------------------------------------------------------------------------
|