Why Ruby amazes me? PRuby Chap 6 方法调用

Gem Gatherer 发表于 2008-05-02 17:01:19

用def可以定义一个方法,方法必须以一个小写字母开头
** 如果使用大写字母,不会出错,但是Ruby会优先猜测这是一个常量而不是一个方法调用,解析可能出错

指定形参默认值
def cool_dude(arg1="Miles", arg2="Coltrane", arg3="Roach")
  "#{arg1}..."
end

** 不能在方法体内定义非单件类或模块
** 可以嵌套定义方法,但此方法只有在外部方法执行的时候才得到定义
** 方法的返回值是执行的最后一个表达式的值,或者return显式返回的值

可变参数列表
def varargs(arg1, *rest)
  ...
end
这样,第一个参数赋值给arg1,其它剩余的参数都会被装入到一个新的Array中,然后赋值给第二个参数
** 它还有个有趣的逆操作:)
def five(a,b,c,d,e)
end
<-- five(1,2,3,*['a','b'])
<-- five( *(10..14).to_a)
看见了么?可以传递一个前面加*的数组,这样,数组元素就会自动被视为单独的参数。

方法和block
调用一个方法时,可以提供一个block与其关联,在方法内可以用yield调用这个block
** 如果方法定义的最后一个参数前缀是&,那么所关联的block会被转换为一个Proc对象,然后赋值给这个参数
def initialize(name,&block)
  @name, @block=name, block
end
def get_tax(amount)
  "Tax = #{@block.call(amount)} "  # 这样便于传递代码片段对象。。。很方便不是么:)
end

调用方法
** 如果没有二义性,在调用方法时,可以省略参数列表两侧的括号。
** 如果在方法名和左括号之间有空格,将得到一个警告

返回值
Ruby可以轻易的返回多个值.
def meth
  return 1,2
end
<-- meth
--> [1, 2]

更加动态的block : 让lambda来辅助block!
if d > 0
 calc = lambda {|n| n*number}
else
  calc = lambda {|n| n+number}
<-- puts((1..10).collect(&calc).join(", "))
在实参里加&的参数是代表什么?Ruby将认为它是一个Proc对象,会将其从参数列表中删除,并将Proc对象转化为一个block,然后关联到该方法

散列参数
当我们要传送动态创建的散列表作为参数时,我们可以不用一般创建散列表的方式(那样容易和block混起来)...
list.create_search('short jazz songs',
                   'genre'  =>  'jazz',
                   'duration_less_than' => 270)
当然,这样要散列表是形参表的最后一个才能有效。
** 上面的调用还有一个形式
list.create_search('short jazz songs',
                   :genre  =>  :jazz,
                   :duration_less_than => 270)

关键词(Tag): ruby
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

Why Ruby amazes me? PRuby Chap 5 标准类型

Gem Gatherer 发表于 2008-05-02 01:03:14

所有的数字也是对象。可以对各种形式的消息做出响应.
整数也支持几种有用的迭代器。
3.times { print "X" }
1.upto(5) { |i| print i, " "}
9.downto(7) { |i| print i, " "}
50.step(80,5) { |i| print i, " "}

** 注意,只包含数字的字符串在表达式运算时不会被自动转换成数字
<-- "34" + "56"
--> "3456"
** 但是可以这样
<-- Integer("34") + Integer("56")
--> 90

** Ruby的字符串是8比特字节的序列
可以用#{expr}序列把任何ruby代码的表达式放进字符串中
<-- "#{'Ho!'*3}, Mike"
--> "Ho!Ho!Ho!, Mike"
** 甚至可以将一句或多条语句加入#{}
puts "now is #{def the(a)
                 'the ' + a
               end
               the('Time')
               } for me."
              
