programing

스프링 부트 시 컨트롤러에 도달하기 전에 요청 본문을 변경하는 방법

jooyons 2023. 3. 16. 21:19
반응형

스프링 부트 시 컨트롤러에 도달하기 전에 요청 본문을 변경하는 방법

스프링 부트 어플리케이션이 있습니다.나는 모든 포스트 요청의 요청 본문을 변경합니다.요구가 컨트롤러에 도달하기 전에 요청 본문을 변경할 수 있습니까?예를 들어주세요.

다른 방법으로는 HttpServletRequest 객체에 Atribut을 추가하는 방법이 있습니다.그런 다음 컨트롤러 클래스에서 @RequestAttribute 주석을 사용하여 해당 속성을 읽을 수 있습니다.

인터셉터 내

@Component
    public class SimpleInterceptor extends HandlerInterceptorAdapter {

        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws ServletException, IOException {
            String parameter = request.getParameter("parameter");
            if (parameter == "somevalue") {
                request.setAttribute("customAttribute", "value");
            }

            return true;
        }

    }

컨트롤러 내

@RestController
@RequestMapping("")
public class SampleController {


    @RequestMapping(value = "/sample",method = RequestMethod.POST)
    public String work(@RequestBody SampleRequest sampleRequest, @RequestAttribute("customAttribute") String customAttribute) {
        System.out.println(customAttribute);
        return "This works";
    }
}

이는 요청 본문을 변경하지 않는다는 장점이 있습니다.

★★
는 않아요.

정보
컨트롤러의 핸들러 메서드에 도달하기 전에 요청 본문을 변경하는 세 가지 옵션을 알고 있습니다.

  1. 메서드를 호출하기 전에 요청을 변경하려면 AOP를 사용합니다.
  2. HTTP 필터를 만듭니다.
  3. 커스텀 스프링 핸들러 작성가로채기.

이미 스프링 부트를 사용하고 있기 때문에 옵션 3, 커스텀 스프링 핸들러요격기, 자네에게 가장 좋은 선택인 것 같군

스프링 핸들러를 다룬 백등기사에 대한 링크입니다.가로채기.

Baldung을 수 에 대한 . 왜냐하면 당신은 오직 읽기만 할 수 있기 때문입니다.InputStrem returned returned returned returned returned returned returned returned returnedHttpServletRequest★★★★★★★★★★★★★★★★★★.

경우 래퍼 를 만들어야 .HttpServletRequest 핸들러 .인터셉터 또는 커스텀필터(필터가 여기에 가는 방법일 수 있습니다).

래퍼 클래스

  1. 글을 읽어보세요.HttpServletRequest 클래스
  2. 필요에 따라 본문을 수정합니다.
  3. .ByteArrayOutputStream.
  4. toByteArraybyte[]개울에서요.
  5. ByteArrayOutputStream을 닫습니다(리소스로 시도하면 좋습니다).
  6. 「」를 .getInputStream★★★★★★ 。
  7. 를를 wrap wrap byte[]ByteArrayInputStream이 ByteArrayInputStream을 사용할 됩니다.getInputStream 줄기를 을 사용하다

요구를 랩하는 방법

  1. 필터에서 래퍼 클래스를 인스턴스화하고 원래 요청(doFilter 메서드의 매개 변수)을 전달합니다.
  2. 원래 요청이 아닌 chain.doFilter 메서드에 래퍼를 전달합니다.

HTTP 필터를 사용한 답변입니다.

RequestFilter.java

@Component
public class RequestFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        RequestWrapper wrappedRequest = new RequestWrapper((HttpServletRequest) request);
        chain.doFilter(wrappedRequest, response);
    }

    @Override
    public void destroy() {

    }

}

RequestWrapper.java

 public class RequestWrapper extends HttpServletRequestWrapper {
        private final String body;
        private ObjectMapper objectMapper = new ObjectMapper();
    
        public RequestWrapper(HttpServletRequest request) throws IOException {
            // So that other request method behave just like before
            super(request);
    
            StringBuilder stringBuilder = new StringBuilder();
            BufferedReader bufferedReader = null;
            try {
                InputStream inputStream = request.getInputStream();
                if (inputStream != null) {
                    bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                    char[] charBuffer = new char[128];
                    int bytesRead = -1;
                    while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                        stringBuilder.append(charBuffer, 0, bytesRead);
                    }
    
                } else {
                    stringBuilder.append("");
                }
            } catch (IOException ex) {
                throw ex;
            } finally {
                if (bufferedReader != null) {
                    try {
                        bufferedReader.close();
                    } catch (IOException ex) {
                        throw ex;
                    }
                }
            }
            // Store request body content in 'requestBody' variable
            String requestBody = stringBuilder.toString();
            JsonNode jsonNode = objectMapper.readTree(requestBody);
            //TODO -- Update your request body here 
            //Sample
            ((ObjectNode) jsonNode).remove("key");
            // Finally store updated request body content in 'body' variable
            body = jsonNode.toString();
    
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
            ServletInputStream servletInputStream = new ServletInputStream() {
                public int read() throws IOException {
                    return byteArrayInputStream.read();
                }
    
                @Override
                public boolean isFinished() {
                    return false;
                }
    
                @Override
                public boolean isReady() {
                    return false;
                }
    
                @Override
                public void setReadListener(ReadListener listener) {
    
                }
            };
            return servletInputStream;
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(this.getInputStream()));
        }
}

