Il s'agit du 4ème volet de la série «Spring Framework 5.0 Major Changes» et des principaux changements liés à WebMVC (nouvelles fonctionnalités, améliorations, etc.) Je voudrais présenter.
Ligne de base
Individuel
Cette fois, j'ai utilisé Spring Boot 2.0.0.M1 (qui dépend de Spring Framework 5.0.0.RC1 par défaut) pour vérifier l'opération. Vous trouverez ci-dessous une brève introduction à la procédure de création d'un projet de vérification.
Note:
Il peut également être créé à l'aide des fonctions SPRING INITIALIZR et IDE.
$ curl -s https://start.spring.io/starter.tgz\
-d name=spring5-web-demo\
-d artifactId=spring5-web-demo\
-d dependencies=web\
-d baseDir=spring5-web-demo\
-d bootVersion=2.0.0.M1\
| tar -xzvf -
$ ./mvnw spring-boot:run
/usr/local/apps/spring5-web-demo
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building spring5-web-demo 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.0.0.M1:run (default-cli) > test-compile @ spring5-web-demo >>>
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ spring5-web-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ spring5-web-demo ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source files to /usr/local/apps/spring5-web-demo/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ spring5-web-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /usr/local/apps/spring5-web-demo/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ spring5-web-demo ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source files to /usr/local/apps/spring5-web-demo/target/test-classes
[INFO]
[INFO] <<< spring-boot-maven-plugin:2.0.0.M1:run (default-cli) < test-compile @ spring5-web-demo <<<
[INFO]
[INFO]
[INFO] --- spring-boot-maven-plugin:2.0.0.M1:run (default-cli) @ spring5-web-demo ---
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.0.M1)
2017-05-17 01:19:22.357 INFO 26370 --- [ main] c.e.s.Spring5WebDemoApplication : Starting Spring5WebDemoApplication on xxx with PID 26370 (/usr/local/apps/spring5-web-demo/target/classes started by xxx in /usr/local/apps/spring5-web-demo)
2017-05-17 01:19:22.360 INFO 26370 --- [ main] c.e.s.Spring5WebDemoApplication : No active profile set, falling back to default profiles: default
2017-05-17 01:19:22.411 INFO 26370 --- [ main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@25c892fe: startup date [Wed May 17 01:19:22 JST 2017]; root of context hierarchy
2017-05-17 01:19:23.717 INFO 26370 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2017-05-17 01:19:23.733 INFO 26370 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2017-05-17 01:19:23.735 INFO 26370 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.15
2017-05-17 01:19:23.822 INFO 26370 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2017-05-17 01:19:23.822 INFO 26370 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1414 ms
2017-05-17 01:19:23.956 INFO 26370 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2017-05-17 01:19:23.961 INFO 26370 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2017-05-17 01:19:23.961 INFO 26370 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2017-05-17 01:19:23.961 INFO 26370 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2017-05-17 01:19:23.961 INFO 26370 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2017-05-17 01:19:24.206 INFO 26370 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@25c892fe: startup date [Wed May 17 01:19:22 JST 2017]; root of context hierarchy
2017-05-17 01:19:24.292 INFO 26370 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2017-05-17 01:19:24.293 INFO 26370 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2017-05-17 01:19:24.323 INFO 26370 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-05-17 01:19:24.323 INFO 26370 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-05-17 01:19:24.370 INFO 26370 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-05-17 01:19:24.533 INFO 26370 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2017-05-17 01:19:24.610 INFO 26370 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http)
2017-05-17 01:19:24.615 INFO 26370 --- [ main] c.e.s.Spring5WebDemoApplication : Started Spring5WebDemoApplication in 2.996 seconds (JVM running for 6.973)
Sur le terminal, "Ctrl+ C」!!
Dans Spring Framework 5.0, les modifications suivantes ont été apportées à la fonction WEB.
Numéro d'article | Changements |
---|---|
1 | Le filtre de servlet fourni par Spring Framework est Servlet 3.Ce sera une implémentation conforme à la spécification API de 1. (En fait, je ne sais pas comment cela s'est conformé ...:sweat_smile:) [Vers les détails:arrow_right:] |
2 | Servlet 4 qui est un composant de Java EE 8 comme argument de la méthode Handler de Spring MVC.Ajouté à 0PushBuilder (HTTP/API pour faire Server Push of 2)Vous pourrez le recevoir.[Vers les détails:arrow_right:] |
3 | Servlet 3.Lorsqu'une erreur surdimensionnée se produit pour la fonction de téléchargement de fichier prise en charge par 0MaxUploadSizeExceededException (MultipartException Sous-exception)Sera jeté.[Vers les détails:arrow_right:]**Note:**Cependant,certainsmotsdumessaged'erreur( "size" Quand"exceed" )Puisqu'il est jugé selon qu'il est inclus ou non, il est le même qu'auparavant en fonction de l'implémentation du serveur d'applications.MultipartException がスローされる可能性があるQuandいう点は意識しておいた方がよいでしょう。 |
4 | Mécanisme de résolution de type de média unifié(MediaTypeFactory classe)Sera ajouté.[Vers les détails:arrow_right:]**Note:**Aveccettecorrespondance,JAF(JavaBeansActivationframework)Le code dépendant est éliminé. |
5 | La liaison de données à des objets immuables est prise en charge.[Vers les détails:arrow_right:] |
6 | Jackson 2.9 est pris en charge.[Vers les détails:arrow_right:] |
7 | Un composant de Java EE 8JSON BindingSeraprisencharge. **Note:**IlpeutêtreutilisécommealternativeàJacksonetGSON.[Verslesdétails:arrow_right:] |
8 | Google Protobuf 3.x est pris en charge.[Vers les détails:arrow_right:] |
9 | Reactive Programing Model(Spring WebFlux décrit plus tard)Avec le soutien de Reactor 3.1 cours(Flux , Mono )、RxJava 1.3 et 2.1 cours(Observable , Sigle , Flowable Tel)Peut être traité comme la valeur de retour de la méthode Handler.[Vers les détails:arrow_right:] |
10 | AntPathMatcher Comme alternative àParsingPathMatcher Sera ajouté.[Vers les détails:arrow_right:] **Note:**Pour les composants liés à Spring WebFlux, ParsingPathMatcher Est utilisé par défaut. |
11 | @ExceptionHandler En tant qu'argument de méthodeRedirectAttributes Vous pourrez le recevoir.(=RedirectAttributes Vous pourrez lier les données à la destination de la redirection via) [Vers les détails:arrow_right:] |
12 | ResponseStatusException Est ajouté, et vous pouvez contrôler l'état HTTP en générant et en lançant une exception en spécifiant un code d'état arbitraire et une phrase de raison.[Vers les détails:arrow_right:] Note: ResponseStatusException Semble avoir été créé pour Spring WebFlux, mais il est également pris en charge pour une utilisation avec Spring MVC. |
13 | ScriptTemplateView (JSR-Afficher l'implémentation à l'aide du moteur de script 223)Objets nécessaires pour réaliser "l'internationalisation des messages" et la "fragmentation des modèles"(Locale ,MessageSource Tel)Sera remis du côté du moteur de gabarit.[Vers les détails:arrow_right:] Note: 「国際化」や「テンプレートをフラグメント化」するための実装自体はSpringからは提供されないので、SpringのテストケースTelを参考に開発者が実装する必要があります。 |
[SPR-14467]: Apparemment, il s'agit d'une implémentation purement (non limitée au filtre de servlet) basée sur la spécification de l'API Servlet 3.1 ( = Le fonctionnement sur Servlet 3.0 n'est pas pris en charge)! Il paraît que. À cet égard, jusqu'à Spring Framework 4.3, du code a été implémenté pour maintenir la compatibilité avec l'environnement Servlet 2.5, mais à partir de Spring Framework 5.0, la prise en charge de l'environnement Servlet 2.5 sera interrompue du tout ([SPR-13189]. ](Https://jira.spring.io/browse/SPR-13189)).
[SPR-12674] PushBuilder
ajouté dans Servlet 4.0, qui est un composant de Java EE 8, comme argument de la méthode Handler de Spring MVC. Vous pourrez recevoir (API pour HTTP / 2 Server Push).
Implémentons un exemple simple et essayons HTTP / 2 Server Push en action. Tout d'abord, créez un fichier HTML avec un lien dans le fichier css et le fichier css.
src/main/resources/static/style.css
body {
background-color: azure;
}
src/main/resources/static/hello.html
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css"/> <!--★★★ Lien vers le fichier css-->
</head>
<body>
<h1>Hello World !!</h1>
</body>
</html>
Ensuite, créez un contrôleur.
package com.example.spring5webdemo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.http.PushBuilder;
@Controller
public class PushBuilderController {
@GetMapping("/push-builder")
String get(PushBuilder builder) { //★★★ Déclarer PushBuilder comme argument
builder.path("/style.css").push(); //★★★ Définissez le fichier css sur le chemin à pousser et poussez
return "forward:/hello.html"; //Renvoyer le nom de la vue pour la transition vers HTML
}
}
Je voudrais dire, "Commençons le serveur et vérifions-le!", Mais je ne pense pas qu'il puisse être compilé parce que l'API (PushBuilder
) de Servlet 4.0 n'est pas trouvée en premier lieu. Donc ... je voudrais modifier le fichier pom.xml
pour utiliser un serveur d'application qui prend en charge Servlet 4.0. Au début, je pensais l'essayer avec Tomcat 9.0, mais Undertow semblait être plus facile à convertir en HTTP / 2, alors j'ai utilisé ici Undertow 2.0 (une version en développement) qui prend en charge Servlet 4.0. Pour vérifier le fonctionnement.
Note:
Undertow 2.0.0.Alpha1 a été publié dans Maven Central, mais malheureusement cette version n'est pas disponible. La raison est que les spécifications API de Servlet 4.0 ont changé au milieu et que les changements n'ont pas été incorporés. Donc ... Dans cette entrée, récupérez le code source de GitHub et installez Undertow 2.0.0.Alpha2-SNAPSHOT dans votre référentiel local.
$ git clone https://github.com/undertow-io/undertow.git $ cd undertow $ mvn -U install -DskipTests=true
Modifiez le fichier pom.xml
pour utiliser Undertow 2.0.0.Alpha2-SNAPSHOT au lieu de Tomcat.
pom.xml
<!-- ... -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<undertow.version>2.0.0.Alpha2-SNAPSHOT</undertow.version> <!-- ★★★ Servlet 4.0 Spécifier la version de support(Version installée dans le référentiel local) -->
</properties>
<!-- ... -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--★★★ Exclure Tomcat-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- ... -->
<!--★★★ Ajouté Undertow-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- ★★★ alpn-Ajouter un démarrage-->
<!--
ALPN (Application Layer Protocol Negotiation)Paramètres de téléchargement de la bibliothèque d'implémentation de.
Le fichier téléchargé est une option de démarrage lorsque l'application est exécutée.(-Xbootclasspath)Spécifié comme.
-->
<dependency>
<groupId>org.mortbay.jetty.alpn</groupId>
<artifactId>alpn-boot</artifactId>
<version>8.1.11.v20170118</version>
<scope>provided</scope>
</dependency>
<!-- ... -->
</dependencies>
<!-- ... -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!--★★★ Option pour démarrer la bibliothèque d'implémentation ALPN(-Xbootclasspath)Désigné pour-->
<jvmArguments>
-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/8.1.11.v20170118/alpn-boot-8.1.11.v20170118.jar
</jvmArguments>
</configuration>
</plugin>
</plugins>
</build>
<!-- ... -->
Afin de communiquer avec HTTP / 2, la communication entre le serveur et le navigateur doit être une communication SSL / TLS par HTTPS, créez donc un keystore qui stocke le certificat auto-signé.
$ keytool -genkey -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore src/main/resources/keystore.p12 -validity 3650
Veuillez saisir le mot de passe du fichier de clés:
Veuillez saisir à nouveau votre nouveau mot de passe:
Quel est votre nom et prénom?
[Unknown]: Sample
Quel est le nom de l'unité organisationnelle?
[Unknown]: Sample
Quel est le nom de l'organisation?
[Unknown]: Sample
Quel est le nom de la ville ou de la région?
[Unknown]: Sample
Quel est l'état ou le nom de l'état?
[Unknown]: Sample
Quel est le code du pays à deux lettres pour cet appareil?
[Unknown]: JP
CN=Sample, OU=Sample, O=Sample, L=Sample, ST=Sample, C=Êtes-vous sûr de vouloir JP?
[Non]: Y
Définissez le fichier de clés qui stocke le certificat auto-signé créé sur Undertow et définissez le serveur HTTPS.
src/main/resources/application.properties
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=password
Enfin, si vous ajoutez la définition de Bean suivante, le support HTTP / 2 d'Undertow est terminé.
// ...
import io.undertow.UndertowOptions;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
// ...
@SpringBootApplication
public class Spring5WebDemoApplication {
// ...
@Bean
public WebServerFactoryCustomizer<UndertowServletWebServerFactory> webServerFactoryCustomizer() {
return undertow -> {
undertow.addBuilderCustomizers(builder -> builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true));
};
}
}
Démarrez Spring Boot et vérifiez que Undertow est HTTP / 2.
$ ./mvnw spring-boot:run
...
2017-05-18 09:02:40.807 INFO 60554 --- [ main] o.s.b.w.e.u.UndertowServletWebServer : Undertow started on port(s) 8080 (https)
2017-05-18 09:02:40.813 INFO 60554 --- [ main] c.e.s.Spring5WebDemoApplication : Started Spring5WebDemoApplication in 2.538 seconds (JVM running for 3.086)
$ curl -s -D - --http2 -k https://localhost:8080/
HTTP/2.0 404
content-type:application/json;charset=UTF-8
date:Thu, 18 May 2017 00:07:16 GMT
{"timestamp":"2017-05-18T00:07:15.967+0000","status":404,"error":"Not Found","message":"Not Found","path":"/"}
Note: La commande cURL sur Mac n'était pas compatible HTTP / 2, j'ai donc exécuté la commande suivante pour installer la commande cURL compatible HTTP / 2.
$ brew reinstall curl -- --with-nghttp2 $ brew link curl --force $ hash -r
Maintenant que nous avons confirmé qu'il a été converti en HTTP / 2, utilisons Chrome pour accéder au chemin qui exécute réellement HTTP / 2 Server Push.
C'est un peu difficile, mais vous pouvez voir que l'initiateur de "/style.css" est "Push / push-builder".
Warning:
Cependant ... Il semble que Server Push ne soit pas activé après la deuxième fois ... + Le journal suivant apparaît dans le journal du serveur ... Donc, quelque chose ne va pas Peut être: pleurer:
2017-05-18 11:02:02.813 ERROR 60871 --- [ XNIO-2 task-9] io.undertow : UT005085: Connection io.undertow.server.protocol.http2.Http2ServerConnection@35f2c3a7 for exchange HttpServerExchange{ GET /style.css request {accept=[text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8], accept-language=[en-US,en;q=0.8,ja;q=0.6], cache-control=[max-age=0], :authority=[localhost:8080], accept-encoding=[gzip, deflate, sdch, br], :path=[/style.css], user-agent=[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36], :scheme=[https], :method=[GET], Referer=[https://localhost:8080/push-builder], upgrade-insecure-requests=[1], Host=[localhost:8080]} response {Last-Modified=[Wed, 17 May 2017 19:36:35 GMT], Cache-Control=[no-store], Content-Length=[37], Content-Type=[text/css], Accept-Ranges=[bytes], Date=[Thu, 18 May 2017 02:02:02 GMT], :status=[200]}} was not closed cleanly, forcibly closing connection
MaxUploadSizeExceededException
: thumbsup:[SPR-9294]: MaxUploadSizeExceededException
( MultipartException
) lorsqu'une erreur surdimensionnée se produit dans la fonction de téléchargement de fichier prise en charge par Servlet 3.0. La sous-exception `) sera maintenant lancée.
Cependant, comme il est déterminé si le message d'erreur contient des mots spécifiques ("" taille "et" dépassé ""), "MultipartException" est lancé comme précédemment en fonction de l'implémentation du serveur d'application. Je pense que vous devriez être conscient qu'il y a une possibilité.
Note:
Spring Framework fournit un composant qui fonctionne avec Commons File Upload, et lorsqu'une taille est dépassée dans Commons File Upload, une exception `MaxUploadSizeExceededException 'est lancée.
Générons maintenant une erreur surdimensionnée. Créer un écran de téléchargement de fichier est un problème, donc ... Dans cette entrée, nous allons télécharger le fichier en utilisant la commande cURL.
Tout d'abord, créez un contrôleur qui reçoit le fichier téléchargé et démarrez Spring Boot.
package com.example.spring5webdemo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@RestController
public class UploadRestController {
@PostMapping("/upload")
String upload(MultipartFile file) {
return file.getOriginalFilename() + " is uploaded !";
}
}
Ensuite, essayez de télécharger un fichier dans 1 Mo (un fichier plus petit que la taille maximale) comme indiqué ci-dessous.
$ pwd
/usr/local/apps/spring5-web-demo
$ curl -s -D - -X POST -F [email protected] http://localhost:8080/upload
HTTP/1.1 100
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 21
Date: Tue, 16 May 2017 16:56:48 GMT
pom.xml is uploaded !
Maintenant, importons un fichier de 1 Mo ou plus. Au fait ... 1 Mo ou plus de fichiers (target / spring5-web-demo-0.0.1-SNAPSHOT.jar
) peuvent être créés avec ./mvnw package
.
$ ./mvnw package
...
$ curl -s -D - -X POST -F file=@target/spring5-web-demo-0.0.1-SNAPSHOT.jar http://localhost:8080/upload
HTTP/1.1 100
HTTP/1.1 500
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 16 May 2017 16:59:21 GMT
Connection: close
{"timestamp":"2017-05-16T16:59:21.255+0000","status":500,"error":"Internal Server Error","message":"Maximum upload size exceeded; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (14620179) exceeds the configured maximum (10485760)","path":"/upload"}
J'ai eu une erreur surdimensionnée, mais je ne sais pas si l'exception MaxUploadSizeExceededException
s'est produite, alors jetons un coup d'œil au journal du serveur.
Journal du serveur(console)
...
2017-05-17 01:59:21.246 ERROR 26570 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size exceeded; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (14620179) exceeds the configured maximum (10485760)] with root cause
org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (14620179) exceeds the configured maximum (10485760)
at org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:811) ~[tomcat-embed-core-8.5.15.jar:8.5.15]
...
En regardant le message du journal, il semble que «MaxUploadSizeExceededException» s'est produit, ajoutons donc un processus de gestion des exceptions pour «MaxUploadSizeExceededException».
package com.example.spring5webdemo;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartFile;
@RestController
public class UploadRestController {
@PostMapping("/upload")
String upload(MultipartFile file) {
return file.getOriginalFilename() + " is uploaded !";
}
@ExceptionHandler(MaxUploadSizeExceededException.class) //★★★ Ajout de la gestion des exceptions
@ResponseStatus(HttpStatus.PAYLOAD_TOO_LARGE)
String handleMaxUploadSizeExceededException() {
return "Upload file size is too large.";
}
}
De plus ... L'analyse des demandes en plusieurs parties est effectuée (analyse différée) après la détermination de la méthode Handler. Cela permettra à la méthode @ ExceptionHandler
de gérer les exceptions liées au téléchargement de fichiers.
src/main/resources/application.properties(Activer l'analyse des délais pour les demandes en plusieurs parties)
spring.servlet.multipart.resolve-lazily=true
Redémarrez Spring Boot et essayez à nouveau de télécharger un fichier de plus de 1 Mo.
$ curl -s -D - -X POST -F file=@target/spring5-web-demo-0.0.1-SNAPSHOT.jar http://localhost:8080/upload
HTTP/1.1 100
HTTP/1.1 413
Content-Type: text/plain;charset=UTF-8
Content-Length: 30
Date: Tue, 16 May 2017 17:01:38 GMT
Connection: close
Upload file size is too large.
Note:
Au fait ... Lors de l'utilisation de Spring Boot, la fonction de téléchargement de fichiers de Servlet 3.0 est activée par défaut, la taille maximale de chaque fichier est de 1M et la taille maximale de la requête entière est limitée à 10M. Vous pouvez modifier la taille maximale dans ʻapplication.properties`.
#Taille maximale par fichier spring.servlet.multipart.max-file-size=2MB #Taille maximale de la demande spring.servlet.multipart.max-request-size=20MB
[SPR-14484] [SPR-14908]: Unification Un mécanisme de résolution du type de média (classe MediaTypeFactory
) est ajouté et le code dépendant de JAF (Java Beans Activation Framework) est éliminé. Dans la nouvelle classe, résolvez les types de média selon la définition de / org / springframework / http / mime.types
(type de média et fichier de mappage d'extension) stocké dans spring-web
. ..
Par exemple, si vous créez la méthode Handler suivante et téléchargez le fichier pom.xml
...
@PostMapping("/upload")
String upload(MultipartFile file) {
MediaTypeFactory.getMediaTypes(file.getOriginalFilename())
.forEach(System.out::println);
return file.getOriginalFilename() + " is uploaded !";
}
Les types de média suivants sont envoyés à la console.
application/xml
De plus, vous pouvez personnaliser la définition de mappage fournie par spring-web
en créant (copiant) / org / springframework / http / mime.types
dans votre projet.
src/main/resources/org/springframework/http/mime.types
...(réduction)
text/xml xml
Si vous téléchargez à nouveau le fichier pom.xml
... Les types de média suivants seront sortis sur la console.
application/xml
text/xml
[SPR-15199]: permet la liaison de données des paramètres de requête à des objets immuables. Si les informations de nom de paramètre existent dans l'argument du constructeur (si -d
ou -paramètres
est ajouté à l'option de compilation), la valeur du paramètre de demande qui correspond au nom de paramètre de l'argument du constructeur et le nom du paramètre de demande est liée. Sera fait.
Note:
S'il y a plusieurs constructeurs, le constructeur par défaut sera appelé comme précédemment. De plus, si vous ne voulez pas laisser les informations de nom de paramètre dans l'argument constructeur, vous pouvez utiliser
@ java.beans.ConstructorProperties
fourni par Java Beans pour mapper l'argument constructeur sur le paramètre request.
Créons maintenant une classe immuable et spécifions cette classe comme argument de la méthode Handler.
package com.example.spring5webdemo;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotNull;
import java.time.LocalDate;
@RestController
public class ImmutableObjectRestController {
@GetMapping("/immutable")
Query search(@Validated Query query) {
return query;
}
public static class Query {
@NotNull
private final String name;
private final String mail;
private final String tel;
private final LocalDate baseDate;
public Query(String name, String mail, String tel,
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate baseDate) {
this.name = name;
this.mail = mail;
this.tel = tel;
this.baseDate = baseDate;
}
public String getName() {
return name;
}
public String getMail() {
return mail;
}
public String getTel() {
return tel;
}
public LocalDate getBaseDate() {
return baseDate;
}
}
}
Dans l'état par défaut, si LocalDate
est défini sur JSON, la lisibilité est très mauvaise, nous allons donc afficher une valeur formatée comme ça.
pom.xml
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId> <!-- JSR-Ajout d'un module pour prendre en charge la sérialisation et la désérialisation de 310 classes-->
</dependency>
src/main/resources/application.properties
spring.jackson.serialization.write-dates-as-timestamps=false
Tout d'abord, essayez d'envoyer une requête avec toutes les valeurs normales.
$ curl -s -D - http://localhost:8080/immutable?name=kazuki43zoo\&[email protected]\&tel=09012345678\&baseDate=2017-08-01
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 16 May 2017 17:02:24 GMT
{"name":"kazuki43zoo","mail":"[email protected]","tel":"09012345678","baseDate":"2017-08-01"}
Vous êtes lié correctement.
Ensuite, vérifions si la validation est effectuée sans spécifier nom
.
$ curl -s -D - http://localhost:8080/[email protected]\&tel=09012345678\&baseDate=2017-08-01
HTTP/1.1 400
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 16 May 2017 17:04:25 GMT
Connection: close
{"timestamp":"2017-05-16T17:04:25.515+0000","status":400,"error":"Bad Request","errors":[{"codes":["NotNull.query.name","NotNull.name","NotNull.java.lang.String","NotNull"],"arguments":[{"codes":["query.name","name"],"arguments":null,"defaultMessage":"name","code":"name"}],"defaultMessage":"may not be null","objectName":"query","field":"name","rejectedValue":null,"bindingFailure":false,"code":"NotNull"}],"message":"Validation failed for object='query'. Error count: 1","path":"/immutable"}
Cela semble également fonctionner correctement.
Enfin, spécifiez une date non valide pour baseDate
( LocalDate
) et vérifiez si une erreur de liaison se produit.
$ curl -s -D - http://localhost:8080/immutable?name=kazuki43zoo\&[email protected]\&tel=09012345678\&baseDate=2017-08-32
HTTP/1.1 400
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 16 May 2017 17:05:10 GMT
Connection: close
{"timestamp":"2017-05-16T17:05:10.161+0000","status":400,"error":"Bad Request","message":"Failed to convert value of type 'java.lang.String[]' to required type 'java.time.LocalDate'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.format.annotation.DateTimeFormat java.time.LocalDate] for value '2017-08-32'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2017-08-32]","path":"/immutable"}
Il semble que cela fonctionne également correctement.
Warning:
J'ai changé le style pour recevoir les informations d'erreur au moment de l'erreur de liaison et de l'erreur de validation en tant que
BindingResult
comme suit ... J'ai pu recevoir l'erreur de validation, mais une exception s'est produite au moment de l'erreur de liaison Je n'ai pas pu le recevoir. (Est-ce une spécification !? ...)@GetMapping("/immutable") Query search(@Validated Query query, BindingResult bindingResult) { // ... return query; }
Je n'étais pas sûr s'il s'agissait d'une spécification ou d'un bug, j'ai donc donné un problème (SPR-15542) à Spring JIRA.
[SPR-14925]: Apparemment Jackson 2.9 ) Semble être la version requise sur Spring Framework 5.0. Jackson 2.9 n'a pas été officiellement publié pour le moment (14 mai 2017), mais il semble que Spring Framework 5.0 sera publié au moment de la sortie officielle. En regardant les changements dans Spring Framework ... À partir de Jackson 2.9, si l'erreur se produit en raison d'un problème avec l'implémentation côté serveur (implémentation POJO), ʻInvalidDefinitionException` sera levée, donc cette exception s'est produite. Il semble que je l'ai parfois corrigé pour qu'il s'agisse d'une erreur de serveur (500 Internal Server Error) au lieu d'une erreur de client (400 Bad Request).
[SPR-14923]: composant Java EE 8 JSON Binding Il est pris en charge et peut être utilisé comme alternative à Jackson et GSON.
Tout d'abord, Spring Boot (spring-boot-starter-web
) dépend de Jackson, donc si vous voulez utiliser JSON-B, vous devez supprimer Jackson des bibliothèques dépendantes et ajouter la bibliothèque pour JSON-B. Il y a.
pom.xml
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <!--Exclure Jackson-->
</exclusion>
</exclusions>
</dependency>
<!-- ... -->
<!--Cette entrée est l'implémentation de référence officielle de Yasson(+JSON-Mise en œuvre P)Ajouter-->
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>yasson</artifactId>
<version>1.0.0-M2</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.1.0-M2</version>
</dependency>
</dependencies>
<!-- ... -->
<repositories>
<!-- ... -->
<!--Ajout du référentiel de versions de Yasson(Pour pouvoir obtenir la version jalon) -->
<repository>
<id>yasson-releases</id>
<name>Yasson Release repository</name>
<url>https://repo.eclipse.org/content/repositories/yasson-releases/</url>
</repository>
</repositories>
Ensuite, créez la classe Controller. Il n'y a pas de précautions particulières pour créer la classe Controller, mais il semble qu'elle ne prend pas en charge la liaison à des classes immuables.
package com.example.spring5webdemo;
import java.time.LocalDate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class JsonbRestController {
@PostMapping("/jsonb")
Resource post(@RequestBody Resource resource) {
return resource;
}
public static class Resource {
private String name;
private String mail;
private String tel;
private LocalDate baseDate;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public LocalDate getBaseDate() {
return baseDate;
}
public void setBaseDate(LocalDate baseDate) {
this.baseDate = baseDate;
}
}
}
Lorsque vous accédez au point de terminaison que vous avez créé, vous pouvez voir que le JSON demandé a été converti (désérialisé) en objet Java et que cet objet a été converti (sérialisé) en JSON.
$ curl -s -D - -X POST -H "Content-Type:application/json" -d '{"name":"kazuki43zoo","mail":"[email protected]","tel":"09012345678","baseDate":"2017-08-01"}' http://localhost:8080/jsonb
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Content-Length: 97
Date: Tue, 16 May 2017 17:08:40 GMT
{"baseDate":"2017-08-01","mail":"[email protected]","name":"kazuki43zoo","tel":"09012345678"}
JSON-B est également disponible dans RestTemplate
, une classe pour les clients HTTP. Vous pouvez également utiliser JSON-B avec le client HTTP de test (TestRestTemplate
) fourni par Spring Boot.
package com.example.spring5webdemo;
import java.time.LocalDate;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) //★★★ Démarrez le serveur en utilisant un port libre lors de l'exécution du test
public class JsonbTests {
@LocalServerPort private int port; //★★★ Le port de démarrage du serveur est injecté
@Autowired TestRestTemplate testRestTemplate; //★★★ Le client HTTP est injecté pour accéder au port de démarrage du serveur
@Test
public void testUsingRestTemplate() {
LocalDate now = LocalDate.now();
RestOperations restOperations = new RestTemplate(); //★★★ JSON dans l'état par défaut-Le composant lié avec B est enregistré
Resource request = new Resource();
request.setName("kazuki43zoo");
request.setMail("[email protected]");
request.setTel("09012345678");
request.setBaseDate(now);
Resource response = restOperations.postForObject("http://localhost:" + port + "/jsonb", request, Resource.class);
Assertions.assertThat(response.getName()).isEqualTo("kazuki43zoo");
Assertions.assertThat(response.getMail()).isEqualTo("[email protected]");
Assertions.assertThat(response.getTel()).isEqualTo("09012345678");
Assertions.assertThat(response.getBaseDate()).isEqualTo(now);
}
@Test
public void testUsingTestRestTemplate() {
LocalDate now = LocalDate.now();
Resource request = new Resource();
request.setName("kazuki43zoo");
request.setMail("[email protected]");
request.setTel("09012345678");
request.setBaseDate(now);
Resource response = testRestTemplate.postForObject("/jsonb", request, Resource.class); //★★★ Si vous utilisez le client HTTP pour les tests, vous pouvez spécifier l'URL à partir du chemin.
Assertions.assertThat(response.getName()).isEqualTo("kazuki43zoo");
Assertions.assertThat(response.getMail()).isEqualTo("[email protected]");
Assertions.assertThat(response.getTel()).isEqualTo("09012345678");
Assertions.assertThat(response.getBaseDate()).isEqualTo(now);
}
public static class Resource {
private String name;
private String mail;
private String tel;
private LocalDate baseDate;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public LocalDate getBaseDate() {
return baseDate;
}
public void setBaseDate(LocalDate baseDate) {
this.baseDate = baseDate;
}
}
}
[SPR-13589]: ProtobufHttpMessageConverter
prend désormais en charge Google Protobuf 3.x. Apparemment, les formats pris en charge par défaut (com.google.protobuf: protobuf-java
) sont uniquement" ʻapplication / x-protobuf"et"
text / plain" et d'autres formats ("ʻapplication / Pour json
"" ʻapplication / xml""
text / html"), des bibliothèques séparées ("
com.google.protobuf: protobuf-java-util(officiel) "et"
com.googlecode.protobuf-" Il semble qu'il soit nécessaire d'ajouter java-format: protobuf-java-format` (fait par un tiers) ").
[SPR-15365]: En relation avec le support du modèle de programmation réactif (Spring WebFlux décrit plus loin), Spring MVC dispose également de la classe Reactor 3.1 (Flux). Les classes
, Mono
), RxJava 1.3 et 2.1 (ʻObservable,
Sigle,
Flowable`, etc.) peuvent maintenant être traitées comme la valeur de retour de la méthode Handler. Lorsque ces classes (ci-après dénommées «classe réactive») sont renvoyées, Spring MVC est un mécanisme à traiter à l'aide du traitement asynchrone de Spring MVC (fonction de liaison avec traitement asynchrone pris en charge depuis Servlet 3.0). Il est devenu.
Publié précédemment "[Understanding Asynchronous Requests on Spring MVC (+ Spring Boot)]](http://qiita.com/kazuki43zoo/items/ce88dea403c596249e8a#spring-mvc%E3%81%8C%E3%82% B5% E3% 83% 9D% E3% 83% BC% E3% 83% 88% E3% 81% 97% E3% 81% A6% E3% 81% 84% E3% 82% 8B% E9% 9D% 9E% E5% 90% 8C% E6% 9C% 9F% E5% 87% A6% E7% 90% 86% E3% 81% AE% E6% 96% B9% E5% BC% 8F) ", Spring MVC Le traitement asynchrone de est divisé selon les deux méthodes suivantes.
La méthode utilisée dépend de la combinaison du type de classe Reactive et du type de média à renvoyer. Plus précisément, "[Spring Framework Reference -Async Requests with Reactive Types-](http://docs.spring.io/spring/docs/5.0.0.RC1/spring-framework-reference/web.html#mvc -ann-async-réactifs-types) ".
Maintenant, retournons en fait la classe Reactive. Étant donné que cette entrée utilise la classe Reactor utilisée par Spring WebFlux, ajoutez d'abord Reactor à la bibliothèque dépendante.
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.1.0.M1</version>
</dependency>
Ensuite, implémentez la méthode Handler qui retourne le "Mono" et le "Flux" du Reactor.
package com.example.spring5webdemo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import java.time.Duration;
import java.util.Random;
@RestController
public class ReactiveRestController {
@GetMapping("/reactor/mono")
Mono<Resource> getMono() {
//Une fois abonné, renvoyez la ressource avec l'ID défini sur un nombre aléatoire
//Appelez subscribeOn pour que le traitement de l'abonné soit dans un thread séparé
// ->Vous n'êtes pas obligé de le faire dans un thread distinct pour traiter cet exemple de code,
// ->En fait, on suppose qu'un "traitement lourd = traitement lié au processeur" est effectué.
return Mono.fromSupplier(() -> new Resource(new Random().nextInt()))
.subscribeOn(Schedulers.parallel());
}
@GetMapping("/reactor/flux")
Flux<Resource> getFlux() {
//Une fois abonné, définissez Ressource avec un identifiant de 0 à 0.Retour toutes les 5 secondes
//Si l'intervalle est utilisé, le traitement de l'abonné sera effectué dans un thread séparé.
return Flux.from(Flux.range(0, 5)).zipWith(Flux.interval(Duration.ofMillis(500)))
.map(tuple -> new Resource(tuple.getT1()));
}
static class Resource {
private final int id;
private Resource(int id) {
this.id = id;
}
public int getId() {
return id;
}
}
}
Enfin, accédons à la méthode Handler que nous avons créée.
Mono+json
$ curl -s -D - http://localhost:8080/reactor/mono
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 16 May 2017 17:10:34 GMT
{"id":129024480}
Flux+event-stream
$ curl -s -D - http://localhost:8080/reactor/flux -H Accept:text/event-stream
HTTP/1.1 200
Content-Type: text/event-stream;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 16 May 2017 17:10:55 GMT
data:{"id":0}
data:{"id":1}
data:{"id":2}
data:{"id":3}
data:{"id":4}
Flux+stream+json
$ curl -s -D - http://localhost:8080/reactor/flux -H Accept:application/stream+json
HTTP/1.1 200
Content-Type: application/stream+json
Transfer-Encoding: chunked
Date: Tue, 16 May 2017 17:11:22 GMT
{"id":0}
{"id":1}
{"id":2}
{"id":3}
{"id":4}
Au fait ... Si vous retournez Flux
avec ʻapplication / json` ...
$ curl -s -D - http://localhost:8080/reactor/flux -H Accept:application/json
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 16 May 2017 17:11:48 GMT
[{"id":0},{"id":1},{"id":2},{"id":3},{"id":4}]
Et le JSON recevra une réponse une fois que toutes les données auront été transmises à l'abonné (environ 2,5 secondes plus tard).
ParsingPathMatcher
est ajouté: thumbsup:[SPR-14544]: ParsingPathMatcher
est ajouté comme alternative à ʻAntPathMatcher. Spring MVC utilise ʻAntPathMatcher
par défaut, mais les composants liés à Spring WebFlux semblent utiliser ParsingPathMatcher
par défaut.
Je ne l'ai pas vu correctement ...: sweat_smile: ʻIl semble que ce qui pourrait être exprimé avec AntPathMatcher peut aussi être exprimé avec
ParsingPathMatcher, et
ParsingPathMatcherest nouvellement appelé"
{* nom de la variable}` " Les expressions sont prises en charge.
Prenons l'exemple suivant ... La spécification de chemin d'accès est la même que "/ users / **
", mais si vous utilisez "{* nom de variable}
", ce sera "
/ La partie "" peut être traitée comme une variable. Notez que vous ne pouvez pas spécifier de caractères après "
{ nom de la variable} " tels que "
/ users / { userPaths} .json` ".
package com.example.spring5webdemo;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.springframework.util.PathMatcher;
import org.springframework.web.util.ParsingPathMatcher;
public class PathMatcherTests {
@Test
public void parsingPathMatcher() {
PathMatcher pathMatcher = new ParsingPathMatcher();
String pattern = "/users/{*userPaths}";
{
String path = "/users/kazuki43zoo/name";
Assertions.assertThat(pathMatcher.match(pattern, path));
Assertions.assertThat(pathMatcher.extractUriTemplateVariables(pattern, path))
.containsEntry("userPaths", "/kazuki43zoo/name");
}
{
String path = "/users/kazuki43zoo";
Assertions.assertThat(pathMatcher.match(pattern, path));
Assertions.assertThat(pathMatcher.extractUriTemplateVariables(pattern, path))
.containsEntry("userPaths", "/kazuki43zoo");
}
{
String path = "/users/";
Assertions.assertThat(pathMatcher.match(pattern, path));
Assertions.assertThat(pathMatcher.extractUriTemplateVariables(pattern, path))
.containsEntry("userPaths", "/");
}
{
String path = "/users";
Assertions.assertThat(pathMatcher.match(pattern, path));
Assertions.assertThat(pathMatcher.extractUriTemplateVariables(pattern, path))
.containsEntry("userPaths", "");
}
}
}
Si vous souhaitez utiliser ParsingPathMatcher
dans le traitement de correspondance des requêtes Spring MVC, vous pouvez le faire en définissant le Bean suivant.
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
//★★★ Appliquer ParsingPathMatcher
configurer.setPathMatcher(new ParsingPathMatcher());
//★★★ Désactiver useSuffixPatternMatch
//★★★ Lorsque useSuffixPatternMatch est activé, "" à la fin du modèle pendant le traitement de la correspondance des demandes`.*`Est donné et une erreur se produit pendant l'analyse de modèle
configurer.setUseSuffixPatternMatch(false);
}
};
}
package com.example.spring5webdemo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PathMatcherRestController {
@GetMapping("/path-matcher/{*paths}") // ★★★ {*Nom de variable}Pour recevoir une valeur de variable comme argument de méthode en utilisant
String get(@PathVariable String paths) {
return paths;
}
}
$ curl -s -D - http://localhost:8080/path-matcher/a/b/c
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 6
Date: Tue, 16 May 2017 19:41:41 GMT
/a/b/c
Note:
Au fait ... Quand j'ai utilisé "
{* nom de la variable}
" dans Spring WebFlux oùParsingPathMatcher
est appliqué par défaut, une erreur s'est produite dans le processus de mappage de la demande ...: déçu_relieved: Lorsque vous utilisez "{* nom de la variable}
", est-ce un bogue qui doit être défini sur "ʻuseSuffixPatternMatch = false`"? J'en ai envie, j'ai donc créé Spring JIRA (SPR-15558).
RedirectAttributes
peut être reçu par la méthode @ ExceptionHandler
: thumbsup:[SPR-14651]: Vous pouvez maintenant recevoir RedirectAttributes
comme argument de la méthode @ ExceptionHandler
et rediriger vers via RedirectAttributes
. Vous pourrez lier des données avec.
Note:
En regardant le problème, il est également rétroporté vers Spring Framework 4.3 ... Je me demande si c'est un problème ...
Normalement, il est préférable de créer un écran et de le vérifier, mais ... Il est difficile de créer un écran, donc vérifiez en utilisant cURL.
package com.example.spring5webdemo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
public class RedirectAttributesController {
@GetMapping("/redirect/{id}")
@ResponseBody
String getIdWithMessage(@PathVariable int id, @ModelAttribute("message") String message) { //La valeur définie dans RedirectAttributes peut être obtenue comme argument de méthode
return id + " : " + message;
}
@GetMapping("/redirect")
String get() {
throw new MyException("error.", 100);
}
@ExceptionHandler
String handleMyException(MyException e, RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("message", e.getMessage()); //★★★ Définir le message dans la portée Flush
redirectAttributes.addAttribute("id", e.getId()); //★★★ Variable de chemin du chemin de redirection "{id}Définissez la valeur à intégrer
return "redirect:/redirect/{id}";
}
private static class MyException extends RuntimeException {
private final int id;
public MyException(String message, int id) {
super(message);
this.id = id;
}
public int getId() {
return id;
}
}
}
$ curl -s -D - -L http://localhost:8080/redirect
HTTP/1.1 302
Set-Cookie: JSESSIONID=13743780747DC2A73F038F2E0DECFDDD; Path=/; HttpOnly
Location: http://localhost:8080/redirect/100;jsessionid=13743780747DC2A73F038F2E0DECFDDD
Content-Language: ja-JP
Content-Length: 0
Date: Tue, 16 May 2017 21:46:50 GMT
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 12
Date: Tue, 16 May 2017 21:46:50 GMT
100 : error.
ResponseStatusException
est ajouté: thumbsup:[SPR-14895]: ResponseStatusException
créé pour Spring WebFlux est maintenant disponible dans Spring MVC, avec le code d'état et la phrase de raison En spécifiant et en générant / lançant une exception, il devient possible de déterminer l'état HTTP pour répondre.
package com.example.spring5webdemo;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
@RestController
public class ResponseStatusExceptionRestController {
@GetMapping("/response-status")
void get() {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "request invalid."); //★★★ Lancez une exception en spécifiant un code de statut arbitraire et une phrase de raison
}
}
$ curl -s -D - -L http://localhost:8080/response-status
HTTP/1.1 400
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 16 May 2017 22:34:21 GMT
Connection: close
{"timestamp":"2017-05-16T22:34:21.909+0000","status":400,"error":"Bad Request","message":"request invalid.","path":"/response-status"}
ScriptTemplateView
: thumbsup:[SPR-15064]: "Internationalisation des messages" dans ScriptTemplateView
(Voir l'implémentation à l'aide du moteur de script de JSR-223)" Et les objets (Locale
, MessageSource
, etc.) nécessaires pour réaliser la "fragmentation du modèle" seront passés du côté du moteur de modèle.
L'implémentation proprement dite pour «l'internationalisation» et les «modèles de fragmentation» n'est pas fournie par Spring.
Dans cette entrée, "nashorn" fourni par Java SE est utilisé comme moteur de script, et "moustache.js" est utilisé comme moteur de modèle. Et la "fragmentation du modèle" sera réalisée.
Tout d'abord, configurez pour utiliser ScriptTemplateView
.
pom.xml(Installez moustachejs)
<dependency>
<groupId>org.webjars.bower</groupId>
<artifactId>mustache</artifactId>
<version>2.3.0</version> <!--Dernière version au moment de la rédaction-->
</dependency>
package com.example.spring5webdemo;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.script.ScriptTemplateConfigurer;
import java.util.Optional;
@Configuration
public class ScriptTemplateViewConfig implements WebMvcConfigurer {
private final WebMvcProperties properties;
public ScriptTemplateViewConfig(WebMvcProperties properties) {
this.properties = properties;
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
WebMvcProperties.View view = properties.getView();
registry.scriptTemplate()
.prefix(Optional.ofNullable(view.getPrefix()).orElse("classpath:/templates/"))
.suffix(Optional.ofNullable(view.getSuffix()).orElse(".html"));
}
@Bean
ScriptTemplateConfigurer ScriptTemplateConfigurer() {
ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
configurer.setEngineName("nashorn");
configurer.setScripts("/META-INF/resources/webjars/mustache/2.3.0/mustache.js", "/META-INF/js/render.js");
configurer.setRenderFunction("render");
return configurer;
}
}
Créez un fichier JavaScript pour personnaliser le processus de rendu standard de Moustache.
J'ai utilisé le terme de personnalisation, mais en réalité, je viens d'ajouter une fonction personnalisée au modèle et j'ai appelé la fonction render
de Moustache.
/main/resources/META-INF/js/render.js
function render(template, model, renderingContext) {
model["message"] = function () { //★★★ Ajout d'une fonction personnalisée pour "Internationalisation des messages"
return function (code, render) {
var messageSource = renderingContext.applicationContext.getBean("messageSource");
var locale = renderingContext.locale;
return render(messageSource.getMessage(code.trim(), null, locale));
}
};
model["include"] = function () { //★★★ Ajout d'une fonction personnalisée pour la «fragmentation des modèles»
return function (viewName, render) {
return render(renderingContext.templateLoader.apply("templates/" + viewName.trim() + ".html"));
}
};
return Mustache.render(template, model, renderingContext);
}
[RenderingContext
](https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet" dans le troisième argument de la méthode de rendu Une instance de /view/script/RenderingContext.java) est transmise, alors récupérez-y les objets nécessaires et traitez-la.
Maintenant, créons un fichier modèle et un contrôleur et exécutons-les.
src/main/resources/templates/script-template/home.html
<html>
<head>
<title>{{title}}</title>
</head>
<body>
<p>{{body}}</p>
<p>{{#message}}welcome{{/message}}</p> <!--★★★ Émet un message à l'aide d'une fonction personnalisée-->
{{#include}}script-template/footer{{/include}} <!--★★★ Inclure des fragments à l'aide de fonctions personnalisées-->
</body>
</html>
src/main/resources/templates/script-template/footer.html(Fragment)
<footer>
Copyright (c) 2017 kazuki43zoo@com
</footer>
package com.example.spring5webdemo;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.http.HttpServletResponse;
@Controller
public class ScriptTemplateController {
@GetMapping("/script-template")
String home(Model model, HttpServletResponse response) {
model.addAttribute("title", "Sample title")
.addAttribute("body", "Sample body");
return "script-template/home";
}
}
Vous devez également créer un fichier de définition de message.
src/main/resources/messages.properties
welcome=salut!
src/main/resources/messages_en.properties
welcome=Hello!
Lorsque vous démarrez Spring Boot et accédez à http: // localhost: 8080 / script-template ...
$ curl -s -D - http://localhost:8080/script-template
HTTP/1.1 200
Content-Type: text/html;charset=UTF-8
Content-Language: ja-JP
Content-Length: 183
Date: Sat, 20 May 2017 01:51:53 GMT
<html>
<head>
<title>Sample title</title>
</head>
<body>
<p>Sample body</p>
<p>salut!</p>
<footer>
Copyright (c) 2017 kazuki43zoo.com
</footer>
</body>
</html>
Si vous changez ʻAccept-Language en ʻen
(anglais) et accédez à nouveau ...
$ curl -s -D - -H Accept-Language:en http://localhost:8080/script-template
HTTP/1.1 200
Content-Type: text/html;charset=UTF-8
Content-Language: en
Content-Length: 171
Date: Sat, 20 May 2017 01:53:46 GMT
<html>
<head>
<title>Sample title</title>
</head>
<body>
<p>Sample body</p>
<p>Hello!</p>
<footer>
Copyright (c) 2017 kazuki43zoo.com
</footer>
</body>
</html>
Vous pouvez confirmer que "Internationalisation du message" et "Inclure le fragment" ont été effectués.
Cette fois, nous avons présenté les principaux changements liés à WebMVC. Le framework Web du modèle de programmation réactif (Spring WebFlux) a été ajouté à partir de Spring Framework 5.0, et il attire l'attention, mais ... Spring MVC est toujours actif (pour le moment ... j'utilise Spring MVC pour le travail. Je pense qu'il y en aura beaucoup plus: sweat_smile :). La prochaine fois, je présenterai "Changements majeurs liés à Test".
Recommended Posts