MethodArgumentResolver example
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package io.reflectoring.argumentresolver;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ArgumentresolverApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ArgumentresolverApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.reflectoring.argumentresolver;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
|
||||
@ControllerAdvice
|
||||
class ErrorHandler {
|
||||
|
||||
@ExceptionHandler(HttpStatusCodeException.class)
|
||||
ResponseEntity<?> handleHttpStatusCodeException(HttpStatusCodeException e) {
|
||||
return ResponseEntity.status(e.getStatusCode()).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package io.reflectoring.argumentresolver;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
|
||||
public class NotFoundException extends HttpStatusCodeException {
|
||||
|
||||
protected NotFoundException() {
|
||||
super(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.reflectoring.argumentresolver;
|
||||
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
public class Repository {
|
||||
|
||||
private final Long id;
|
||||
private final String slug;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package io.reflectoring.argumentresolver;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
class RepositoryArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private static final Pattern SLUG_PATTERN = Pattern.compile("^/([^/]*).*$");
|
||||
|
||||
private final RepositoryFinder repositoryFinder;
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.getParameter().getType() == Repository.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(
|
||||
MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) {
|
||||
|
||||
String requestPath = ((ServletWebRequest) webRequest).getRequest().getPathInfo();
|
||||
|
||||
Matcher matcher = SLUG_PATTERN.matcher(requestPath);
|
||||
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Cannot resolve argument of type Site. Expecting the slug to be the first part of the request path (%s).",
|
||||
requestPath));
|
||||
}
|
||||
|
||||
String slug = matcher.group(1);
|
||||
if (slug == null || slug.isBlank()) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Cannot resolve argument of type Site. Slug is empty (request path: %s).",
|
||||
requestPath));
|
||||
}
|
||||
|
||||
Optional<Repository> repository = repositoryFinder.findBySlug(slug);
|
||||
|
||||
if (repository.isEmpty()) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return repository.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.reflectoring.argumentresolver;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
class RepositoryArgumentResolverConfiguration implements WebMvcConfigurer {
|
||||
|
||||
private final RepositoryFinder repositoryFinder;
|
||||
|
||||
@Override
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
|
||||
resolvers.add(new RepositoryArgumentResolver(repositoryFinder));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package io.reflectoring.argumentresolver;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface RepositoryFinder {
|
||||
|
||||
Optional<Repository> findBySlug(String slug);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package io.reflectoring.argumentresolver;
|
||||
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
@WebMvcTest(controllers = TestController.class)
|
||||
class RepositoryArgumentResolverTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
private RepositoryFinder repositoryFinder;
|
||||
|
||||
@Test
|
||||
void resolvesSiteSuccessfully() throws Exception {
|
||||
|
||||
given(repositoryFinder.findBySlug("my-repo"))
|
||||
.willReturn(Optional.of(new Repository(1L, "my-repo")));
|
||||
|
||||
mockMvc.perform(get("/my-repo/foo"))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
void notFoundOnUnknownSlug() throws Exception {
|
||||
|
||||
given(repositoryFinder.findBySlug("unknownSlug"))
|
||||
.willReturn(Optional.empty());
|
||||
|
||||
mockMvc.perform(get("/unknownSlug/foo"))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.reflectoring.argumentresolver;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
class TestController {
|
||||
|
||||
@GetMapping("/{slug}/foo")
|
||||
String getSomething(Repository repository) {
|
||||
assertThat(repository.getId()).isEqualTo(1L);
|
||||
return "test";
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user