Длинный текст из 25 000 слов кратко описывает прием параметров запроса SpringMVC.

задняя часть
Длинный текст из 25 000 слов кратко описывает прием параметров запроса SpringMVC.

Это первый выпуск публичного аккаунта «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сериализовать и десериализовать).

spmvc-p-14
spmvc-p-14

а также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Параметры запроса метода можно получить несколькими способами:

  1. использовать@RequestParamОбработка аннотаций.
  2. Используйте объект для получения, обратите внимание на то, чтобы имя свойства объекта совпадало сQueryИмена параметров совпадают.
  3. использовать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)кодирование. Ниже описаны несколько распространенных форм параметров для отправки параметров формы.

  • 【Необъект】- Принимается одиночный параметр необъектного типа.
spmvc-p-1
spmvc-p-1

Соответствующие контроллеры следующие:

@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, вам нужно построить формат параметра запроса следующим образом:

spmvc-p-2
spmvc-p-2

Поскольку аннотации не используются, последний обработчик параметровServletModelAttributeMethodProcessor, в основномHttpServletRequestПараметры формы в инкапсулированном вMutablePropertyValuesВ экземпляре он создается типом параметра (создается путем построения отраженияUserinstance), отразите соответствующий атрибут, чтобы заполнить значение. Кроме того, параметр запроса атрибута списка в сложном параметре запроса выглядит странно, по сути он такой же, как и в.propertiesДобавить файлы на финальную картуMapТип параметра формулировки тот же, поэтому индекс для первого слоя вложенного массива или типа списка должен быть записанfirstLevel[index].fieldNameформа. Итак, можно ли все параметры запроса запихнуть в одно поле и отправить?

spmvc-p-3
spmvc-p-3

Выполнение этого напрямую не сработает, потому что фактическая фиксацияFormформа,keyдаuserнить,valueна самом деле слишком строка, пропускаяString->UserТип преобразователя, на самом деле,RequestParamMethodArgumentResolverполагатьсяWebConversionServiceсерединаConverterсписок экземпляров для преобразования параметров, в то время как по умолчаниюConverterПользовательских преобразований точно не будет в спискеString->UserПреобразователь типа:

spmvc-p-4
spmvc-p-4

Есть еще решение, добавить кастомный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Обработка аннотаций:

spmvc-p-5
spmvc-p-5

Код бэкэнд-контроллера тоже проще:

@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.". Возьмем простой пример:

spmvc-p-6
spmvc-p-6

Контроллер в фоновом режиме выглядит следующим образом:

@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Отправить по:

spmvc-p-8
spmvc-p-8

Предполагая диск в компьютере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Программное обеспечение захватывает пакет и видит:

spmvc-p-7
spmvc-p-7

Знать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. Просто и практично следующим образом:

spmvc-p-9
spmvc-p-9

Код метода контроллера:

@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)Среди трех случаев:

  1. @ModelAttributeИспользуется в методе (возвращаемое значение), метод не имеет возвращаемого значения (voidТипы),Model(Map)Параметры должны быть установлены самостоятельно.
  2. @ModelAttributeИспользуется в методе (возвращаемое значение), метод имеет возвращаемое значение (неvoidтип), возвращаемое значение добавляется кModel(Map)параметр,keyЗависит от@ModelAttributeизvalueУкажите, иначе будет использован тип возвращаемого значения string (первая буква заменяется на строчную, если тип возвращаемого значенияInteger,ноkeyдляinteger).
  3. @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Путь:

spmvc-p-10
spmvc-p-10

Код контроллера:

@PostMapping(value = "/map")
public String mapArgs(@RequestParam Map<String, Object> map) {
    log.info("{}", map);
    return map.toString();
}

"Параметры Map, аннотированные с помощью @RequestHeader"

Обработчик параметра в этом случаеRequestHeaderMapMethodArgumentResolver, функция состоит в том, чтобы получить все заголовки запроса запросаKey-Value.

"Параметры Map, аннотированные с помощью @PathVariable"

Обработчик параметра в этом случаеPathVariableMapMethodArgumentResolver, роль состоит в том, чтобы получить все параметры пути, инкапсулированные какKey-Valueструктура.

Коллекция MultipartFile — массовая загрузка файлов

При пакетной загрузке файлов нам, как правило, необходимо получитьMultipartFileСбор, есть два варианта:

  1. использоватьMultipartHttpServletRequestпараметры, звоните напрямуюgetFilesспособ получитьMultipartFileсписок.
  2. использовать@RequestParamОформление аннотацииMultipartFileсписок, обработчик параметраRequestParamMethodArgumentResolver, по сути, это только первый способ инкапсуляции.
spmvc-p-11
spmvc-p-11

Код метода контроллера выглядит следующим образом:

@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;
}
spmvc-p-12
spmvc-p-12

Недостаток использования строк для получения и последующего преобразования заключается в том, что существует слишком много кодов шаблонов, стиль кодирования недостаточно лаконичен и слишком много повторяющейся работы.

2. Используйте аннотацию @DateTimeFormat или @JsonFormat

@DateTimeFormatАннотации подходят@RequestBodyКогда параметр используется, будет обнаружено, что выдаетInvalidFormatExceptionИсключение, указывающее, что преобразование не удалось, это связано с тем, что при обработке этой аннотации поддерживается толькоFormОтправка формы (Content-Typeдляx-www-form-urlencoded), Например:

spmvc-p-13
spmvc-p-13
@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. ?Совпадение с 1 символом.
  2. *соответствует 0 или более"персонаж".
  3. **Совпадение 0 или более в пути"содержание".
  4. Регулярная поддержка, например{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 Старая статья, переизданная обложка из японской манги «Вор-камикадзе»)