其它构建字符串的方式...
%q : 代表单引号引起的字符串(%q就相当于一个')
%Q : 代表单引号引起的字符串(%Q就相当于一个")
%q/general single-quoted string/  --> 'general single-quoted string'
%Q!general single-quoted string!  --> "general double-quoted string"
** %q和%Q后面的第一个字符是分界符。此字符串直到下一个分界符出现为止(不包括分界符本身)

String的一些方法
String#split将一个字符串分割开来,可以接受正则表达式
<-- "1 | 2".split(/\s*\|\s*/)
--> ["1", "2"]

String#chomp将字符串前后的空格去掉

String.squeeze将字符串中重复并连续的字符合并。
<-- "Heeeeeeello  world".squeeze("e")
--> "Hello world" # 如果不带参数则unique所有字符

** 有一些函数,例如squeeze,会返回处理后的字符串,但它还有一个版本squeeze!,是在字符串本身上做修改

String#scan可以从字符串中抽取匹配正则表达式的元素
<-- "112|2223".scan(/\d+/)
--> ["112", "2223"]

区间 : Ruby用区间实现了序列(sequence),条件(conditions)和间隔(intervals)

区间作为序列:序列是有起点和终点,并可以在序列中产生连续值的方法
** 序列可以用..和...来产生 ..创建闭区间(包括右端点),而...是开区间(不包括右端点)

1..10
'a'..'z'
** 序列可以用to_a方法转换成列表
<-- (1..3).to_a
--> [1, 2, 3]
<-- ('bar'..'bat').to_a
--> ["bar", "bas", "bat"] #没做任何额外声明就产生这结果了。。。真够智能的-_-
区间的一些方法
<-- d = 0..9
--> true
<-- d.include?(5)
--> 0
<-- [d.min, d.max]
--> [0, 9]
<-- d.reject { |i| i < 5}
--> [5, 6, 7, 8, 9]

** 可以用自己的对象来创建区间,但必须满足两个条件
1. 这些对象可以用<=> (spaceship操作符)来比较大小
class a
  def <=>(other)
    self.val <=> other.val
  end
end
2. 要能用succ产生下一个值
class a
  def succ
    raise(IndexError, "Too Large") if @val >= 9
    a.new(@val.succ)
  end
end
这样a就可以作为区间了
as = a.new(2)..a.new(8)

区间作为条件
while line = gets
  puts line if line=~/start/ .. line=~/end/
end
这段代码打印从标准输出得到的若干行的集合,每个集合都满足以下条件
1. 集合第一行包含"start"
2. 集合最后一行包含"end"

区间作为间隔
** 可以用===来测试一些值是否落入区间表达式的间隔内
<-- (1..10) === 3.14
--> true

正则表达式:Ruby内建支持。
创建的方式有三种,都是等效的:
a = Regexp.new('/^\s*[a-z]')
b = /^\s*[a-z]'/
c = %r{'/^\s*[a-z]'}

匹配的方式有两种, Regexo#match(string)或者=~(肯定匹配),!~(否定匹配)
name = "Fats Waller"
<-- name =~ /a/
--> 1
** 匹配返回匹配繁盛的字符位置
** 匹配会设置一些ruby线程变量
*** $& 得到与模式匹配的字符串
*** $` 得到匹配之前的字符串
*** $' 得到匹配之后的字符串
*** $~ MatchData对象,后有详述
*** - , 后有详述

** ^和$分别匹配行首和行尾
<-- "a op b" =~ /^op/
--> nil
** \A匹配字符串开头
** \z匹配字符串结尾。若字符串结尾为\n,则会无视\n
** \Z匹配字符串结尾。不会无视\n
** \b匹配词的边界 (组词字符是字母,数字和下划线的任意组合)
** \B匹配非词的边界

[]可以匹配在其之间的任何字符
** 特殊字符.|()[{+^$*?在方括号内被视为普通字符
** 字符串替换仍正常进行,例如\b代表回退空格,\n代表换行
** []内可以使用Posix字符类

字符类缩写
字符类 实际内容 含义
\d [0-9] 数字
\D [^0-9] 非数字
\s [ \t\r\n\f] 空格
\S [^ \t\r\n\f] 非空格
\w [A-Za-z0-9_] 组词字符
\W [^A-Za-z0-9_] 非组词字符

Posix字符类
字符类 含义
[:alnum:] 字母和数字
[:alpha:] 字母(大小写)
[:blank:] 空格和制表符
[:cntrl:] 控制字符(至少包括0x00-0x1f,0x7f)
[:digit:] 数字
[:graph:] 除了空格之外的可打印字符
[:lower:] 小写字母
[:print:] 任何可打印字符
[:punct:] 除字母数字和空格外的可打印字符
[:space:] 空格
[:upper:] 大写字符
[:xdigit:] 16进制数字(0-9,a-f,A-F)

 

重复: 若r代表任何模式,那么
r* 代表0或多个
r+ 代表1或多个
r? 代表0或1个
** r*和r+的匹配都是贪心的

编组收集:小括号在正则表达式中有重要的作用
<-- show_regexp('banana', /(an)*/)
--> <<banana>>
括号的作用一是改变优先级,二是收集括号匹配的结果。
Ruby计算开始括号的数目,保存每个开始括号和相应的关闭括号之间部分匹配的结果。可以在模式的剩余部分和Ruby程序中使用这种部分匹配
** 在模式内部,指的是第一个组的匹配,是第二个,...
** 在模式外,则有,等特殊变量。
** 这可以让你轻易寻找各式各样的重复,例如
<-- show_regexp("Mississippi", /(\w+)/)
--> M<<ississ>>ippi

基于模式的替换
String#sub替换一次,而String#gsub对所有满足的子串都加以替换
** 函数的第一个参数是正则表达式,第二个参数可以是String或者Block。如果是block,匹配的子串会传递给block,block的结果会替换到原先的子串中

MatchData对象
** 每次调用Regexp#match时候,如果失败则返回nil,如果成功,返回MatchData类的一个实例。MatchData对象让你访问关于这次匹配的所有可用信息。
MatchData ma;
ma[0] ---> $&
ma[1]..ma[9] ---> ..
ma.pre_match ---> $`
ma.post_match ---> $'

 

关键词(Tag): ruby
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

Way to Webdev:) Head First Servlet & JSP Chap14: 模式和Struts

Mystra 发表于 2008-04-25 21:26:19

Web应用可能很大,为了使我们的项目成功,我们必须使我们的网站做到下面几点:
1. 可维护性
2. 性能
3. 模块性
4. 灵活性,可扩展性

以下是一些宏观准则...
1. 使用接口编写代码  (不用关注实现)
2. 关注点分离和内聚  (让一个模块尽量只关注一件事情)
3. 隐藏复杂性  (如果一个类需要查找服务,而这件事情又很复杂,我们应该用一个组件来专门完成这件事情)
4. 松耦合  (一个类在和另一个类进行通讯时,不应对对方内部实现了解太多)
5. 远程代理 (隐藏复杂性的一种,让用户用操作一个本地对象的类似手法来操纵一个远程对象)
6. 增强声明性控制 (以声明方式控制应用,这样我们通过修改能改变尽量多的系统行为,而无需修改代码)

专题:远程对象:怎样调用在其它JVM里面对象的方法?
Java和J2EE为我们提供了一些机制,可以解决最常见的两个难题: 查找远程对象,以及处理本地和远程对象之间的底层网络I/O通信
JNDI(Java Naming and Directory Interface,Java命名和目录接口):这是访问命名和目录服务的API
** 利用JNDI我们可以在网络上的某个位置查找远程对象
** 如果我们希望某个对象能被JNDI查找到,那么可以向JNDI注册这些对象

RMI(Remote Method Invocation,远程方法调用)
** 利用RMI我们可以假装在调用本地对象方法一样去调用远程对象方法
** RMI创建了一个代理,并把这个对象注册到某种注册表。我们用这个代理做调用,它能处理所有通信细节,套接字,I/O流,TCP/IP,对参数和返回值串行化和反串行化,处理异常等等
不用RMI的客户端
public void foClient() {
  try {
    // 得到一个新Socket
    // 得到一个OutputStream,链到一个ObjectOutputStream
    // 发送opCode和参数,刷新输出OS
    // 得到InputStream,用于接受返回值
    // 读取返回值,处理异常
    // 完成清理
  } // 捕获处理远程异常
}

用RMI的客户端
public void foClient() {
  try {
    // 查找远程对象(桩)
    // 调用远程对象的方法
    // 捕获和处理远程异常
  }
}

** 可是,查找远程对象这个JNDI查找还是很复杂。我们要想点办法...
** 这时候我们应该使用业务委托.
一个典型的业务委托的伪代码
// 得到请求,并完成一个JNDI查找
// 得到一个桩
// 调用业务方法
// 处理并抽出所有远程异常
// 把返回值发送回给控制器

** 可以看到,在查找服务时,有重复代码。那么我们就用服务定位器来简化业务委托
** 多个业务委托可能使用相同的JNDI服务
** 业务委托也需要提高内聚。不应同时处理注册查找服务
** 服务定位器可以封装查找不同服务的复杂性。要知道,企业bean和普通远程对象所用的查找代码是完全不一样的

** JSP端每请求一次网络对象的属性,就要做一次远程调用。而这个调用是非常耗时的。
有时我们需要权衡,是响应时间更重要,还是数据的实时性?如果是前者,我们可以利用传输对象传送一个串行化的Java对象。一旦传输,传输对象就和其来源完全失去联系了。

MVC控制器:
为了隐藏复杂性,提高内聚度,Struts帮助我们分离了本该在MVC的“控制器”层解决的表单验证和转发/包含等动作处理都分离开来.
以下失Struts的关键组件.
1. Action Servlet : 每个应用一个。Struts会自己提供
2. 表单bean :对于应用需要处理的各个HTML表单,现在要为它们提供一个Java Bean.Action Servlet会自动填充数据和调用validate(),所以可以把数据转换和错误处理逻辑放在这里
3. Action对象: 动作映射到用例中的一个活动.它有一个关键动作execute(),可以在其中获得验证表单参数,并调用模型组件
4. struts-config.xml : 特定的部署文件。需要完成以下映射:
** 请求URL到Action
** Action到表单Bean
** Action到视图

Struts为前端控制器增加的8个特性
** 声明式控制 : Struts允许在请求URL,验证对象,调用模型的对象以及在视图之间建立映射
** 自动请求分派 : Action.execute()返回一个指示性的ActionForward,它告诉ActionServlet要分派到哪个视图
** 数据源管理
** 数十个新的定制标记
** 国际化支持:针对错误类和定制标记
** 声明式验证:将表单的验证剥离开来
** 全局异常处理
** 插件:可以自己改进struts应用

我们来看一下某个实际例子的Struts-config.xml
<struts-config>
  <form-beans>
    <form-bean name="SelectBeerForm" type="com.example.web.BeerSelectForm" /> <!-- 表单bean对象的符号名和类 -->
  </form-beans>
  <action-mappings>
    <action path="/SelectBeer" type="com.example.web.BeerSelectAction" name="SelectBeerForm" scope="request" validate="true" input="/form.jsp">
      <forward name="show_results" path="/result.jsp"/>
    </action> <!-- 将URL路径映射到控制器类 -->
  </action-mapping>
  <message-resources parameter="ApplicationResources" null="false" />
</struts-config>

** 在Web.xml里要指定ActionServlet,拦截所有的请求

关键词(Tag): jsp servlet
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

Way to Webdev:) Head First Servlet & JSP Chap13: 过滤器和包装器

