Spring MVCカスタムメソッドパラメータ解析実戦
9211 ワード
読者に適しています.Spring MVC/Bootを使ってウェブアプリプロジェクトを開発していますが、http requestパラメータ解析方式はフレーム自体が提供する解析器の開発者には適用されません.
ケース: Postインターフェース:/api/v 1/orders 引数:type:string、limit:number 追加の要求:requestのQueryStringとbodyの中でパラメータを同時に携帯する場合、QueryStringを優先的に使用する.たとえば
開発案:
1. Controllerを作成し,HttpServletRequestを用いてパラメータを自主的に解析した.キーコードは以下の通りです
私達の理想的なインターフェースの方法はこうであるべきです.
やってくる、少年.HandlerMethodAgMentResolaverで調べてみますか?
2.まずこのインターフェースの内容を見てみます.
・reolveAgment この方法はパラメータを具体的に扱う方法です.方法の参照は、ターゲットパラメータに関するメタデータparameterを含み、現在要求されているModelAndView容器mavContinerは、現在の要求webRequestは、最後にbinderを作成するための工場クラスの例がある.
じゃ、まだ何を待っていますか?
3.パラメータをNullに変えることができるなら、それを作成できない理由がないでしょう.このインターフェース自体はどのような実現がありますか?私達が使えるものがありますか?スクリーニングを経て、私たちはModelAttributeMethodProcessorという実現類が私たちが探しているものに近いように見えます.私たちのパラメータ解析器を完璧にしましょう.
深さ: 他の多くのシーンは、カスタムパラメータ解析器に適用され、Controller内の方法を開発するのに便利です.例えば、すべてまたはほとんどのインターフェースは、user有効性チェックを行い、方法内でuserオブジェクトを取得する必要があります.私たちはスクリーンショットを実現することによってアメリカの有効性を検証できます.通常この時にアメリカのオブジェクトを入手しました.スクリーンでアメリカのオブジェクトをrequest atributeに入れて、もう一つのパラメータ解析器をrequest atributeから取り出すことができます.このように、私達はControllerの方法声明の中で直接に有効なuserオブジェクトを得ることができます.開発効率を大幅に向上させ、コードの可読性も向上させることができます.
こんなにたくさん話しましたから、自分で試してみてもいいです.少なくともgithubに行って作者に注目してみましょう.
ケース: Postインターフェース:/api/v 1/orders 引数:type:string、limit:number 追加の要求:requestのQueryStringとbodyの中でパラメータを同時に携帯する場合、QueryStringを優先的に使用する.たとえば
curl -X POST 'https://company.com/api/v1/orders?type=Food' -d 'type=Dig&limit=100'
この時解析したパラメータはtype=Food&limit=100であるべきです.開発案:
1. Controllerを作成し,HttpServletRequestを用いてパラメータを自主的に解析した.キーコードは以下の通りです
@RestController
public class OrderController {
@PostMapping("/api/v1/orders")
public List queryOrders(HttpServletRequest request) {
String queryStr = request.getQueryString();
String body = request.getReader().lines.stream()
.collect(Collectors.joining(System.lineSeparator()));
/* map , */
Map paramMap = parseParam(queryStr, body);
String type = paramMap.get("type");
int limit = paramMap.get("limit");
/**
* do your business
*/
return orderList;
}
/**
* other methods
*/
}
よさそうです.しかし、もしたくさんのインターフェースがあれば?インタフェースのパラメータが増えたら?インタフェースのパラメータが変化したら?インタフェースパラメータがチェックされていますか?インターフェースごとにこのように書いていますが、その前の3行は同じですか?各インターフェースのパラメータ名は、mapのkeyでのみ指定できます.パラメータタイプは、mapから取得して強回転します.パラメータチェックは手書きのみです.私の話を聞いて、これはいい案ではないと思いますか?私達の理想的なインターフェースの方法はこうであるべきです.
@RestController
public class OrderController {
@PostMapping("/api/v1/orders")
public List queryOrders(@Valid QueryOrdersParam param) {
return orderService.queryOrders(param.getType(), param.getLimit());
}
/**
* other methods
*/
}
パラメータビームはこう定義されています.@Data
@Builder
public class QueryOrdersParam {
@NotBlank(message = "Type must be given")
private String type;
@Max(value = 1000)
private int limit=100;
}
さわやかに見えますか?パラメータBeanはjavax.validationを使ってフレームのチェックを依頼できることを知っています.Controller法のパラメータはフレームを通して自動的に注入できる.しかし、フレーム解析要求パラメータをビームに注入する過程は私たちの需要とは違っています.どうすればいいですか?やってくる、少年.HandlerMethodAgMentResolaverで調べてみますか?
2.まずこのインターフェースの内容を見てみます.
public interface HandlerMethodArgumentResolver {
/**
* Whether the given {@linkplain MethodParameter method parameter} is
* supported by this resolver.
* @param parameter the method parameter to check
* @return {@code true} if this resolver supports the supplied parameter;
* {@code false} otherwise
*/
boolean supportsParameter(MethodParameter parameter);
/**
* Resolves a method parameter into an argument value from a given request.
* A {@link ModelAndViewContainer} provides access to the model for the
* request. A {@link WebDataBinderFactory} provides a way to create
* a {@link WebDataBinder} instance when needed for data binding and
* type conversion purposes.
* @param parameter the method parameter to resolve. This parameter must
* have previously been passed to {@link #supportsParameter} which must
* have returned {@code true}.
* @param mavContainer the ModelAndViewContainer for the current request
* @param webRequest the current request
* @param binderFactory a factory for creating {@link WebDataBinder} instances
* @return the resolved argument value, or {@code null}
* @throws Exception in case of errors with the preparation of argument values
*/
Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}
・supports Parameeter この方法はブール値を返して、このパラマーが処理する必要があるかどうかを識別します.・reolveAgment この方法はパラメータを具体的に扱う方法です.方法の参照は、ターゲットパラメータに関するメタデータparameterを含み、現在要求されているModelAndView容器mavContinerは、現在の要求webRequestは、最後にbinderを作成するための工場クラスの例がある.
じゃ、まだ何を待っていますか?
@Slf4j
@Component
public class ControllerArgumentsResolver implements HandlerMethodArgumentResolver {
/**
* true
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return true;
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
return null;
}
}
このインターフェースの実現には自分で着手し、Spring MVCフレームをcontent.xmlに注入する必要があります.
Spring Bootフレームであれば符号注入が必要です.@Configuration
public class ArgumentResolversConfig extends WebMvcConfigurerAdapter{
@Override
public void addArgumentResolvers(List argumentResolvers) {
super.addArgumentResolvers(argumentResolvers);
argumentResolvers.add(new ControllerArgumentResolver());
}
}
自分たちのパラメータ解析器を配置してから、インターフェースをテストしてみましょう.@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application-context.xml")
@WebAppConfiguration
@Slf4j
public class ControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setup() {
/* MockMvc */
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void testQueryOrders() {
mockMvc.perform(MockMvcRequestBuilers.post("/api/v1/orders?type=Dig").content("type=Food&limit=10").accept(MedisType.APPLICATION_HSON).andExpect(MockMvcResultMatchers.status.isOk()));
}
}
うん、やっぱりControllerの方法で取ったパラメータBeanはNullです.3.パラメータをNullに変えることができるなら、それを作成できない理由がないでしょう.このインターフェース自体はどのような実現がありますか?私達が使えるものがありますか?スクリーニングを経て、私たちはModelAttributeMethodProcessorという実現類が私たちが探しているものに近いように見えます.私たちのパラメータ解析器を完璧にしましょう.
@Slf4j
@Component
public class ControllerArgumentsResolver implements HandlerMethodArgumentResolver {
/**
* @RequestAttribute 。
* , @Valid 。
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(Valid.class) && parameter
.hasParameterAnnotation(RequestAttribute.class);
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
String name = parameter.getParameterName();
Object attribute = mavContainer.containsAttribute(name) ?
mavContainer.getModel().get(name) :
this.createAttribute(parameter);
/* Controller */
String requestStr = request.getQueryString();
String body = request.getReader().stream().lines().collect(Collectors.joining(System.lineSeparator()));
Map paramMap = parseRequest(requestStr, body);
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
/* */
binder.bind(new MutablePropertyValues(map));
/* validation */
validateIfApplicable(binder, parameter);
/* BindException */
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
Map bindingResultModel = binder.getBindingResult().getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
log.debug("Found argument {} ---> {}", parameter.getParameterName(), attribute);
/* binder Bean */
return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
}
はい、今はControllerの中の方法のパラメータを変えて、私達のカスタムコメントを追加します.@RestController
public class OrderController {
@PostMapping("/api/v1/orders")
public List queryOrders(@RequestAttribute @Valid QueryOrdersParam param) {
return orderService.queryOrders(param.getType(), param.getLimit());
}
/**
* other methods
*/
}
それからテストクラスを走ります.はい、通過します.大功が成る深さ: 他の多くのシーンは、カスタムパラメータ解析器に適用され、Controller内の方法を開発するのに便利です.例えば、すべてまたはほとんどのインターフェースは、user有効性チェックを行い、方法内でuserオブジェクトを取得する必要があります.私たちはスクリーンショットを実現することによってアメリカの有効性を検証できます.通常この時にアメリカのオブジェクトを入手しました.スクリーンでアメリカのオブジェクトをrequest atributeに入れて、もう一つのパラメータ解析器をrequest atributeから取り出すことができます.このように、私達はControllerの方法声明の中で直接に有効なuserオブジェクトを得ることができます.開発効率を大幅に向上させ、コードの可読性も向上させることができます.
こんなにたくさん話しましたから、自分で試してみてもいいです.少なくともgithubに行って作者に注目してみましょう.