13 Ocak 2021 Çarşamba

SpringMVC HandlerMethodArgumentResolver Arayüzü

Giriş
İstekteki parametrelerin bizim metodumuza göre çözümlenmesini sağlar. Normalde spring zaten HTTP isteğini Rest parametre tipine çevirir, ancak burada bizim istediğimiz, HTTP isteğindeki bir header alanını metoda parametre olarak geçmek.

Örnek
Controller sınıfı şöyle olsun
@RestController
@RequestMapping("/api")
public class ExampleController {

  @GetMapping("/context")
  public ContextInformation getApplicationContext(ContextInformation contextInformation) {
    return contextInformation;
  } 
}
Http isteği şöyle olsun
GET /api/context HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8080
User-Agent: HTTPie/2.2.0
X-App-Context: {"parentId":1000,"profile":"ADMIN","userId":1}
Bu isteği kendi RestController sınıfına yönlendirmek için şöyle yaparız
@Component
public class ContextInformationResolver implements HandlerMethodArgumentResolver {

  private final ObjectMapper objectMapper;

  public ContextInformationResolver(ObjectMapper objectMapper) {
    this.objectMapper = objectMapper;
  }

  @Override
  public boolean supportsParameter(MethodParameter methodParameter) {
    return methodParameter.getParameterType().equals(ContextInformation.class);
  }

  @Override
  public Object resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
    NativeWebRequest nativeWebRequest,
WebDataBinderFactory webDataBinderFactory)
throws Exception {
    
    String jsonContext = nativeWebRequest.getHeader("X-App-Context");
    return objectMapper.readValue(jsonContext, ContextInformation.class);
  }
}
HandlerArgumentResolver sınıfını Spring'e eklemek için şöyle yaparız
@Configuration
@EnableWebMvc
class WebMvcContext extends WebMvcConfigurerAdapter {
  
  @Override
  public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    argumentResolvers.add(new ContextInformationResolver ());
  }
}
Diğer

Alternatif 1
Bu sınıfa bir diğer alternatif ise, OncePerRequestFilter kullanarak Http isteği içindeki header alanını HttpServletRequest içindeki attribute map'ine yerleştirmek
Örnek
Elimizde şöyle bir filtre olsun
@Component
public class HeaderProcessorFilter extends OncePerRequestFilter {

  private final RequestUtils requestUtils;

  public HeaderProcessorFilter(RequestUtils requestUtils) {
    this.requestUtils = requestUtils;
  }

  @Override
  public void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
    FilterChain filterChain) throws IOException, ServletException {
 
requestUtils.storeContextInformationAsRequestAttribute(request);
    filterChain.doFilter(request, response);
  }
}
Header değerini attribute değerine atamak için şöyle yaparız
@Component
public class RequestUtils {

  private final ObjectMapper objectMapper;

  private static final String APP_CONTEXT_HEADER = "X-App-Context";
  private static final String REQUEST_ATTRIBUTE_APP_CONTEXT = "appContext";

  public RequestUtils(ObjectMapper objectMapper) {
    this.objectMapper = objectMapper;
  }

  public void storeContextInformationAsRequestAttribute(HttpServletRequest request)
throws IOException {
    String jsonHeader = request.getHeader(APP_CONTEXT_HEADER);

    if (jsonHeader != null) {
      request.setAttribute(REQUEST_ATTRIBUTE_APP_CONTEXT,
        objectMapper.readValue(jsonHeader, ContextInformation.class));
    }
  }
}
Alternatif 2
OncePerRequestFilter kullanarak Http isteği içindeki header alanını HttpServletRequest içindeki attribute map'ine yerleştirilir. Daha bir AOP @Before kullanılarak bir aspect yazılır ve aspect içinde ContextInformation ile bir işlem yapılıyor.

Bu yöntemi kulllanınca Controller'ın metodunda da ContextInformation parametresine gerek kalmıyor.

Hiç yorum yok:

Yorum Gönder