- суг команда
- Автор: Ты в безопасности
задний план
-
Сценарий 1: внешние и внутренние интерфейсы стыковки, добавление, удаление, изменение и проверка, параметры, запрошенные в начале, в основном представляют собой единый фрагмент данных, а формат json в основном {"ключ":"значение"} . Продукт впоследствии расширяется, и передача параметров становится пакетной операцией. Формат json — [xxx,xxx] или [{"key":"value"}], если серверная часть изменяет принимающий объект исходного интерфейса на массив, а фронтенд и бэкенд выпущены в оттенках серого, будет несовместимость со старой версией
-
Сценарий 2: клиентская часть продукта может состоять из веб-части, ПК и приложения.Например, когда структура параметров интерфейса преобразуется в массив, веб-часть обновляется, но Приложение и сторона ПК не обновляются, поэтому есть несовместимость на другом конце
Решения
- новый интерфейс
-
Достоинства: не влияет на старый интерфейс, сфера влияния небольшая
-
Недостатки: Дублирующийся код, бесполезный интерфейс позже
- Front-end и back-end изначально согласовали параметры запроса массива
-
Достоинства: более фундаментальное решение проблемы
-
Недостатки: Недостаток программистов, не все программисты могут заранее определить тип параметров интерфейса
- В большинстве случаев проблема заключается в том, чтобы решить проблему.Идея заключается в том, что бэкенд перехватывает и обрабатывает полученные параметры запроса.После проверки правильности данные json равномерно инкапсулируются в общий объект или массив.
- Преимущества: нужно только реорганизовать исходный интерфейс, чтобы он был совместим с обоими случаями.
- Недостатки: необходимо настроить синтаксический анализ json, если синтаксический анализ плохой, будет сообщено об ошибке десериализации json
код выше
Вот процесс попытки решить описанный выше сценарий тремя способами.
Определите класс сущности MyBeanVo, который получает внешний интерфейс.
package com.test.config;
public class MyBeanVo {
String value = "";
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
varargs (не могу решить)
Сначала я думал, что переменный параметр Object... в Java может передавать один параметр или несколько параметров при вызове метода, но это не решается. Поскольку переменные параметры на самом деле являются массивами Object[]
@RestController
public class MyController {
@PostMapping("/hello")
public String test(@RequestBody MyBeanVo... param) {
MyBeanVo vo = param[0];
return vo.getValue();
}
}
Сообщается об ошибке при передаче одного параметра: «исключение»: «org.springframework.http.converter.HttpMessageNotReadableException», «message»: «Ошибка синтаксического анализа JSON: не удается десериализовать экземпляр com.test.config.MyBean[] out токена START_OBJECT; вложенным исключением является com.fasterxml.jackson.databind.JsonMappingException: невозможно десериализовать экземпляр com.test.config.MyBean[] из токена START_OBJECT\n в [Источник: java.io.PushbackInputStream@24e6f1b2; строка : 1, столбец: 1]"
Причина: Внешний параметр (отдельные данные) не может быть проанализирован как MyBean[], что требует десериализации Json.
пользовательская десериализация
Вариант первый
Определить класс сущностей партии
package com.test.config;
import java.util.List;
public class BatchVo<T> {
List<T> list;
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
}
Аннотация @JsonComponent будет автоматически внедрена в Spring, а метод десериализации будет автоматически выполняться при десериализации BatchVo
package com.test.config;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import org.springframework.boot.jackson.JsonComponent;
import java.io.IOException;
import java.util.ArrayList;
@JsonComponent
public class MyJsonDeserializer extends JsonDeserializer<BatchVo<MyBeanVo>> {
@Override
public BatchVo<MyBeanVo> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);
BatchVo vo = new BatchVo<MyBeanVo>();
String str = treeNode.toString();
// 前端传参是数组
if (treeNode.isArray()) {
vo.list = JSONObject.parseArray(str, MyBeanVo.class);
}
// 前端传参是单个数据
if (treeNode.isObject()) {
vo.list = new ArrayList();
vo.list.add(JSONObject.parseObject(str, MyBeanVo.class));
}
return vo;
}
}
Связанные параметры должны быть добавлены с помощью @RequestBody, иначе десериализация не может пройти через метод десериализации MyJsonDeserializer.
@RestController
public class MyController {
@PostMapping("/hello")
public String test(@RequestBody BatchVo<MyBeanVo>param) {
MyBeanVo vo = param.getList().get(0);
return vo.getValue();
}
}
Сделайте запрос: POST localhost:8080/hello
параметр body: [{"value":"hello world"}] или {"value":"hello world"}
Возвраты: привет мир
Анализ: очевидно, что такой дизайн, если MyBean не может быть разработан, чтобы быть мощным и универсальным, и может получать все параметры запроса внешнего интерфейса. В противном случае каждый класс Vo должен написать класс синтаксического анализа столбца десериализации, который реализует JsonDeserializer, или должен каждый раз выполнять десериализацию Json на уровне контроллера. Такая реализация становится громоздкой и увеличивает объем кода.
Вариант 2
Анализатор пользовательских параметров Анализатор пользовательских параметров
package com.test.config;
import com.alibaba.fastjson.JSON;
import org.apache.commons.io.IOUtils;
import org.springframework.core.MethodParameter;
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 javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.List;
public class RequestBodyArgumentResolver implements HandlerMethodArgumentResolver {
/**
* 只拦截BatchBody注解且为数组的请求参数
* 每个mapping的方法只会执行一次此方法
*/
public boolean supportsParameter(MethodParameter methodParameter) {
Class paramType = methodParameter.getParameterType();
boolean isArray = paramType.isArray();
boolean isList = paramType.isAssignableFrom(List.class);
boolean hasAnnotation = methodParameter.hasParameterAnnotation(BatchBody.class);
return hasAnnotation && (isArray || isList);
}
/**
* 通过了supportsParameter校验的mapping方法每次都会执行此方法
*/
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
String json = getRequestBodyString(nativeWebRequest);
Type type = methodParameter.getGenericParameterType();
Object obj = JSON.parseObject(json, type);
return obj;
}
/**
* 格式化json数据,统一为数组形式
* 解析json字符串需做得更完善,例如校验json格式是否正确
*/
private String getRequestBodyString(NativeWebRequest webRequest) throws IOException {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
String json = IOUtils.toString(request.getInputStream(), "UTF-8").trim();
if (json.startsWith("{") && json.endsWith("}")) {
return "[" + json + "]";
}
if (json.startsWith("[") && json.endsWith("]")) {
return json;
}
return null;
}
}
Зарегистрируйте RequestBodyArgumentResolver с помощью WebMvcConfigurerAdapter.
package com.test.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new RequestBodyArgumentResolver());
super.addArgumentResolvers(argumentResolvers);
}
}
Определите интерфейс сопоставления и добавьте аннотацию @BatchBody к параметру
@RestController
public class MyController {
@PostMapping("/hello2")
public String test2(@BatchBody MyBeanVo[] param) {
MyBeanVo vo = param[0];
return vo.getValue();
}
@PostMapping("/hello3")
public String test3(@BatchBody List<MyBeanVo> param) {
MyBeanVo vo = param.get(0);
return vo.getValue();
}
@PostMapping("/hello4")
public String test4(@BatchBody MyBeanVo... param) {
MyBeanVo vo = param[0];
return vo.getValue();
}
}
Входящие параметры {"value":"hello world"} или [{"value":"hello world"}]
Возвраты: привет мир
Может быть полностью совместим с массивами, наборами, переменными параметрами (на самом деле массивами).
Анализ: RequestBodyArgumentResolver анализирует строку Json, должен проверить правильность формата, должен быть совместим с параметрами одиночных данных и пакетных данных, просто измените параметр на параметр List/array[]/variable, а затем добавьте @BatchBody аннотация перед ним.Реализуемо, сервисный уровень и уровень dao должны быть разработаны для передачи параметров в пакетах
Суммировать
SpringMVC предоставляет множество настраиваемых интерфейсов и классов перехвата/фильтрации, которые регистрируются в классе конфигурации, предоставляя разработчикам удобный API, который может удовлетворить потребности большинства сценариев в разработке, и его расширяемость действительно хороша. В то же время мы проектируем интерфейс и функцию и рассматриваем сценарии ее расширения и доступа, чтобы сделать каждую функцию более надежной.Сначала проектируйте, а затем кодируйте, чтобы снизить затраты на пробы и ошибки.