Mystra 发表于 2008-04-24 21:54:32

过滤器: 它有两个作用。
1. 请求发送到Servlet之前,可以用过滤器截获和处理请求
** 可以完成安全检查,重新格式化请求首部或体,建立请求审计或日志,等等
2. 在Servlet结束工作之后,在响应发回给客户之前,可以用过滤器处理响应
** 压缩响应流,追加或修改响应流,创建一个完全不同的响应(不要惊讶,这是很有用的:) )

过滤器在某些方面很像Servlet
1. 过滤器有自己的API.当一个类实现了Filter接口,这个类被允许访问ServletContext,而且可以和其它过滤器链接
2. 容器管理过滤器的生命周期(过滤器有init和destroy)
3. 过滤器也要在DD中声明

一个例子...
package com.example.web;
import java.io.*;
import javax.servlet.*;  // Filter和FilterChain都在这个里面
import javax.servlet.http.*;

public class BeerRequestFilter implements Filter {
  private FilterConfig fc;
  public void init(FilterConfig config)  throws ServletException {
    this.fc = config; // 这几乎是惟一需要做的一件事
  }
 
  public void doFilter( ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
    HttpServletRequest httpReq = (HttpServletRequest) req; // 注意这里是ServletRequest,但是可以确定HttpServletRequest,目前似乎没有非Http的ServletRequest...
    String name= httpReq.getRemoteUser();
    if ( name != null) {
      fc.getServletContext().log("User" + name + "is updating");
    }
    chain.doFilter(req, resp);
  }
  public void destroy()  {
  } // 清理工作,一般为空
}

