У меня есть контроллер с RequestMapping(foo/{fooId}/bar/})
, FooEntity
используется во всех методах, как я могу предварительно загрузить FooEntity
для всех методов FooController
?
@RestController
@RequestMapping("foo/{fooId}/bar")
public class FooController {
public void prefetchFoo(@PathVariable Long fooId) {
...
}
@PostMapping("/")
public ResponseEntity<void> createFooBar(FooEntity prefetchedFoo) {
...
}
@GetMapping("/{barId}")
public ResponseEntity<void> getFooBar(FooEntity prefetchedFoo) {
...
}
}
В своих исследованиях я нашел @ModelAttribute
, но видел это только в контексте веб-представлений. Можно ли использовать его в контексте API JSON?
Вы можете использовать @ModelAttribute
в контексте API JSON. Метод @ModelAttribute
будет вызываться перед каждым методом обработчика запроса и может заполнить атрибут модели необходимыми данными. Это применимо независимо от того, создает ли ваш API представления или ответы JSON.
@RestController
@RequestMapping("foo/{fooId}/bar")
public class FooController {
@Autowired
private FooService fooService;
@ModelAttribute
public FooEntity prefetchFoo(@PathVariable Long fooId) {
// Logic to fetch FooEntity based on fooId
return fooService.findFooById(fooId);
}
@PostMapping("/")
public ResponseEntity<Void> createFooBar(@ModelAttribute FooEntity prefetchedFoo) {
...
return ResponseEntity.ok().build();
}
@GetMapping("/{barId}")
public ResponseEntity<FooEntity> getFooBar(@PathVariable Long barId, @ModelAttribute FooEntity prefetchedFoo) {
...
return ResponseEntity.ok(prefetchedFoo);
}
}
Используйте свой FooService
@RestController
@RequestMapping("/foo/{fooId}/bar")
public class FooController {
@Autowired
private FooService fooService;
@PostMapping("/")
public ResponseEntity<Void> createFooBar(
@PathVariable Long fooId,
@RequestBody(required = false) SomeRequest request
) {
FooEntity prefetchedFoo = fooService.getFooById(fooId);
System.out.println(prefetchedFoo);
return ResponseEntity.ok().build();
}
@GetMapping("/{barId}")
public ResponseEntity<Void> getFooBar(
@PathVariable Long fooId,
@PathVariable Long barId
) {
FooEntity prefetchedFoo = fooService.getFooById(fooId);
System.out.println("barId " + barId);
System.out.println(prefetchedFoo);
return ResponseEntity.ok().build();
}
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreloadedFoo {
}
HandlerMethodArgumentResolver
— это интерфейс стратегии для преобразования параметров метода в значения аргументов в контексте данного запроса.
supportsParameter()
метод проверяет, поддерживается ли данный параметр метода этим преобразователем.
resolveArgument()
метод преобразует параметр метода в значение аргумента из данного запроса.
@Component
public class FooEntityArgumentResolver implements HandlerMethodArgumentResolver {
@Autowired
private FooService fooService;
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(PreloadedFoo.class) != null
&& parameter.getParameterType().equals(FooEntity.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory
) throws Exception {
// Extract path variable from the request attributes
@SuppressWarnings("unchecked")
Map<String, String> pathVariables = (Map<String, String>) webRequest.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
String fooIdParam = pathVariables != null ? pathVariables.get("fooId") : null;
// use getParameter() if you want to pass the fooId as requestParam
// webRequest.getParameter("requestParam");
if (fooIdParam != null) {
Long fooId = Long.parseLong(fooIdParam);
return fooService.getFooById(fooId);
}
throw new IllegalArgumentException("fooId parameter is required");
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private FooEntityArgumentResolver fooEntityArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(fooEntityArgumentResolver);
}
}
Добавьте в комментариях, если вам нужны дополнительные разъяснения.
Цель предварительной выборки заключается в том, что она будет использоваться во всех методах, поэтому я хочу избежать повторения кода. В чем будут отличия от варианта 2 и использования
@ModelAttribute
?