序文
http リクエストにパラメータを入力するには、json 文字列、フォーム フォーム、uri でのパラメータの結合など、さまざまな方法があります。SpringBoot では、インターフェイスが受け取る任意のタイプのパラメータに対応する実装メソッドがあります。たとえば、@RequestBody アノテーションで装飾された正式パラメータは、JSON 入力パラメータを受け取りますが、他のタイプの入力パラメータを受け取ることはできません。そうでない場合は、「サポートされていないメディア タイプ」エラーが報告されます。 。プロジェクトで複数の入力パラメーターを受け取ることができるインターフェイスの実装が必要な場合、それはどのように実装されるべきですか?
要件を実現する方法の説明
SpringBoot で入力パラメータを受け取る各メソッドの原理は、フレームワークが対応するパラメータ パーサーをカプセル化することです。パラメータ解析の原理については、私の記事を参照してください。例
:
- RequestResponseBodyMethodProcessor パーサーは、パラメーターに@RequestBodyアノテーションが付けられ、Content-Type が application/json であるリクエストを解析します。詳細については、「RequestResponseBodyMethodProcessor ソース コード」を参照してください。
- ServletModelAttributeMethodProcessor パーサーは、Content-Type が application/x-www-form-urlencoded であるリクエストを解析します。詳細については、ServletModelAttributeMethodProcessor ソース コードを参照してください。
カスタム パラメーター パーサーを介して実装します。具体的な実装手順は次のとおりです。
- アノテーションをカスタマイズします。このアノテーションを持つ仮パラメータは、json、フォームフォーム、スプライシング URI パラメータをパラメータとして受け入れることができます。
- パラメータ パーサーをカスタマイズし、要求された Content-Type に応じてパラメータの受け渡し方法を判断し、対応するパッケージ化されたパーサーを使用して解析します (パラメータ解析のロジックを自分で実装することもできますが、JSON 文字列またはフォーム パラメータを解析するロジックはより複雑なので、ここでは使用しません。たとえば、既製のものを直接使用します)
- カスタム パーサーを IOC コンテナに登録する
コード実装ロジック
- カスタム注釈
import java.lang.annotation.*;
@Target({
ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestData {
String value() default "";
/**
* 参数是否是必填的,默认false,可以设置为true
*/
boolean required() default false;
}
- カスタムパラメータパーサー
import com.alibaba.fastjson.JSONObject;
import com.changgou.item.annotation.RequestData;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
public class RequestHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
/**
* 解析Content-Type为application/json的默认解析器是RequestResponseBodyMethodProcessor
*/
private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor;
/**
* 解析Content-Type为application/x-www-form-urlencoded的默认解析器是ServletModelAttributeMethodProcessor
*/
private ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor;
public RequestHandlerMethodArgumentResolver(RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor,
ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor) {
this.requestResponseBodyMethodProcessor = requestResponseBodyMethodProcessor;
this.servletModelAttributeMethodProcessor = servletModelAttributeMethodProcessor;
}
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.hasMethodAnnotation(RequestData.class);
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
final String applicationXwwwFormUrlencoded = MediaType.APPLICATION_FORM_URLENCODED_VALUE;
HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
if (request == null) {
throw new RuntimeException(" request must not be null!");
}
String contentType = request.getContentType();
// 获取uri上的参数
Map<String, String[]> params = request.getParameterMap();
Map<String, Object> paramMap = new HashMap<>();
if (!CollectionUtils.isEmpty(params)) {
for (Map.Entry<String, String[]> entry : params.entrySet()) {
String key = entry.getKey();
String[] value = entry.getValue();
if (value != null && value.length == 1) {
paramMap.put(key, value[0]);
}
}
}
// 如果入参是拼接在uri上,则直接把拼在uri的参数封装成一个对象
if (StringUtils.isBlank(contentType) && !CollectionUtils.isEmpty(paramMap)) {
Class classType = methodParameter.getParameterType();
Object model = Map.class.isAssignableFrom(classType) ?
paramMap : JSONObject.parseObject(JSONObject.toJSONString(paramMap), classType);
return model;
}
/*
* 如果ContentType是application/x-www-form-urlencoded,那么使用ServletModelAttributeMethodProcessor解析器
*
* 注:其实默认的,当系统识别到参数前有@RequestBody注解时,就会走RequestResponseBodyMethodProcessor解析器;这里就
* 相当于在走默认的解析器前走了个判断而已。
*/
if (applicationXwwwFormUrlencoded.equals(contentType)) {
return servletModelAttributeMethodProcessor.resolveArgument(methodParameter,
modelAndViewContainer, nativeWebRequest, webDataBinderFactory);
}
return requestResponseBodyMethodProcessor.resolveArgument(methodParameter,
modelAndViewContainer, nativeWebRequest, webDataBinderFactory);
}
}
- パラメータリゾルバはIOCコンテナに登録されます
import com.changgou.item.argumentresolver.RequestHandlerMethodArgumentResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class ArgumentResolversConfig {
private final RequestMappingHandlerAdapter requestMappingHandlerAdapter;
public ArgumentResolversConfig(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
this.requestMappingHandlerAdapter = requestMappingHandlerAdapter;
}
@PostConstruct
private void addArgumentResolvers() {
// 获取到的是不可变的集合
List<HandlerMethodArgumentResolver> argumentResolvers =
requestMappingHandlerAdapter.getArgumentResolvers();
RequestHandlerMethodArgumentResolver requestHandlerMethodArgumentResolver =
getRequestHandlerMethodArgumentResolver(argumentResolvers);
// 获取到的是不可变的集合,所以我们需要新建一个集合来放置参数解析器
List<HandlerMethodArgumentResolver> myArgumentResolvers =
new ArrayList<>(argumentResolvers.size() + 1);
// 将自定义的解析器,放置在第一个; 并保留原来的解析器
myArgumentResolvers.add(requestHandlerMethodArgumentResolver);
myArgumentResolvers.addAll(argumentResolvers);
requestMappingHandlerAdapter.setArgumentResolvers(myArgumentResolvers);
}
/**
* 获取MyHandlerMethodArgumentResolver实例
*/
private RequestHandlerMethodArgumentResolver getRequestHandlerMethodArgumentResolver(
List<HandlerMethodArgumentResolver> argumentResolversList) {
// 解析Content-Type为application/json的默认解析器
RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = null;
// 解析Content-Type为application/x-www-form-urlencoded的默认解析器
ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor = null;
for (HandlerMethodArgumentResolver argumentResolver : argumentResolversList) {
if (requestResponseBodyMethodProcessor != null && servletModelAttributeMethodProcessor != null) {
break;
}
if (argumentResolver instanceof RequestResponseBodyMethodProcessor) {
requestResponseBodyMethodProcessor = (RequestResponseBodyMethodProcessor) argumentResolver;
continue;
}
if (argumentResolver instanceof ServletModelAttributeMethodProcessor) {
servletModelAttributeMethodProcessor = (ServletModelAttributeMethodProcessor) argumentResolver;
}
}
if (requestResponseBodyMethodProcessor == null || servletModelAttributeMethodProcessor == null) {
throw new RuntimeException("requestResponseBodyMethodProcessor and "
+ " servletModelAttributeMethodProcessor must not be null!");
}
return new RequestHandlerMethodArgumentResolver(requestResponseBodyMethodProcessor,
servletModelAttributeMethodProcessor);
}
}