过滤器是“可入栈”的。过滤器可以通过FilterChain串起来(先进先出),并且在Chain里面可以加入Servlet
声明过滤器
<filter>
  <filter-name>BeerRequest</filter-name>
  <filter-class>com.example.web.BeerRequestFilter</filter-class>
  <init-param>
    <param-name>LogFileName</param-name>
    <param-value>UserLog.txt</param-value>
  </init-param> <!-- 可以有多个init-param -->
</filter>
声明对应URL模式的过滤器映射
<filter-mapping>
  <filter-name>BeerRequest</filter-name>
  <url-pattern>*.do</url-pattern>  <!-- 对于这些URL都要过滤一把 -->
</filter-mapping>
声明对应servlet名的过滤器映射
<filter-mapping>
  <filter-name>BeerRequest</filter-name>
  <servlet-name>AdviceServlet</servlet-name>  <!-- 仅对于此Servlet要过滤 -->
</filter-mapping>

** 如果多个过滤器指定了同样的资源,这些过滤器将以声明顺序来串联处理这些资源
** 我们有必要小心考虑过滤器的顺序. 例如,给JPG加水印的过滤器应该在压缩JPG的过滤器之前

** 坏消息:这样只能过滤来自客户的请求,但是还有一类请求是从转发或包含,请求分派或者错误处理器产生的。
** 好消息:在Servlet规范2.4版本中,过滤器可以应用于请求分派器
例子:
<filter-mapping>
  <filter-name>MonitorFilter</filter-name>
  <url-pattern>*.do</url-pattern>
  <dispatcher>REQUEST</dispatcher>  <!-- 来自用户请求 -->
  <dispatcher>INCLUDE</dispatcher>  <!-- 来自包含 -->
  <dispatcher>FORWARD</dispatcher>  <!-- 来自转发 -->
  <dispatcher>ERROR</dispatcher>   <!-- 来自错误处理器 -->
