61.2. 컨테이너 요청 필터
61.2.1. 개요
이 섹션에서는 서버( 컨테이너) 측에서 들어오는 요청 메시지를 가로채는 데 사용되는 컨테이너 요청 필터 를 구현하고 등록하는 방법을 설명합니다. 컨테이너 요청 필터는 종종 서버 측의 헤더를 처리하는 데 사용되며 모든 종류의 일반 요청 처리(즉, 라는 특정 리소스 메서드와 독립적인 처리)에 사용할 수 있습니다.
또한 컨테이너 요청 필터는 두 개의 고유한 확장 지점( PreMatchContainerRequest (리소스 일치 단계 전) 및 ContainerRequest (리소스 일치 단계 이후)에 설치할 수 있기 때문에 특별한 경우입니다.
61.2.2. ContainerRequestFilter 인터페이스
javax.ws.rs.container.ContainerRequestFilter 인터페이스는 다음과 같이 정의됩니다.
// Java
...
package javax.ws.rs.container;
import java.io.IOException;
public interface ContainerRequestFilter {
public void filter(ContainerRequestContext requestContext) throws IOException;
}
ContainerRequestFilter 인터페이스를 구현하면 서버 측에서 다음 확장 포인트 중 하나에 대한 필터를 생성할 수 있습니다.
-
PreMatchContainerRequest -
ContainerRequest
61.2.3. ContainerRequestContext 인터페이스
ContainerRequestFilter 의 필터 메서드는 들어오는 요청 메시지 및 관련 메타데이터에 액세스하는 데 사용할 수 있는 javax.ws.rs.container.ContainerRequestContext 유형의 단일 인수를 수신합니다. ContainerRequestContext 인터페이스는 다음과 같이 정의됩니다.
// Java
...
package javax.ws.rs.container;
import java.io.InputStream;
import java.net.URI;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo;
public interface ContainerRequestContext {
public Object getProperty(String name);
public Collection getPropertyNames();
public void setProperty(String name, Object object);
public void removeProperty(String name);
public UriInfo getUriInfo();
public void setRequestUri(URI requestUri);
public void setRequestUri(URI baseUri, URI requestUri);
public Request getRequest();
public String getMethod();
public void setMethod(String method);
public MultivaluedMap getHeaders();
public String getHeaderString(String name);
public Date getDate();
public Locale getLanguage();
public int getLength();
public MediaType getMediaType();
public List getAcceptableMediaTypes();
public List getAcceptableLanguages();
public Map getCookies();
public boolean hasEntity();
public InputStream getEntityStream();
public void setEntityStream(InputStream input);
public SecurityContext getSecurityContext();
public void setSecurityContext(SecurityContext context);
public void abortWith(Response response);
}61.2.4. PreMatchContainerRequest 필터의 샘플 구현
PreMatchContainerRequest 확장 지점(즉, 리소스 일치 전에 필터가 실행되는 경우)에 대한 컨테이너 요청 필터를 구현하려면 ContainerRequestFilter 인터페이스를 구현하는 클래스를 정의합니다. @PreMatching 주석으로 클래스에 주석을 답니다( PreMatchContainerRequest 확장 지점을 선택).
예를 들어 다음 코드는 우선순위가 20인 PreMatchContainerRequest 확장 지점에 설치된 간단한 컨테이너 요청 필터의 예를 보여줍니다.
// Java
package org.jboss.fuse.example;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.annotation.Priority;
import javax.ws.rs.ext.Provider;
@PreMatching
@Priority(value = 20)
@Provider
public class SamplePreMatchContainerRequestFilter implements
ContainerRequestFilter {
public SamplePreMatchContainerRequestFilter() {
System.out.println("SamplePreMatchContainerRequestFilter starting up");
}
@Override
public void filter(ContainerRequestContext requestContext) {
System.out.println("SamplePreMatchContainerRequestFilter.filter() invoked");
}
}61.2.5. ContainerRequest 필터의 구현 예
ContainerRequest 확장 지점(즉, 리소스 일치 후 필터가 실행되는 경우)에 대한 컨테이너 요청 필터를 구현하려면 @PreMatching 주석 없이 ContainerRequestFilter 인터페이스를 구현하는 클래스를 정의합니다.
예를 들어 다음 코드는 우선 순위가 30인 ContainerRequest 확장 지점에 설치되는 간단한 컨테이너 요청 필터의 예를 보여줍니다.
// Java
package org.jboss.fuse.example;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.ext.Provider;
import javax.annotation.Priority;
@Provider
@Priority(value = 30)
public class SampleContainerRequestFilter implements ContainerRequestFilter {
public SampleContainerRequestFilter() {
System.out.println("SampleContainerRequestFilter starting up");
}
@Override
public void filter(ContainerRequestContext requestContext) {
System.out.println("SampleContainerRequestFilter.filter() invoked");
}
}61.2.6. ResourceInfo 삽입
ContainerRequest 확장 지점(즉, 일치하는 리소스가 발생한 후 ) ResourceInfo 클래스를 삽입하여 일치하는 리소스 클래스 및 리소스 메서드에 액세스할 수 있습니다. 예를 들어 다음 코드는 ResourceInfo 클래스를 ContainerRequestFilter 클래스의 필드로 삽입하는 방법을 보여줍니다.
// Java
package org.jboss.fuse.example;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.ext.Provider;
import javax.annotation.Priority;
import javax.ws.rs.core.Context;
@Provider
@Priority(value = 30)
public class SampleContainerRequestFilter implements ContainerRequestFilter {
@Context
private ResourceInfo resinfo;
public SampleContainerRequestFilter() {
...
}
@Override
public void filter(ContainerRequestContext requestContext) {
String resourceClass = resinfo.getResourceClass().getName();
String methodName = resinfo.getResourceMethod().getName();
System.out.println("REST invocation bound to resource class: " + resourceClass);
System.out.println("REST invocation bound to resource method: " + methodName);
}
}61.2.7. 호출 중단
컨테이너 요청 필터의 적절한 구현을 생성하여 서버 측 호출을 중단할 수 있습니다. 일반적으로 이 기능은 서버 측에서 보안 기능을 구현하는 데 유용합니다. 예를 들어 인증 기능 또는 권한 부여 기능을 구현하는 데 유용합니다. 들어오는 요청이 성공적으로 인증되지 않으면 컨테이너 요청 필터 내에서 호출을 중단할 수 있습니다.
예를 들어 다음 사전 일치 기능은 URI의 쿼리 매개변수에서 사용자 이름과 암호를 추출하고 인증 방법을 호출하여 사용자 이름 및 암호 자격 증명을 확인합니다. 인증에 실패하면 ContainerRequestContext 오브젝트에서 abortWith 를 호출하여 호출이 중단되고 클라이언트에 반환되는 오류 응답을 전달합니다.
// Java
package org.jboss.fuse.example;
import javax.annotation.Priority;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.Provider;
@PreMatching
@Priority(value = 20)
@Provider
public class SampleAuthenticationRequestFilter implements
ContainerRequestFilter {
public SampleAuthenticationRequestFilter() {
System.out.println("SampleAuthenticationRequestFilter starting up");
}
@Override
public void filter(ContainerRequestContext requestContext) {
ResponseBuilder responseBuilder = null;
Response response = null;
String userName = requestContext.getUriInfo().getQueryParameters().getFirst("UserName");
String password = requestContext.getUriInfo().getQueryParameters().getFirst("Password");
if (authenticate(userName, password) == false) {
responseBuilder = Response.serverError();
response = responseBuilder.status(Status.BAD_REQUEST).build();
requestContext.abortWith(response);
}
}
public boolean authenticate(String userName, String password) {
// Perform authentication of 'user'
...
}
}61.2.8. 서버 요청 필터 바인딩
서버 요청 필터(즉, Apache CXF 런타임에 설치)를 바인딩 하려면 다음 단계를 수행합니다.
다음 코드 조각에 표시된 대로 컨테이너 요청 필터 클래스에
@Provider주석을 추가합니다.// Java package org.jboss.fuse.example; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.ext.Provider; import javax.annotation.Priority; @Provider @Priority(value = 30) public class SampleContainerRequestFilter implements ContainerRequestFilter { ... }
컨테이너 요청 필터 구현이 Apache CXF 런타임에 로드되면 REST 구현에서 로드된 클래스를 자동으로 검색하여
@Provider주석( 검사 단계)으로 표시된 클래스를 검색합니다.XML에서 Cryostat-RS 서버 끝점을 정의할 때(예: 18.1절. “Cryostat-RS 서버 끝점 구성”참조)
jaxrs:providers요소의 공급자 목록에 서버 요청 필터를 추가합니다.<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs" xmlns:cxf="http://cxf.apache.org/blueprint/core" ... > ... <jaxrs:server id="customerService" address="/customers"> ... <jaxrs:providers> <ref bean="filterProvider" /> </jaxrs:providers> <bean id="filterProvider" class="org.jboss.fuse.example.SampleContainerRequestFilter"/> </jaxrs:server> </blueprint>참고이 단계는 Apache CXF의 비표준 요구 사항입니다. Cryostat-RS 표준에 따라
@Provider주석은 필터를 바인딩하는 데 필요한 모든 주석이어야 합니다. 그러나 실제로 표준 접근 방식은 다소 유연하지 않으며 많은 라이브러리가 대규모 프로젝트에 포함될 때 충돌하는 공급자로 이어질 수 있습니다.