❝Это первый выпуск публичного аккаунта «Throwable Digest»."22"Эта оригинальная статья временно включена в альбом «Архитектура и актуальный бой». Следующая длинная статья, которую предварительно планируется выпустить, называется "Graphic and Text Analysis JUC Synchronizer Framework", а следующая короткая статья - "Введение в SpringBoot 2.x: введение в модуль jdbc и простое использование JdbcTemplate".
❞
помещение
в повседневном использованииSpringMVCПри разработке можно столкнуться с различными типами параметров внешнего запроса.Вот относительно полное резюме.SpringMVCИнтерфейс, который обрабатывает параметры контроллера,HandlerMethodArgumentResolver, этот интерфейс имеет множество подклассов, которые имеют дело с различными параметрами (типами аннотаций) соответственно. Ниже перечислены лишь несколько подклассов:
-
RequestParamMethodArgumentResolver: Процесс синтаксического анализа использует@RequestParamАннотированные параметры,MultipartFileпараметры типа иSimpleтип (напримерlong,intи др.) параметры. -
RequestResponseBodyMethodProcessor: обработка синтаксического анализа@RequestBodyАннотированные параметры. -
PathVariableMapMethodArgumentResolver: обработка синтаксического анализа@PathVariableАннотированные параметры.
На самом деле, как правило, при анализе параметров запроса контроллера используется следующее:HandlerMethodArgumentResolverComposite, который загружает все включенныеHandlerMethodArgumentResolverПодкласс. а такжеHandlerMethodArgumentResolverПодклассы используются при разборе параметровHttpMessageConverter(На самом деле, это также анализ списка, обхода и сопоставления) Подклассы используются для анализа сопоставления, общие, такие какMappingJackson2HttpMessageConverter(использоватьJacksonсериализовать и десериализовать).
а такжеHandlerMethodArgumentResolverОт чего именно зависит подклассHttpMessageConverterЭкземпляр фактически определяется заголовком запросаContent-Type(существуетSpringMVCединообразно названный какMediaType,Видетьorg.springframework.http.MediaType) определяется, поэтому мы должны очистить внешний запрос перед обработкой параметров запроса контроллераContent-Typeчто именно это такое. Приведенная выше логика может напрямую смотреть на исходный кодAbstractMessageConverterMethodArgumentResolver#readWithMessageConverters, идея относительно ясна. существует@RequestMappingВ примечаниях,producesа такжеconsumesАтрибут есть и запрошенAcceptили отзывчивыйContent-TypeСвязанный:
-
consumesАтрибут: указывает тип содержимого отправки для обработки запроса (Content-Type),Напримерapplication/json,text/htmlПодождите, только нажмите соответствующийContent-Typevalue примет запрос. -
producesАтрибут: Указывает возвращенный тип содержимого, только если (Accept) type содержит указанный возвращаемый тип, если возвращаемыйJSONДанные обычно рассматриваются для использованияapplication/json;charset=UTF-8.
Еще один момент,SpringMVCиспользуется по умолчанию вJacksonтак какJSONЕсли вы не полностью понимаете работу всего исходного кода, обычно не очень рекомендуется модифицировать набор инструментов по умолчанию.MappingJackson2HttpMessageConverter(например, некоторые люди любят использоватьFastJson,выполнитьHttpMessageConverterпредставлятьFastJsonДелатьHTTPконвертер сообщений, на самом деле такая практика не рекомендуется).
Получение параметра запроса SpringMVC
Фактически общая форма илиJSONЗапросы данных относительно просты, и некоторая сложная обработка в основном включаетURLПараметры пути, загрузка файла, данные типа массива или списка и т. д. Кроме того, относительно существования атрибута типа даты в типе параметра (например,java.util.Date,java.sql.Date,java.time.LocalDate,java.time.LocalDateTime,java.time.ZonedDateTimeи т. д.), обычно требуется логическая реализация пользовательской реализации при разбореString-->日期类型преобразование. На самом деле причина очень проста: типы, связанные с датой, не обязательно одинаковы для каждой страны, каждого часового пояса и даже каждого пользователя, поэтомуSpringMVCНе существует общего решения для анализа типов даты и времени. Следующие классы моделей могут использоваться для демонстрации некоторых примеров:
@Data
public class User {
private String name;
private Integer age;
private List<Contact> contacts;
}
@Data
public class Contact {
private String name;
private String phone;
}
Следующие в основномHTTPизGETМетоды иPOSTметод представлен вSpringMVCБудут проанализированы примеры корректной обработки параметров в системе, и будут потрачены усилия на их устранение.SpringMVCв системе"уникальныйURLпараметр пути"Несколько советов по работе с наиболее распространенными"параметр даты"Разумные методы обработки (дляGETМетоды иPOSTОбработка параметров, представленная методом, в основном включает в себя другие, такие какDELETE,PUTПараметры других методов обрабатываются и могут быть соответственно изменены).
Обработка параметров запроса метода GET
HTTP(s)использование протоколаGETКогда метод делает запрос, отправленные параметры находятся вURLмодальныйQueryчасть, то естьURLиз?Аргументы после идентификатора в форматеkey1=value1&key2=value2.GETПараметры запроса метода можно получить несколькими способами:
- использовать
@RequestParamОбработка аннотаций. - Используйте объект для получения, обратите внимание на то, чтобы имя свойства объекта совпадало с
QueryИмена параметров совпадают. - использовать
HttpServletRequestметоды, предоставляемые экземпляром ("Не рекомендуется, есть жесткое кодирование").
Предполагая запрошенныйURLдляhttp://localhost:8080/get?name=doge&age=26, то контроллер выглядит следующим образом:
@Slf4j
@RestController
public class SampleController {
@GetMapping(path = "/get1")
public void get1(@RequestParam(name = "name") String name,
@RequestParam(name = "age") Integer age) {
log.info("name:{},age:{}", name, age);
}
@GetMapping(path = "/get2")
public void get2(UserVo vo) {
log.info("name:{},age:{}", vo.getName(), vo.getAge());
}
@GetMapping(path = "/get3")
public void get3(HttpServletRequest request) {
String name = request.getParameter("name");
String age = request.getParameter("age");
log.info("name:{},age:{}", name, age);
}
@Data
public static class UserVo {
private String name;
private Integer age;
}
}
параметр формы
Параметры формы, как правило, соответствуют странице<form>все на этикетке<input>помеченname-valueАгрегированные параметры, как правилоContent-TypeНазначенapplication/x-www-form-urlencoded, также будет выполнено значение параметра формы (URL)кодирование. Ниже описаны несколько распространенных форм параметров для отправки параметров формы.
- 【Необъект】- Принимается одиночный параметр необъектного типа.
Соответствующие контроллеры следующие:
@PostMapping(value = "/post")
public String post(@RequestParam(name = "name") String name,
@RequestParam(name = "age") Integer age) {
String content = String.format("name = %s,age = %d", name, age);
log.info(content);
return content;
}
Честно говоря, если проявить настойчивость, отправку всех сложных параметров можно в конечном итоге преобразовать в несколько однопараметрических приемов, но это приведет к созданию большого количества избыточного кода, а ремонтопригодность будет относительно низкой. В этом случае используется обработчик параметраRequestParamMapMethodArgumentResolver.
- 【Object】 - Получен параметр типа объекта.
Затем мы пишем интерфейс для отправки информации о пользователе.Используется упомянутый выше класс модели, который в основном включает имя пользователя, возраст и список контактной информации.На данный момент окончательный код нашего целевого контроллера выглядит следующим образом:
@PostMapping(value = "/user")
public User saveUser(User user) {
log.info(user.toString());
return user;
}
объединение сил обозначениеContent-Typeдляapplication/x-www-form-urlencoded, вам нужно построить формат параметра запроса следующим образом:
Поскольку аннотации не используются, последний обработчик параметровServletModelAttributeMethodProcessor, в основномHttpServletRequestПараметры формы в инкапсулированном вMutablePropertyValuesВ экземпляре он создается типом параметра (создается путем построения отраженияUserinstance), отразите соответствующий атрибут, чтобы заполнить значение. Кроме того, параметр запроса атрибута списка в сложном параметре запроса выглядит странно, по сути он такой же, как и в.propertiesДобавить файлы на финальную картуMapТип параметра формулировки тот же, поэтому индекс для первого слоя вложенного массива или типа списка должен быть записанfirstLevel[index].fieldNameформа. Итак, можно ли все параметры запроса запихнуть в одно поле и отправить?
Выполнение этого напрямую не сработает, потому что фактическая фиксацияFormформа,keyдаuserнить,valueна самом деле слишком строка, пропускаяString->UserТип преобразователя, на самом деле,RequestParamMethodArgumentResolverполагатьсяWebConversionServiceсерединаConverterсписок экземпляров для преобразования параметров, в то время как по умолчаниюConverterПользовательских преобразований точно не будет в спискеString->UserПреобразователь типа:
Есть еще решение, добавить кастомныйorg.springframework.core.convert.converter.ConverterПросто сделай это:
@Component
public class StringUserConverter implements Converter<String, User> {
@Autowaired
private ObjectMapper objectMapper;
@Override
public User convert(String source) {
try {
return objectMapper.readValue(source, User.class);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
}
Вышеупомянутый метод относится к практике спасения страны через кривую, и его не рекомендуется использовать в производственных условиях, а"Если стыковка некоторых сторонних интерфейсов не может обойти этот параметр"(такое действительно встречается очень много, и некоторые древние унаследованные системы склонны к разным странным операциям), вы можете выбрать этот способ реализации.
- 【Массив】 - Список или параметр типа массива.
Крайне не рекомендуется использовать вapplication/x-www-form-urlencodedОтправки форм этого типа мультимедиа принудительно используют параметры типа списка или массива, за исключением совместимости с обработкой отправки параметров в устаревших системах. Например, форма отправленных параметров:
list = ["string-1", "string-2", "string-3"]
Тогда параметр формы следует записать так:
| name | value |
|---|---|
| list[0] | string-1 |
| list[1] | string-2 |
| list[2] | string-3 |
Код контроллера следующий:
@PostMapping(path = "/list")
public void list(@RequestParam(name="list") List<String> list) {
log.info(list);
}
Более сложный пример выглядит следующим образом, предполагая, что формат сообщения, которое вы хотите отправить, имеет следующий вид:
user = [{"name":"doge-1","age": 21},{"name":"doge-2","age": 22}]
Тогда параметр формы следует записать так:
| name | value |
|---|---|
| user[0].name | doge-1 |
| user[0].age | 21 |
| user[1].name | doge-2 |
| user[1].age | 22 |
Код контроллера следующий:
@PostMapping(path = "/user")
public void saveUsers(@RequestParam(name="user") List<UserVo> users) {
log.info(users);
}
@Data
public class UserVo{
private String name;
private Integer age;
}
Этот формат не очень гибок в передаче параметров, может даже снизить эффективность разработки и удобочитаемость параметров.
JSON-параметры
В общем, прямо вPOSTТело запроса в запросе отправляетJSONСтрока таким образом дляSpringMVCболее дружелюбный, просто поставьтеContent-TypeУстановить какapplication/json, Затем загрузите оригиналJSONДостаточно строки, используются параметры метода контроллера@RequestBodyОбработка аннотаций:
Код бэкэнд-контроллера тоже проще:
@PostMapping(value = "/user-2")
public User saveUser2(@RequestBody User user) {
log.info(user.toString());
return user;
}
из-за использования@RequestBodyОбратите внимание, что последний используемый процессор параметров:RequestResponseBodyMethodProcessor, на самом деле будет использоватьMappingJackson2HttpMessageConverterВыполните преобразование типа параметра, нижний слой зависит отJacksonсопутствующие пакеты."Этот метод рекомендуется, он является наиболее распространенным и наиболее надежным.JSONобработка параметров".
Параметры пути URL
URLПараметры пути или параметры пути запроса основаны наURLПараметры, полученные шаблоном, такие как/user/{userId}ЯвляетсяURLшаблон(URLЗаполнители параметров в шаблоне{}), фактически запрошенныйURLдля/user/1, Затем сопоставьте фактический запросURLа такжеURLшаблон для извлеченияuserId1. существуетSpringMVCсередина,URLПараметр пути в шаблоне называетсяPath Variable, что соответствует аннотации@PathVariable, соответствующий процессор параметровPathVariableMethodArgumentResolver."Обратите внимание, что синтаксический анализ @PathVariable сопоставляется в соответствии с атрибутом value(name) независимо от порядка параметров URL.". Возьмем простой пример:
Контроллер в фоновом режиме выглядит следующим образом:
@GetMapping(value = "/user/{name}/{age}")
public String findUser1(@PathVariable(value = "age") Integer age,
@PathVariable(value = "name") String name) {
String content = String.format("name = %s,age = %d", name, age);
log.info(content);
return content;
}
Это использование широко используется вRepresentational State Transfer(REST)стиль архитектуры программного обеспечения, который я лично считаю более гибким и понятным (отURLи метод запроса, чтобы полностью понять значение и функцию интерфейса). Два относительно специальных использования описаны ниже.
- условный
URLпараметр.
На самом деле параметр пути поддерживает регулярные выражения, например, мы используем/sex/{sex}Когда нужен интерфейсsexдолжно бытьF(Female)илиM(Male), то наш шаблон URL можно определить как/sex/{sex:M|F}, код показан ниже:
@GetMapping(value = "/sex/{sex:M|F}")
public String findUser2(@PathVariable(value = "sex") String sex){
log.info(sex);
return sex;
}
Только/sex/Fили/sex/Mзапросы будут поступатьfindUser2()Controller, другие запросы для этого префикса пути являются недопустимыми и возвращают код состояния 404. Вот лишь введение в простейшиеURLИспользование регулярных выражений параметров, более мощное использование, можно изучить самостоятельно.
-
@MatrixVariableиспользование.
MatrixVariableСлишкомURLТип параметра, соответствующий аннотации@MatrixVariable, но это не такURLЗначение в (здесь обозначение значения — часть между двумя «/»), но часть значения, которая отделяется знаком «;», а К-В задается знаком «=». Это звучит немного абстрактно, например: если нам нужно вызвать программиста, чье имя — doge, чей пол — мужской и чья группа — code animal,GETпросилURLЭто может быть выражено как:/call/doge;gender=male;group=programmer, метод контроллера, который мы разработали, выглядит следующим образом:
@GetMapping(value = "/call/{name}")
public String find(@PathVariable(value = "name") String name,
@MatrixVariable(value = "gender") String gender,
@MatrixVariable(value = "group") String group) {
String content = String.format("name = %s,gender = %s,group = %s", name, gender, group);
log.info(content);
return content;
}
Конечно, если вы пишете код по приведенному выше примеру, попробуйте запросить интерфейс и обнаружите, что это ошибка:400 Bad Request - Missing matrix variable 'gender' for method parameter of type String. Это потому что@MatrixVariableИспользование аннотаций небезопасно, т.SpringMVCПо умолчанию его поддержка отключена. включать@MatrixVariableподдержка требует настройкиRequestMappingHandlerMapping#setRemoveSemicolonContentметодfalse:
@Configuration
public class CustomMvcConfiguration implements InitializingBean {
@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;
@Override
public void afterPropertiesSet() throws Exception {
requestMappingHandlerMapping.setRemoveSemicolonContent(false);
}
}
Не рекомендуется использовать, если нет особой необходимости@MatrixVariable.
Файл загружен
загрузка файла в использованииPOSTMANПри моделировании запроса необходимо выбратьform-data,POSTОтправить по:
Предполагая диск в компьютереDВ корневом каталоге диска есть файл образа с именемdoge.jpg, теперь для загрузки файла через локальный сервисный интерфейс код контроллера такой:
@PostMapping(value = "/file1")
public String file1(@RequestPart(name = "file1") MultipartFile multipartFile) {
String content = String.format("name = %s,originName = %s,size = %d",
multipartFile.getName(), multipartFile.getOriginalFilename(), multipartFile.getSize());
log.info(content);
return content;
}
Вывод консоли:
name = file1,originName = doge.jpg,size = 68727
Может быть, немного запутался, откуда взялись параметры, мы можем использоватьFildderПрограммное обеспечение захватывает пакет и видит:
ЗнатьMultipartFileОсновные атрибуты экземпляра являются производными отContent-Disposition,Content-Typeа такжеContent-Length,Кроме того,InputStreamИспользуется для чтения последней части тела запроса (последовательности байтов файла). Обработчик параметров используетRequestPartMethodArgumentResolver(Помните, используйте@RequestPartа такжеMultipartFile一定是使用此参数处理器)。 В остальных случаях используйте@RequestParamа такжеMultipartFileили просто использоватьMultipartFile(Имя параметра должно совпадать сPOSTв видеContent-Dispositionописыватьnameпоследовательный) также может получать загруженные данные файла, в основном черезRequestParamMethodArgumentResolverПроцесс синтаксического анализа, его более мощные функции, бетон может их видетьsupportsParameterметод, код метода контроллера для обоих случаев выглядит следующим образом:
@PostMapping(value = "/file2")
public String file2(MultipartFile file1) {
String content = String.format("name = %s,originName = %s,size = %d",
file1.getName(), file1.getOriginalFilename(), file1.getSize());
log.info(content);
return content;
}
@PostMapping(value = "/file3")
public String file3(@RequestParam(name = "file1") MultipartFile multipartFile) {
String content = String.format("name = %s,originName = %s,size = %d",
multipartFile.getName(), multipartFile.getOriginalFilename(), multipartFile.getSize());
log.info(content);
return content;
}
Другие параметры
Другие параметры в основном включают заголовки запросов,Cookie,Model,Mapи другие связанные параметры, а некоторые из них не очень часто используются или некоторые относительно собственные значения атрибутов (такие какHttpServletRequest,HttpServletResponseили их встроенные методы экземпляра и т. д.) не обсуждаются.
заголовок запроса
Значение заголовка запроса в основном передается через@RequestHeaderПолучение параметров аннотации, процессор параметровRequestHeaderMethodArgumentResolver, в аннотации нужно указать заголовок запросаKey. Просто и практично следующим образом:
Код метода контроллера:
@PostMapping(value = "/header")
public String header(@RequestHeader(name = "Content-Type") String contentType) {
return contentType;
}
Cookie
CookieЗначение в основном через@CookieValueПараметр аннотации получен, и процессор параметровServletCookieValueMethodArgumentResolver, что необходимо указать в аннотацииCookieизKey. Код метода контроллера выглядит следующим образом:
@PostMapping(value = "/cookie")
public String cookie(@CookieValue(name = "JSESSIONID") String sessionId) {
return sessionId;
}
Параметр типа модели
ModelОбработчик параметра типаModelMethodProcessor, на самом деле обработка этого параметра заключается в возврате напрямуюModelAndViewContainerв случаеModel(конкретно этоModelMapтип), поскольку функциональность различных интерфейсов и классов должна быть объединена, экземпляр обратного вызоваBindingAwareModelMapтип, который наследуется отModelMapВ то же время понялModelинтерфейс. Например:
@GetMapping(value = "/model")
public String model(Model model, ModelMap modelMap) {
log.info("{}", model == modelMap);
return "success";
}
Обратите внимание, что при вызове этого интерфейса вывод консолиINFOСодержимое журнала:true. Еще одно замечание:ModelMapилиModelдобавленные элементы свойств добавляются кHttpRequestServletЭкземпляр переносится на страницу для рендеринга.Исходя из использования механизма шаблонов, вы можете напрямую использовать заполнители для извлечения этих значений атрибутов в содержимом файла шаблона.
Параметр @ModelAttribute
@ModelAttributeОбработчик параметров для обработки аннотацийModelAttributeMethodProcessor,@ModelAttributeИсходный код функции аннотирован следующим образом:
❝Annotation that binds a method parameter or method return value to a named model attribute, exposed to a web view.
❞
Проще говоря, поkey-valueФормально привязать параметры метода или возвращаемые значения метода кModel(Map)Среди трех случаев:
-
@ModelAttributeИспользуется в методе (возвращаемое значение), метод не имеет возвращаемого значения (voidТипы),Model(Map)Параметры должны быть установлены самостоятельно. -
@ModelAttributeИспользуется в методе (возвращаемое значение), метод имеет возвращаемое значение (неvoidтип), возвращаемое значение добавляется кModel(Map)параметр,keyЗависит от@ModelAttributeизvalueУкажите, иначе будет использован тип возвращаемого значения string (первая буква заменяется на строчную, если тип возвращаемого значенияInteger,ноkeyдляinteger). -
@ModelAttributeИспользуется в параметрах метода, вы можете получить уже установленный в том же контроллере@ModelAttributeсоответствующее значение.
в контроллере (используя@ControllerизSpringкомпоненты), если есть один или несколько с использованием@ModelAttributeЭти методы всегда выполняются до входа в метод контроллера, а порядок выполнения определяется порядком загрузки (конкретный порядок является приоритетным с параметрами и сортируется по возрастанию первым методом), например:
@Slf4j
@RestController
public class ModelAttributeController {
@ModelAttribute
public void before(Model model) {
log.info("before..........");
model.addAttribute("before", "beforeValue");
}
@ModelAttribute(value = "beforeArg")
public String beforeArg() {
log.info("beforeArg..........");
return "beforeArgValue";
}
@GetMapping(value = "/modelAttribute")
public String modelAttribute(Model model, @ModelAttribute(value = "beforeArg") String beforeArg) {
log.info("modelAttribute..........");
log.info("beforeArg..........{}", beforeArg);
log.info("{}", model);
return "success";
}
@ModelAttribute
public void after(Model model) {
log.info("after..........");
model.addAttribute("after", "afterValue");
}
@ModelAttribute(value = "afterArg")
public String afterArg() {
log.info("afterArg..........");
return "afterArgValue";
}
}
При вызове этого интерфейса журнал вывода консоли выглядит следующим образом:
after..........
before..........
afterArg..........
beforeArg..........
modelAttribute..........
beforeArg..........beforeArgValue
{after=afterValue, before=beforeValue, afterArg=afterArgValue, beforeArg=beforeArgValue}
Можно проверить, что правила сортировки и настройки параметров, а также полученные результаты согласуются с предыдущим анализом.
Ошибки или параметр BindingResult
ErrorsФактическиBindingResultродительский интерфейс,BindingResultВ основном используется для обратного вызоваJSRЭлемент атрибута исключения проверки параметра, еслиJSR303Исключение проверки, обычно выдаетсяMethodArgumentNotValidExceptionисключение и вернется400(Bad Request), см. Глобальные обработчики исключенийDefaultHandlerExceptionResolver.ErrorsОбработчик параметра для типаErrorsMethodArgumentResolver. Например:
@PostMapping(value = "/errors")
public String errors(@RequestBody @Validated ErrorsModel errors, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
for (ObjectError objectError : bindingResult.getAllErrors()) {
log.warn("name={},message={}", objectError.getObjectName(), objectError.getDefaultMessage());
}
}
return errors.toString();
}
//ErrorsModel
@Data
@NoArgsConstructor
public class ErrorsModel {
@NotNull(message = "id must not be null!")
private Integer id;
@NotEmpty(message = "errors name must not be empty!")
private String name;
}
консоль интерфейса вызоваWarnЖурнал выглядит следующим образом:
name=errors,message=errors name must not be empty!
В общем, не рекомендуется таким образом работать с элементами свойств исключений проверки JSR, потому что это потребует много повторяющейся работы по жесткому кодированию.ResponseEntityExceptionHandlerПереопределите соответствующий метод или используйте второй метод одновременно@ExceptionHandlerа также@(Rest)ControllerAdviceАннотация для обработки исключений. Например:
@RestControllerAdvice
public class ApplicationRestControllerAdvice{
@ExceptionHandler(BusinessException.class)
public Response handleBusinessException(BusinessException e, HttpServletRequest request){
// 这里处理异常和返回值
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public Response handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request){
// 这里处理异常和返回值
}
}
❝Стоит отметить, что после определенной версии SpringBoot зависимости, связанные с JSR303, извлекаются в зависимости от spring-boot-starter-validation.Если вы хотите использовать функции проверки, связанные с JSR303, вы должны ввести этот стартер самостоятельно
❞
Параметр @Value
Параметры метода контроллера могут быть@ValueПараметры, измененные аннотацией, будут изменены сEnvironmentВ экземпляре значение атрибута собирается и преобразуется в соответствующий параметр (то есть источником параметра является не тело запроса, а значение атрибута среды, которое было загружено и обработано в контексте), а обработчик параметра являетсяExpressionValueMethodArgumentResolver. Например:
@GetMapping(value = "/value")
public String value(@Value(value = "${spring.application.name}") String name) {
log.info("spring.application.name={}", name);
return name;
}
spring.application.nameАтрибуты обычно указываются в файле конфигурации и добавляются в глобальные при загрузке атрибутов файла конфигурации.Environmentсередина.
Параметр типа карты
MapДиапазон параметров типа относительно широк, что соответствует ряду обработчиков параметров, обратите внимание на разницу, используя некоторые из упомянутых выше аннотаций.MapТипы и вообще не использовать аннотацииMapПараметр типа, обрабатывающий два, не то же самое. Вот несколько относительно типичныхMapПример обработки параметра типа.
"без всяких аннотацийMap<String,Object>параметр"
В этом случае параметр фактически вызывается напрямуюModelAndViewContainerсерединаModelMapнапример, обработчик параметраMapMethodProcessor,ПрошлоеMapСвойства, добавленные в параметры, будут перенесены на страницу.
"Аннотировано с помощью @RequestParamMap<String,Object>параметр"
Обработчик параметра в этом случаеRequestParamMapMethodArgumentResolver, необходимо указать используемый метод запросаContent-Typeдляx-www-form-urlencoded, нельзя использоватьapplication/jsonПуть:
Код контроллера:
@PostMapping(value = "/map")
public String mapArgs(@RequestParam Map<String, Object> map) {
log.info("{}", map);
return map.toString();
}
"Параметры Map
Обработчик параметра в этом случаеRequestHeaderMapMethodArgumentResolver, функция состоит в том, чтобы получить все заголовки запроса запросаKey-Value.
"Параметры Map
Обработчик параметра в этом случаеPathVariableMapMethodArgumentResolver, роль состоит в том, чтобы получить все параметры пути, инкапсулированные какKey-Valueструктура.
Коллекция MultipartFile — массовая загрузка файлов
При пакетной загрузке файлов нам, как правило, необходимо получитьMultipartFileСбор, есть два варианта:
- использовать
MultipartHttpServletRequestпараметры, звоните напрямуюgetFilesспособ получитьMultipartFileсписок. - использовать
@RequestParamОформление аннотацииMultipartFileсписок, обработчик параметраRequestParamMethodArgumentResolver, по сути, это только первый способ инкапсуляции.
Код метода контроллера выглядит следующим образом:
@PostMapping(value = "/parts")
public String partArgs(@RequestParam(name = "file") List<MultipartFile> parts) {
log.info("{}", parts);
return parts.toString();
}
Обработка параметра типа даты
Лично обработка параметров даты считается самой сложной в обработке параметров запроса, потому что логика общей обработки даты не универсальна, а слишком много индивидуальной обработки затрудняет наличие единой стандартной логики обработки для обработки и преобразования типа даты. параметры. Тем не менее, вот несколько общих методов работы со всеми видами странных форматов даты. Во всех примерах, описанных ниже, используетсяJDK8Дата и время введены вAPI,околоjava.util.Dateосновная дата и времяAPIиспользуются таким же образом.
1. Унифицированный прием в виде строк
Это самый примитивный, но самый эффективный способ, он равномерно принимается в виде строки, а затем сам занимается преобразованием типов, вот небольшой пример:
static DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@PostMapping(value = "/date1")
public String date1(@RequestBody UserDto userDto) {
UserEntity userEntity = new UserEntity();
userEntity.setUserId(userDto.getUserId());
userEntity.setBirthdayTime(LocalDateTime.parse(userDto.getBirthdayTime(), FORMATTER));
userEntity.setGraduationTime(LocalDateTime.parse(userDto.getGraduationTime(), FORMATTER));
log.info(userEntity.toString());
return "success";
}
@Data
public class UserDto {
private String userId;
private String birthdayTime;
private String graduationTime;
}
@Data
public class UserEntity {
private String userId;
private LocalDateTime birthdayTime;
private LocalDateTime graduationTime;
}
Недостаток использования строк для получения и последующего преобразования заключается в том, что существует слишком много кодов шаблонов, стиль кодирования недостаточно лаконичен и слишком много повторяющейся работы.
2. Используйте аннотацию @DateTimeFormat или @JsonFormat
@DateTimeFormatАннотации подходят@RequestBodyКогда параметр используется, будет обнаружено, что выдаетInvalidFormatExceptionИсключение, указывающее, что преобразование не удалось, это связано с тем, что при обработке этой аннотации поддерживается толькоFormОтправка формы (Content-Typeдляx-www-form-urlencoded), Например:
@Data
public class UserDto2 {
private String userId;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime birthdayTime;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime graduationTime;
}
@PostMapping(value = "/date2")
public String date2(UserDto2 userDto2) {
log.info(userDto2.toString());
return "success";
}
//或者像下面这样
@PostMapping(value = "/date2")
public String date2(@RequestParam("name"="userId")String userId,
@RequestParam("name"="birthdayTime") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime birthdayTime,
@RequestParam("name"="graduationTime") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime graduationTime) {
return "success";
}
а также@JsonFormatАннотации можно использовать вFormформа илиJSONСценарии запроса параметров, поэтому более рекомендуется использовать@JsonFormatОбратите внимание, но обратите внимание, что вам нужно указать часовой пояс (timezoneСобственность, например, восемь районов в Восточном КитаеGMT+8), в противном случае это может привести к"расстройство суточного биоритма в связи с дальним перелетом",Например:
@PostMapping(value = "/date2")
public String date2(@RequestBody UserDto2 userDto2) {
log.info(userDto2.toString());
return "success";
}
@Data
public class UserDto2 {
private String userId;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime birthdayTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime graduationTime;
}
❝Как правило, LocalDateTime используется как тип параметра поля даты, поскольку его преобразование проще, чем другие типы даты и времени JDK8.
❞
3. Настройка сериализации и десериализации Джексона
потому чтоSpringMVCИспользовать по умолчаниюJacksonиметь дело с@RequestBodyпреобразование параметров, поэтому преобразование типа даты может быть достигнуто путем настройки сериализатора и десериализатора, чтобы мы могли использоватьapplication/jsonОтправьте параметры запроса в форму. Пример здесь - преобразовать запросJSONСтрока в параметреLocalDateTimeтип, принадлежащийJSONдесериализации, поэтому требуется собственный десериализатор:
@PostMapping(value = "/date3")
public String date3(@RequestBody UserDto3 userDto3) {
log.info(userDto3.toString());
return "success";
}
@Data
public class UserDto3 {
private String userId;
@JsonDeserialize(using = CustomLocalDateTimeDeserializer.class)
private LocalDateTime birthdayTime;
@JsonDeserialize(using = CustomLocalDateTimeDeserializer.class)
private LocalDateTime graduationTime;
}
public class CustomLocalDateTimeDeserializer extends LocalDateTimeDeserializer {
public CustomLocalDateTimeDeserializer() {
super(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
}
4. Лучшие практики
Все первые три метода имеют такие проблемы, как жесткое программирование.MappingJackson2HttpMessageConverterсерединаObjectMapperДля типа даты обрабатываются сериализатор и десериализатор по умолчанию, чтобы он мог действовать глобально, и нет необходимости использовать другие аннотации или настраивать схему сериализации (конечно, иногда требуется специальная настройка обработки), или, в Другими словами, когда требуется специальная обработка, используйте только другие аннотации или пользовательские схемы сериализации для сценариев. Использование интерфейса хукаJackson2ObjectMapperBuilderCustomizerможет быть реализован в контейнереObjectMapperНастройка атрибутов в синглтоне:
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer(){
return customizer->{
customizer.serializerByType(LocalDateTime.class,new LocalDateTimeSerializer(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
customizer.deserializerByType(LocalDateTime.class,new LocalDateTimeDeserializer(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
};
}
Его можно настроитьMappingJackson2HttpMessageConverterудерживается вObjectMapper, вышесказанноеLocalDateTimeСериализаторы и десериализаторы действуют глобально.
запросить совпадение URL
Основная обработка параметров запроса была описана выше.SpringMVCтакже будет следоватьURLшаблон, чтобы соответствовать, используяAntСтиль пути, класс инструмента обработкиorg.springframework.util.AntPathMatcher, из этого типа комментариев правила сопоставления в основном включают следующие четыре пункта
:
-
?Совпадение с 1 символом. -
*соответствует 0 или более"персонаж". -
**Совпадение 0 или более в пути"содержание". - Регулярная поддержка, например
{spring:[a-z]+}Присвоить значение, соответствующее регулярному выражению [a-z]+, значению с именем"spring"переменная пути.
Некоторые примеры:
"URL-адреса в форме '?'":
@GetMapping(value = "/pattern?")
public String pattern() {
return "success";
}
/pattern 404 Not Found
/patternd 200 OK
/patterndd 404 Not Found
/pattern/ 404 Not Found
/patternd/s 404 Not Found
"URL-адреса в форме '*'":
@GetMapping(value = "/pattern*")
public String pattern() {
return "success";
}
/pattern 200 OK
/pattern/ 200 OK
/patternd 200 OK
/pattern/a 404 Not Found
"URL-адреса в форме '**'":
@GetMapping(value = "/pattern/**/p")
public String pattern() {
return "success";
}
/pattern/p 200 OK
/pattern/x/p 200 OK
/pattern/x/y/p 200 OK
"URL-адреса вида {spring:[a-z]+}":
@GetMapping(value = "/pattern/{key:[a-c]+}")
public String pattern(@PathVariable(name = "key") String key) {
return "success";
}
/pattern/a 200 OK
/pattern/ab 200 OK
/pattern/abc 200 OK
/pattern 404 Not Found
/pattern/abcd 404 Not Found
Вышеуказанные четыре узора URL могут использоваться в комбинации и постоянно меняются.
URLсоответствие также следует"полное совпадение", то есть есть два шаблона для одного и того жеURLможет быть успешно сопоставлено, то"выбрать самый точныйURLсоответствовать", введите соответствующий метод контроллера, например:
@GetMapping(value = "/pattern/**/p")
public String pattern1() {
return "success";
}
@GetMapping(value = "/pattern/p")
public String pattern2() {
return "success";
}
Вышеупомянутые два контроллера, если требуетсяURLдля/pattern/p, окончательный метод входаpattern2. Приведенный выше пример просто перечисляетSpringMVCсерединаURLТипичный пример соответствия, причем не проработанный в глубину.
наконец,org.springframework.util.AntPathMatcherКак инструментальный класс его можно использовать отдельно, а не только для сопоставленияURL, также можно использовать для сопоставления пути к системному файлу, но вам нужно использовать его параметризованную конструкцию, чтобы изменить внутреннийpathSeparatorпеременные, например:
AntPathMatcher antPathMatcher = new AntPathMatcher(File.separator);
резюме
Автор провел много времени, прочесывая и анализируяSpring,SpringMVCИсходный код , но развитие бизнеса требуется на длительный период времени, и вещи немного ржавые с точки зрения архитектуры.Ведь вещи станут ржавыми, когда они не используются.Это здравый смысл. Эта статья основана на некоторыхSpringMVCОпыт исходного кода обобщает некоторые знания, связанные с обработкой параметров запроса, и я надеюсь помочь себе и всем.
Использованная литература:
- spring-boot-web-starter: исходный код 2.3.0.RELEASE.
(Конец этой статьи c-7-d e-a-20180512 r-a-20200713 Старая статья, переизданная обложка из японской манги «Вор-камикадзе»)