</filter-mapping>

响应过滤器:
现在我们可以理解那个栈了。在过滤器的栈中,先压栈的是请求过滤器,然后是Servlet,再然后是响应过滤器(注意一般是在doFilter最后才执行chain.doFilter的!)
但是问题在于,等到过滤器想去处理输出时,Servlet已经将输出flush掉,再也来不及处理了.

为了解决这个问题,我们可以把一个虚拟的输出流传给Servlet(Servlet可不知道这是个假输出,这个输出流不会把响应刷新,而是缓存起来)我们可以由输出流继续处理这些输出
为了不要实现我们自己的HttpServletResponse(太复杂了)...我们使用几个Wrapper(这就是包装器!)
** ServletRequestWrapper / HttpServletRequestWrapper
** ServletResponseWrapper / HttpServletResponseWrapper
一个压缩输出流过滤器的例子
class CompressionResponseWrapper extends HttpServletResponseWrapper {
  public ServletOutputStream getOutputStream throws... {
     ...
     servletGzipOS = new GzipSOS(resp.getOutputStream());
     return servletGZipOS;
  }
}
class MyCompressFilter implements Filter {
  public void init(FilterConfig) cfg { }
  public void doFilter (request,response chain) {
    CompressionResponseWrapper wrappedResp = new CompressionResponseWrapper(response);
    wrappedResp.setHeader("Content-Encoding", "gzip");
    fc.doFilter(request, wrappedResp);
    GZIPOutputStream gzos = wrappedResp.getGZIPOutputStream();
    gzos.finish(); // 由于Servlet不再担任刷新输出的责任,我们这里要主动刷新.
    chain.doFilter(request, wrappedResp); // 现在沿过滤器链接,传递的就是这个伪输出流了...
  }
  public void destroy(){}
}

