2014년 4월 7일 월요일

[Servlet/Jsp] headFirst - Ch13 - filter, wrapper


0. 환경

macbook-pro 13” 2012 mid / 10GB
tomcat7
교재: Head First Servlet/Jsp, 한빛미디어, 2012


1. 학습목표

1) filter
2) wrapper


2. filter

필터는 자바 컴포넌트로서 서블릿으로 요청이 넘어가기 전에 요청을 가로채 어떤 처리를 진행

용도

  1. request filter
    • 보안관련 내용 확인
    • 요청헤더와 바디 포멧팅 수정
    • 요청 감시나 기록
  2. response filter
    • 응답 스트림 압축
    • 응답 스트림에 내용 추가 및 수정
    • 완전히 새로운 응답 생성

특징

  1. 필터는 모듈식으로 DD에 설정하고 실행 순서를 설정할 수 있음
  2. 필터가 데이터 형태를 변경하는 경우 그 순서를 지정할 수 있으나 그 의존성을 필터 내부에 삽입할 필요는 없음
  3. filter는 자신의 고유한 API를 가지며 Container는 filter의 API를 앎
  4. Container는 filter의 생명주기를 관리

Filter Interface

javax.servlet.Filter

public interface Filter {
    // init() 메소드는 반드시 구현
    // Container가 Filter를 인스턴스화 할 때 실행
    // Filter 호출 전 뭔가 설정할 것이 있는 경우 (일반적으로 FilterConfig 객체를 내부 field에 저장)
    public void init( FilterConfig ) throws ServletException;

    // 실제 filter를 구현
    public void doFilter ( ServletRequest, ServletResponse, FilterChain ) throws IOException, ServletException;

    // Conteriner가 Filter의 인스턴스를 제거할 때 호출 ( 삭제 전 수행할 코드 )
    public void destroy();
}

FilterChain Interface

  1. DD를 통해 filter의 순서를 지정할 수 있고 FilterChain이 다음에 실행할 filter를 판단함으로써 순차적 진행을 가능하게 함
  2. Filter.doFilter()와 다름
  3. FilterChain.doFilter()는
    • 다음에 호출할 filter가 무엇인지 파악하여 doFilter()를 실행
    • 체인의 마지막 filter인 경우 Servler의 service() 메소드 호출

javax.servlet.FilterChain

public interface FilterChain {
    public void doFilter ( ServletRequest, ServletResponse ) throws IOException, ServletException;
}

개념적인 스택 모델

  1. 요청이 들어오면 Filter A의 doFilter() 메소드 호출
    • doFilter() 메소드에서 FilterChain 인스턴스의 doFilter() 메소드를 만날 때까지 수행
  2. Conatiner는 Filter B의 doFilter() 메소드를 Stack의 제일 위에 쌓음
    • doFilter() 메소드에서 FilterChain 인스턴스의 doFilter() 메소드를 만날 때까지 수행
  3. Container는 Servlet A의 service() 메소드를 Stack의 제일 위에 쌓음
  4. Conatiner는 service() 메소드의 실행 완료시 Stack에서 제거
  5. Container는 Filter B의 doFilter() 메소드로 돌아옴
    • doFilter() 메소드에서 FilterChain 인스턴스의 doFilter() 이후의 로직을 수행
    • 이후 로직에서 response관련 작업
  6. Container는 Filter B의 doFilter() 실행 완료시 Stack에서 제거
  7. Container는 Filter A의 doFilter() 메소드로 돌아옴
    • doFilter() 메소드에서 FilterChain 인스턴스의 doFilter() 이후의 로직을 수행
    • 이후 로직에서 response관련 작업
  8. Container는 Filter A의 doFilter() 실행 완료시 Stack에서 제거
  9. Container는 응답 완료함

requset filter / response filter

// Filter를 구현한 Class 중..
// .
public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) {
    // 요청을 처리
    chain.doFilter ( request, response );
    // 응답을 처리
}
// .

3. request filter

실행 순서 지정

  1. url-pattern으로 일치하는 필터가 체인에 순서대로 들어감
    • 일치하는 모든 필터를 모두 체인에 넣고 실행
  2. 이후 servlet-name으로 일치하는 필터를 찾아 그 정의된 순서대로 체인에 등록
<web-app ...>
    <servlet> ... </servlet>

    <!-- filter-name, filter-class는 반드시 필요한 항목 -->
    <!-- init-param은 옵션 -->
    <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>
    </filter>

    <!-- filter-mapping 선언규칙 -->
    <!-- filter-name은 반드시 필요하며, filter 선언을 찾기 위해 필요 -->
    <!-- url-pattern, servlet-name 중 하나는 반드시 필요 -->
    <filter-mapping>
        <filter-name>BeerRequest</filter-name>
        <!-- url-pattern은 어떤 웹 어플리케이션 리소들에 필터를 적용할지 정의 -->
        <url-patterm>*.do</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>BeerRequest</filter-name>
        <!-- 필터를 적용할 웹 어플리케이션 하나를 정의 -->
        <servlet-name>AdviceServlet</servlet-name>
    </filter-mapping>
</web-app>

RequestDispatcher 요청시

forward, include, request dispatch, error를 통해 웹 리소스를 요청하는 경우에도 필터 적용 가능

<filter-mapping>
    <filter-name>MonitorFilter</filter-name>
    <url-pattern>*.do</url-pattern>
    <!-- dispatcher 항목을 0 .. 4까지 포함 가능 -->
    <!-- request는 client가 요청할 때 적용한다는 의미 -->
    <!-- dispatcher 항목이 없는 경우 request가 DEFAULT -->
    <dispatcher>request</dispatcher>
        그리고 / 또는
    <!-- include는 client가 포함될 때 적용 -->
    <dispatcher>include</dispatcher>
        그리고 / 또는
    <!-- forward는 client가 요청을 다른 곳으로 넘길 때 적용 -->
    <dispatcher>forward</dispatcher>
        그리고 / 또는
    <!-- error는 error 핸들러는 호출했을 때 적용 -->
    <dispatcher>error</dispatcher>
</filter-mapping>

4. response filter

심각한 문제점

  1. filter는 Servlet으로 request, response객체를 전달
    • Servlet에게 다시 response 객체를 전달받기를 기다림
  2. Servlet은 자신이 받은 인자가 filtering 되었다는 사실을 모름
    • 출력에 대한 제어는 Container가 가져감
    • Container는 결과를 Client에게 전달
  3. filter는 response 객체를 전달받기 위해 계속 기다림

해결책

4가지의 wrapper class를 사용하여 Response 객체를 새롭게 정의한 후 Servlet에게 전달
Servlet에서 새 Response 객체에 기록시 Client로 전달되기 전에 제어 가능


5. Wrapper

랩퍼 클래스는 현재 구현하려는 타입의 객체를 내부에 가짐
모든 메소드 호출을 위임할 동일한 타입의 클래스 객체에 대한 참조를 내부에서 가짐


Written with Dec7.

댓글 없음:

댓글 쓰기