我尝试创建一个多租户REST API。
Java EE 7
应用-服务器: WildFly-Swarm 2017.11.0
JAX-RS: wildfly-swarm-weld
其目的是从查询参数中获取租户(德语Mandant)名称,并设置租户的名称以触发代理EntityManager。
这是TOMAS DVORAK的基本概念:https://www.tomas-dvorak.cz/posts/jpa-multitenancy/
我很难使用拦截器,正如标题中所说,我需要用HTTP代码和JSON错误消息中止被拦截的REST请求的响应。
我不能使用筛选器,因为EE正在使用另一个带有筛选器的线程,并且我无法通过ThreadLocal传递租户名称。
我很难获得响应对象。
以下是我到目前为止编写的代码:
import javax.inject.Inject;
import javax.interceptor.*;
import javax.servlet.http.*;
import org.multitenancy.test.beans.*;
/**
* Wrap every call with tenant identification, detected from list of parameters
* of called method.
*/
@Interceptor
public class TenantInterceptor
{
@Inject
private TenantRegistry tenantRegistry;
@Inject
HttpServletRequest servletRequest;
@AroundInvoke
public Object wrapWithTenant(final InvocationContext ctx) throws Exception
{
System.out.println("wrapWithTenant() Called");
printParameter();
if (servletRequest.getParameterMap().containsKey("mandant"))
{
String mandantNameReq = servletRequest.getParameterMap().get("mandant")[0];
if (tenantRegistry.verifyMandantByName(mandantNameReq))
{
System.out.println(mandantNameReq + " is verified");
final String oldValue = TenantHolder.getCurrentTenant();
System.out.println("old value " + oldValue);
try
{
TenantHolder.setTenant(mandantNameReq);
System.out.println("Mandant gesetzt: " + mandantNameReq);
return ctx.proceed();
}
finally
{
if (oldValue != null)
{
TenantHolder.setTenant(oldValue);
}
else
{
TenantHolder.cleanupTenant();
}
}
}
else
{
//TODO: Response einbauen
// containerRequestContext.abortWith(
// Response.status(Response.Status.BAD_REQUEST)
// .entity(new ApiError("Mandant not found"))
// .type(MediaType.APPLICATION_JSON)
// .build());
}
}
else
{
// containerRequestContext.abortWith(
// Response.status(Response.Status.BAD_REQUEST)
// .entity(new ApiError("Parameter doesnt contain mandant"))
// .type(MediaType.APPLICATION_JSON)
// .build());
}
return null;
}
private void printParameter()
{
if (servletRequest.getParameterMap().isEmpty())
{
System.out.println("No Properties given");
}
else
{
for (String key : servletRequest.getParameterMap().keySet())
{
for (String val : servletRequest.getParameterValues(key))
{
System.out.println(key + "\t" + val);
}
}
}
}
}提前感谢:)
发布于 2018-01-03 01:59:13
我会避免将内容存储在线程局部变量中,因为这取决于容器实现是否使用相同的线程进行请求分派。它可以在一个容器中工作,但不能在另一个容器中工作。
我会做以下事情:
1)为租户创建一个可注入的bean定义:
@javax.enterprise.inject.Produces
Tentant tenant(HttpServletRequest request) throws TenantNotFoundException {
// logic
}2) JAX-RS中的异常处理
@javax.ws.rs.ext.Provider
public class TenantNotFoundExceptionMapper implements javax.ws.rs.ext.ExceptionMapper<TenantExceptionNotFound> {
public Response toResponse(TenantNotFoundException exception) {
// your 400 response here
}
}3)将租户注入您的业务逻辑
@Path("/foo")
public class Foo {
@javax.inject.Inject
private Tenant tenant
}发布于 2018-01-11 22:46:45
我无法使用筛选器,因为EE正在使用另一个带有筛选器的线程,并且我无法通过ThreadLocal传递租户名称。
为何会这样呢?您如何访问该应用程序?如果通过REST访问它,这是应用程序的入口点,从此就可以访问ThreadLocal了。重要的是,您将@Priority设置为一个较低的值,以便过滤器成为最先执行的过滤器之一。
我们在应用程序中使用了类似的方法,将租户标识符保存在ThreadLocal中
@Provider
@Priority(1)
public class TenantFilter implements ContainerRequestFilter
{
private static final Logger LOG = LoggerFactory.getLogger(TenantFilter.class);
private static final String TENANT_IDENTIFIER = "mandant";
@Inject
private TenantRegistry tenantRegistry;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
List<String> identifierHeader = requestContext.getHeaders().get(TENANT_IDENTIFIER);
if ((identifierHeader == null || identifierHeader.isEmpty()))
{
LOG.error("No header " + TENANT_IDENTIFIER + " found. Access denied. [path: " + path + "]");
accessForbidden(requestContext);
return;
}
String tenantIdentifier = identifierHeader.get(0);
if (tenantRegistry.verifyMandantByName(tenantIdentifier)) {
LOG.trace("Filtering request for " + TENANT_IDENTIFIER + " header, using: " + tenantIdentifier);
TenantIdentifierResolver.tenantIdentifier.set(tenantIdentifier);
}
}
private void accessForbidden(ContainerRequestContext requestContext)
{
Response accessForbidden = Response.status(Status.FORBIDDEN).build();
requestContext.abortWith(accessForbidden);
}
}TenantIdentifierResolver是一个保存tenand标识符的线程本地,Hibernate使用它为数据库访问设置正确的模式访问。
https://stackoverflow.com/questions/48049799
复制相似问题