** HTTP拿到首部信息为Content-Encoding : gzip,它会在显示数据之前将其解压缩。

关键词(Tag): jsp servlet
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

Way to Webdev:) Head First Servlet & JSP Chap12: Web应用安全

Mystra 发表于 2008-04-23 20:44:19

Servlet安全的四大要素:
1. 认证(抵挡假冒者) : 确认你的身份
2. 授权(抵挡非放升级者) : 确认你所在的身份有访问响应资源的权限
3. 机密性(抵挡窃听者 ) : 确认你发送的数据不会被窃听
4. 数据完整性(抵挡篡改者) : 确认你发送的数据不会被篡改

认证的基本过程 ...
1. 浏览器请求一个Web资源
2. 服务器确定此资源是受限的
3. 容器发回一个HTTP401(Unauthorized)
4. 浏览器得到401,要求用户提供用户名和口令
5. 浏览器再次请求同一资源,但还包括一个安全HTTP首部以及用户名和口令
6. 容器检验用户名和口令是否匹配
7. 如果不匹配,返回401.如果匹配,检查此角色是否被允许访问此资源,如果可以,把资源返回给客户

** Servlet中可以实现安全,但不好。安全应有部署人员管理

授权:
Tomcat的安全领域
tomcat-users.xml用于将用户映射到角色。当然,这个设置是容器实现相关的
<tomcat-users>
  <role rolename="Guest"/>
  <role rolename="Member"/>
  <user username="Bill" password="coder" roles="Member,Guest">
  ....
