SpringBoot: Interceptor para leer campo particular de la solicitud y la puso en la respuesta

daltonfury42:

Todas las solicitudes y respuestas manejadas por nuestro controlador resorte resto tiene una sección común, que tiene ciertos valores:

{
    "common": {
        "requestId": "foo-bar-123",
        "otherKey1": "value1",
        "otherKey2": "value2",
        "otherKey3": "value3"
    },
    ...
}

Actualmente todas mis funciones de controlador están leyendo el commony lo copia en la respuesta manualmente. Me gustaría moverlo a un interceptor de algún tipo.

Traté de hacer esto utilizando ControllerAdvicey ThreadLocal:

@ControllerAdvice
public class RequestResponseAdvice extends RequestBodyAdviceAdapter
    implements ResponseBodyAdvice<MyGenericPojo> {

  private ThreadLocal<Common> commonThreadLocal = new ThreadLocal<>();

  /* Request */

  @Override
  public boolean supports(
      MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
    return MyGenericPojo.class.isAssignableFrom(methodParameter.getParameterType());
  }

  @Override
  public Object afterBodyRead(
      Object body,
      HttpInputMessage inputMessage,
      MethodParameter parameter,
      Type targetType,
      Class<? extends HttpMessageConverter<?>> converterType) {
    var common = (MyGenericPojo)body.getCommon();
    if (common.getRequestId() == null) {
       common.setRequestId(generateNewRequestId()); 
    }
    commonThreadLocal(common);
    return body;
  }

  /* Response */

  @Override
  public boolean supports(
      MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
    return MyGenericPojo.class.isAssignableFrom(returnType.getParameterType());
  }

  @Override
  public MyGenericPojo beforeBodyWrite(
      MyGenericPojo body,
      MethodParameter returnType,
      MediaType selectedContentType,
      Class<? extends HttpMessageConverter<?>> selectedConverterType,
      ServerHttpRequest request,
      ServerHttpResponse response) {
    body.setCommon(commonThreadLocal.get());
    commonThreadLocal.remove();
    return body;
  }
}

Esto funciona cuando pruebo el envío de una solicitud a la vez. Pero, ¿es garantizado que afterBodyReady beforeBodyWritees llamado en el mismo hilo, cuando están llegando varias solicitudes?

Si no es así, o incluso de otro modo, ¿cuál es la mejor manera de hacer esto?

la compensación:

Creo que no hay necesidad de su propia ThreadLocalpuede utilizar atributos de la petición.

@Override
public Object afterBodyRead(
        Object body,
        HttpInputMessage inputMessage,
        MethodParameter parameter,
        Type targetType,
        Class<? extends HttpMessageConverter<?>> converterType) {

    var common = ((MyGenericPojo) body).getCommon();
    if (common.getRequestId() == null) {
        common.setRequestId(generateNewRequestId());
    }

    Optional.ofNullable((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
            .map(ServletRequestAttributes::getRequest)
            .ifPresent(request -> {request.setAttribute(Common.class.getName(), common);});

    return body;
}


@Override
public MyGenericPojo beforeBodyWrite(
        MyGenericPojo body,
        MethodParameter returnType,
        MediaType selectedContentType,
        Class<? extends HttpMessageConverter<?>> selectedConverterType,
        ServerHttpRequest request,
        ServerHttpResponse response) {

    Optional.ofNullable(RequestContextHolder.getRequestAttributes())
            .map(rc -> rc.getAttribute(Common.class.getName(), RequestAttributes.SCOPE_REQUEST))
            .ifPresent(o -> {
                Common common = (Common) o;
                body.setCommon(common);
            });

    return body;
}

EDITAR

Optionals puede ser reemplazado con

RequestContextHolder.getRequestAttributes().setAttribute(Common.class.getName(),common,RequestAttributes.SCOPE_REQUEST);

RequestContextHolder.getRequestAttributes().getAttribute(Common.class.getName(),RequestAttributes.SCOPE_REQUEST);

EDIT 2

Sobre la seguridad hilo

1) aplicación web Spring basado en servlet estándar tenemos escenario hilo-por-petición. Solicitud es procesada por uno de los subprocesos de trabajo a través de todos los filtros y rutinas. La cadena de procesamiento será ejecutado por el mismo hilo de principio a fin. Así afterBodyReady beforeBodyWritegarantizada para ser ejecutado por el mismo hilo para una determinada solicitud.

2) Su RequestResponseAdvice por sí mismo no tiene estado. Utilizamos RequestContextHolder.getRequestAttributes()que es ThreadLocal y declarada como

private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
        new NamedThreadLocal<>("Request attributes");

Y javadoc ThreadLocal afirma:

su clase proporciona las variables de subproceso local. Estas variables difieren de sus homólogos normales en que cada hilo que accede a uno (a través de su método GET o set) tiene su propia copia, inicializado de forma independiente de la variable.

Así que no veo ningún problema hilo de seguridad en este sulotion.

Supongo que te gusta

Origin http://10.200.1.11:23101/article/api/json?id=447571&siteId=1
Recomendado
Clasificación