RequestBodyAdvice를 사용하여 이를 실현한 방법은 다음과 같습니다.

  1. RequestBodyAdvice를 구현하는 클래스를 만들고 @ControllerAdvice로 주석을 추가합니다.
@ControllerAdvice
public class CustomRequestBodyAdvice implements RequestBodyAdvice {
  1. 다음 4가지 방법을 구현해야 합니다.

a. support: 여기서는 대상 컨트롤러를 제어할 수 있습니다.또, 요구 본문의 타입을 지정함으로써, 어느 쪽의 요구 본문을 보다 효율적으로 관리할 수 있습니다.

@Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        log.info("In supports() method of {}", getClass().getSimpleName());
        return methodParameter.getContainingClass() == AuthorController.class && type.getTypeName() == AuthorDTO.class.getTypeName();
    }

b. Body Ready 이전

<!-- language: lang-js -->

@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
    log.info("In beforeBodyRead() method of {}", getClass().getSimpleName());
    return httpInputMessage;
}

c. after BodyRead: 요청 본문을 수정할 수 있는 위치입니다.

<!-- language: lang-js -->

@Override
public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
    log.info("In afterBodyRead() method of {}", getClass().getSimpleName());
    if (body instanceof AuthorDTO) {
        AuthorDTO authorDTO = (AuthorDTO) body;
        authorDTO.setName("Test");
        return authorDTO;
    }

    return body;
}

d. 핸들 Empty Body

<!-- language: lang-js -->

@Override
public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
    log.info("In handleEmptyBody() method of {}", getClass().getSimpleName());
    return body;
}

출처 : http://www.javabyexamples.com/quick-guide-to-requestbodyadvice-in-spring-mvc

이에 대한 한 가지 방법은 성찰에 의한 것이다.ProceedingJoinPoint에 메서드로 전달된 args 개체가 포함되어 있습니다.

@Aspect
@Component
public class AopInterceptor {

    @Around(value = "@annotation(xyz.rpolnx.spring.web.poc.annotation.AopIntercepExample)")
    public Object handler(final ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        Object[] args = joinPoint.getArgs();

        Class<?> someClass = args[0].getClass();

        Field field = someClass.getDeclaredField("custom");
        field.setAccessible(true);
        field.set(args[0], "custom");
        field.setAccessible(false);

        return joinPoint.proceed();
    }
}

@RestController
public class SimpleController {

    @PostMapping("/aop")
    @AopIntercepExample
    public Person handleAopIntercept(@RequestBody Person nodes) {
        return nodes;
    }
}

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AopIntercepExample {
}

public class Person {
    private String name;
    private String id;
    private String custom;
}

저도 이 문제가 있었어요.이 스레드가 조금 도움이 되었고 Bijaya Bhaskar Swain보다 더 간단한 솔루션을 올리고 싶었습니다.

package com.thebois.inpassering.adapters.merchant.staffrestrepo;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.thebois.inpassering.adapters.merchant.FilterOrders;
import com.thebois.inpassering.adapters.merchant.facilityownerrestrepo.FacilityOwner;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
import javax.servlet.FilterChain;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.springframework.core.annotation.Order;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;


@Component
@AllArgsConstructor
@Order(value = FilterOrders.STAFF_REQUEST_ORDER)
public class StaffCreationFilter extends OncePerRequestFilter  {

  ObjectMapper objectMapper;

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {
    RequestWrapper modifiedRequest = new RequestWrapper(request);
    filterChain.doFilter(modifiedRequest, response);
  }

  @Override
  protected boolean shouldNotFilter(HttpServletRequest request) {
    return !(request.getServletPath().contains("/staff") && request.getMethod().equals("POST"));
  }

  private class RequestWrapper extends HttpServletRequestWrapper {

    String body;

    public RequestWrapper(HttpServletRequest request) throws IOException {
      super(request);
      String body = new ContentCachingRequestWrapper(request).getReader().lines().collect(Collectors.joining(System.lineSeparator()));
      //get your dto
      Staff staff = objectMapper.readValue(body, Staff.class);

      //edit your dto
      long facilityOwnerId = ((Number) request.getAttribute("facilityOwnerId")).longValue();
      FacilityOwner facilityOwner = FacilityOwner.builder()
          .facilityOwnerId(facilityOwnerId)
          .build();

      Staff modifiedStaff = Staff.builder()
          .facilityOwner(facilityOwner)
          .username(staff.getUsername())
          .password(new BCryptPasswordEncoder().encode(staff.getPassword()))
          .build();

      //save your changes to body
      this.body = objectMapper.writeValueAsString(modifiedStaff);
    }


    @Override
    public ServletInputStream getInputStream() {
      final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));
      ServletInputStream servletInputStream = new ServletInputStream() {
        public int read() {
          return byteArrayInputStream.read();
        }

        @Override
        public boolean isFinished() {
          return false;
        }

        @Override
        public boolean isReady() {
          return true;
        }

        @Override
        public void setReadListener(ReadListener listener) {

        }
      };
      return servletInputStream;
    }

    @Override
    public BufferedReader getReader() throws IOException {
      return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
  }
}

언급URL : https://stackoverflow.com/questions/50932518/how-to-modify-request-body-before-reaching-controller-in-spring-boot

반응형