</tomcat-users>
1. 授权的第一步就是像上面的片断一样定义角色。
2. 定义资源/方法约束
<web-app>
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>UpdateRecipes</web-resource-name> <!--内部名称-->
      <url-pattern>/Beer/AddRecipe/*</url-pattern> <!-- 这里的命名规则和Servlet映射一样 -->
      <url-pattern>/Beer/ReviewRecipe/*</url-pattern>
      <http-method>GET</http-method>
      <http-method>POST</http-method>
    <web-resource-collection>
    <auth-constraint>  <!-- 不要被名字迷惑,这里是可以访问的角色列表 -->
      <role-name>Admin</role-name>
      <role-name>Member</role-name>
    </auth-constraint>
  </security-constraint>
</web-app>
** 这里的受限资源的实质是"对于HTTP GET来说,这是一个受限资源"
** 如果指定一个<http-method>元素,那么未指定的所有HTTP方法都是不受约束的。当然,如果一个http-method都没有指定,那么全部http方法都不能访问
** 别误会...这些资源只是客户不能直接访问而已,Web应用内的其它页面还是可以访问的
** 如果没有auth-constraint,容器允许不经认证就访问这些URL

<auth-constraint>规则:
** 如果没有说哪些角色受约束(即根本没有auth-constraint标签),那么任何角色都不受约束
** 如果放了一个空auth-constraint标签,那么任何角色都不能访问
** 如果两个web-resource-collection指定了相同的资源,却设定了不同的role访问权限,那么结果是两个auth-constraint元素中所有角色的并集都可以访问
** 上一条规则有一个特例,如果至少一个auth-constraint元素是空元素,那么它对应的资源肯定不能被任何人访问(即使其它auth-constraint设定它可以被访问)

程序式安全
// 在Servlet里面
if ( request.isUserInRole("Manager")) { // 硬编码,当然是不好的,没有泛用性!
 ...
}
** getRemoteUser : 可以用于检查认证状态

在DD中,我们可以把Servlet中硬编码的角色映射到我们自己定义的角色
<web-app...>
  <servlet>
    <security-role-ref>
      <role-name>Manager</role-name>
      <role-link>Admin</role-link>
    </security-role-ref>
  </servlet>
</web-app>
<web-app...>
  <security-role>
    <role-name>Admin</role-name>
  </security-role>
</web-app> ....
** 就算程序设的角色名确实与一个实际的用户匹配,容器也会使用<security-role-ref>映射

认证
容器可以支持4种认证类型。它们的主要区别是所传输的用户名和口令信息有多安全.
1. BASIC : 以一种未加密的编码(base64)传输
2. DIGEST : 以加密形式传输
3. CLIENT-CERT : 非常安全,但是客户要有证书
4. FORM :允许自定义登陆表单,但是最不安全。用户名和口令都在HTTP请求中发回,而且没有加密

要使用认证,只要在DD中声明认证机制就可以
<web-app...>
 ...
 <login-config>
   <auth-method>BASIC</auth-method> <!-- 或者DIGEST,CLIENT-CERT -->
 </login-config>
</web-app>
表单略微复杂一些
<web-app...>
 ...
 <login-config>
   <auth-method>FORM</auth-method>
   <form-login-config>
     <form-login-page>/loginPage.html</form-login-page>
     <form-error-page>/loginError.html</form-login-page>
   </form-login-config>
 </login-config>
</web-app>
同时,我们要创建DD里声明的两个HTML。。。
<!-- loginPage.html -->
<form method="POST" action="j_security_check">
  <input type="text" name="j_username">
  <input type="password" name="j_password">  <!-- 这三个以j打头的input都是系统要求如此命名的 -->
  <input type="submit" value="Enter">
</form>
** loginError.html就是普通的HTML

保护正在传输的数据,解决之道是使用HTTPS
** HTTPS协议是基于TCP协议之上的SSL来传输的。即使被监听,也无法读取出有效的信息。
** 以声明方式防御式的实现数据传输的安全性
<web-app>
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>UpdateRecipes</web-resource-name> <!--内部名称-->
      <url-pattern>/Beer/AddRecipe/*</url-pattern> <!-- 这里的命名规则和Servlet映射一样 -->
      <url-pattern>/Beer/ReviewRecipe/*</url-pattern>
      <http-method>GET</http-method>
      <http-method>POST</http-method>
    <web-resource-collection>
    <auth-constraint>  <!-- 不要被名字迷惑,这里是可以访问的角色列表 -->
      <role-name>Admin</role-name>
      <role-name>Member</role-name>
    </auth-constraint>
    <user-data-constraint>
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
  </security-constraint>
</web-app>

** transport-guarantee可以取3个值:
NONE:默认值,没有数据保护
INTEGRAL:数据在传输过程中不能更改
CONFIDENTIAL:数据在传输过程中不能被看到(当然更不能改)
** 容器会在适当的时候让浏览器以https协议发送用户名/密码等关键信息
** 这个动作会以容器向用户发送一个301(REDIRECT)的方式实现。

关键词(Tag): jsp servlet
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

Way to Webdev:) Head First Servlet & JSP Chap11: Web应用部署

Mystra 发表于 2008-04-21 20:43:39

先复习一下各类文件放在哪里...
1. 静态内容和JSP : 应用目录的任意子目录(可以直接访问)或者WEB-INF的任何子目录(客户不能直接访问,但可以由其它文件包含)
2. Servlet : 应用目录/classes
3. 定制标记处理器类 : 应用目录/classes
4. TLD: WEB-INF的子目录或者应用目录/lib/任意jar文件的META-INF及其子目录下
5. JavaBean类 : 应用目录/classes
6. web.xml : WEB-INF下
7. Jar文件 : 应用目录/lib

WAR(web archive,web归档) : WAR实际就是一个JAR.
WAR中可以在META-INF/MANIFEST.MF中声明库和类的依赖性,这样在部署时就能检查容器能否找到应用依赖的包和类
** 如果容器需要寻找一个类,会先查找WEB-INF/classes下面的类,然后再寻找WEB-INF/lib中的jar文件

Servlet映射方式
完全匹配 : <url-pattern>/Beer/SelectBeer.do</url-pattern>
目录匹配 : <url-pattern>/Beer/*</url-pattern> <!-- 仍然必须要以/开头!! -->
扩展名匹配 : <url-pattern>*.do</url-pattern> <!-- 不能以/开头!! -->
如果一个URL同时可以被几种方法匹配,那么顺序是:完全匹配-->目录匹配-->扩展名匹配
** 记住,http://.../test/fooStuff/bar/和http://.../test/fooStuff/bar是不一样的!!前者可以被<url-pattern>/fooStuff/bar/*</url-pattern>匹配,后者不行!

DD中配置欢迎文件:
<web-app....>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
</web-app>
在用户访问一个路径,但是没有直接映射时,容器会按list中的顺序查找该路径是否有welcome-file-list中声明的页面。如果list中的页面都没有存在,那么行为是容器自定义的,可能抛出一个404或者显示该目录下文件等等

DD中配置错误文件
1. 声明普遍性的错误页面,应用于应用内所有的资源,而不只是JSP
<error-page>
  <exception-type>java.lang.Throwable</exception-type>
  <location>/errorPage.jsp</location>
</error-page>
2. 针对特定异常的的错误页面
<error-page>
  <exception-type>java.lang.ArithmeticException</exception-type>
  <location>/arithmeticErrorPage.jsp</location>
</error-page>
3. 针对特定HTTP错误码的错误页面
<error-page>
  <error-code>404</error-code>
  <location>/notFoundErrorPage.jsp</location>
</error-page>
** 异常要使用完全限定类名
** 不能同时指定error-code和exception-type
** 可以用HttpServletResponse.sendError来人为制造异常

配置Servlet的初始化
一旦Servlet有一个<load-on-startup>的子元素,且其值为非0,那么Servlet将在启动时而非第一次访问时被加载
** load-on-startup越接近1,越先被加载
** 如果load-on-startup值一样,那么按照servlet被声明的顺序加载

JSP文档:
为了使JSP完全符合XML格式,采取了以下映射
指令(除Taglib)  <%@ page import="java.util.*" %>  --> <jsp:directive.page import="java.util.*" />
声明  <%! int y=3; %>  -->  <jsp:declaration> int y=3; </jsp:declaration>
Scriptlet <% list.add("Fred"); %> --> <jsp:scriptlet>list.add("Fred");</jsp:scriptlet>
文本 There is no spoon. --> <jsp:text>There is no spoon</jsp:text>
脚本表达式 <%= it.next() %> --> <jsp:expression>it.next()</jsp:expression>
** 整个jsp必须被包围在一个<jsp:root>中,taglib指令在<jsp:root>里面

关键词(Tag): jsp servlet
收藏: QQ书签 del.icio.us 订阅: Google 抓虾