[JAVA] Wichtige Änderungen in Bezug auf Spring Framework 5.0 Web MVC

Dies ist die vierte Ausgabe der Reihe "Wichtige Änderungen an Spring Framework 5.0" sowie die wichtigsten Änderungen (neue Funktionen und Verbesserungen usw.) im Zusammenhang mit WebMVC. Ich möchte vorstellen.

Serie

Version zur Überprüfung des Betriebs

Basislinie


Individuell


Dieses Mal habe ich Spring Boot 2.0.0.M1 (das standardmäßig von Spring Framework 5.0.0.RC1 abhängt) verwendet, um den Vorgang zu überprüfen. Im Folgenden finden Sie eine kurze Einführung in die Vorgehensweise beim Erstellen eines Überprüfungsprojekts.

Note:

Es kann auch mit den Funktionen SPRING INITIALIZR und IDE erstellt werden.

$ 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)
Auf dem Terminal "Strg+ C」!!

Änderungen im Zusammenhang mit WebMVC

In Spring Framework 5.0 wurden die folgenden Änderungen an der WEB-Funktion vorgenommen.

Artikelnummer Änderungen
1 Der von Spring Framework bereitgestellte Servlet-Filter ist Servlet 3.Es wird eine Implementierung sein, die der API-Spezifikation von 1 entspricht. (Eigentlich weiß ich nicht, wie es entsprach ...:sweat_smile:) [Zu Details:arrow_right:]
2 Servlet 4, eine Komponente von Java EE 8, als Argument der Handler-Methode von Spring MVC.Hinzugefügt bei 0PushBuilder(HTTP/API für Server Push von 2)Sie können es empfangen.[Zu Details:arrow_right:]
3 Servlet 3.Wenn ein Übergrößenfehler für die von 0 unterstützte Datei-Upload-Funktion auftrittMaxUploadSizeExceededException(MultipartExceptionUnterausnahme)Wird geworfen.[Zu Details:arrow_right:]

**Note:**BestimmteWörterinderFehlermeldung("size"Wann"exceed")Ist enthalten, ist es also abhängig von der Implementierung des Anwendungsservers derselbe wie zuvor.MultipartExceptionがスローされる可能性があるWannいう点は意識しておいた方がよいでしょう。
4 Einheitlicher Auflösungsmechanismus für Medientypen(MediaTypeFactoryKlasse)Wird hinzugefügt werden.[Zu Details:arrow_right:]

**Note:**MitdieserKorrespondenzhatJAF(JavaBeansActivationframework)Abhängiger Code wird eliminiert.
5 Die Datenbindung an unveränderliche Objekte wird unterstützt.[Zu Details:arrow_right:]
6 Jackson 2.9 wird unterstützt.[Zu Details:arrow_right:]
7 Eine Komponente von Java EE 8JSON BindingWirdunterstützt.

**Note:**EskannalsAlternativezuJacksonundGSONverwendetwerden.[ZuDetails:arrow_right:]
8 Google Protobuf 3.x wird unterstützt.[Zu Details:arrow_right:]
9 Reactive Programing Model(Spring WebFlux wird später beschrieben)Mit der Unterstützung von Reaktor 3.1 Klasse(Flux, Mono)、RxJava 1.3 und 2.1 Klasse(Observable, Sigle, FlowableEine solche)Kann als Rückgabewert der Handler-Methode behandelt werden.[Zu Details:arrow_right:]
10 AntPathMatcherAls Alternative zuParsingPathMatcherWird hinzugefügt werden.[Zu Details:arrow_right:]

**Note:**Für Spring WebFlux-bezogene KomponentenParsingPathMatcherWird standardmäßig verwendet.
11 @ExceptionHandlerAls MethodenargumentRedirectAttributesSie können es empfangen.(=RedirectAttributesSie können die Daten über mit dem Umleitungsziel verknüpfen) [Zu Details:arrow_right:]
12 ResponseStatusExceptionWird hinzugefügt, und Sie können den HTTP-Status steuern, indem Sie eine Ausnahme generieren und auslösen, indem Sie einen beliebigen Statuscode und eine beliebige Begründung angeben.[Zu Details:arrow_right:]

Note: ResponseStatusExceptionScheint für Spring WebFlux erstellt worden zu sein, wird jedoch auch für die Verwendung mit Spring MVC unterstützt.
13 ScriptTemplateView(JSR-Zeigen Sie die Implementierung mit der 223-Skript-Engine an)Objekte, die erforderlich sind, um "Internationalisierung von Nachrichten" und "Fragmentierung von Vorlagen" zu realisieren.(Locale,MessageSourceEine solche)Wird an die Template-Engine-Seite übergeben.[Zu Details:arrow_right:]

Note: 「国際化」や「テンプレートをフラグメント化」するための実装自体はSpringからは提供されないので、SpringのテストケースEine solcheを参考に開発者が実装する必要があります。

Entspricht die Implementierung des Servlet-Filters der Servlet 3.1-API-Spezifikation? : Daumen hoch:

[SPR-14467]: Anscheinend handelt es sich um eine reine (nicht auf Servlet-Filter beschränkte) Gesamtimplementierung auf Basis der Servlet 3.1-API-Spezifikation ( = Betrieb auf Servlet 3.0 wird nicht unterstützt)! Es scheint, dass. In dieser Hinsicht wurde bis zu Spring Framework 4.3 Code implementiert, um die Kompatibilität mit der Servlet 2.5-Umgebung aufrechtzuerhalten. Ab Spring Framework 5.0 wird die Unterstützung für die Servlet 2.5-Umgebung jedoch überhaupt eingestellt ([SPR-13189]). ](Https://jira.spring.io/browse/SPR-13189)).

Servlet 4.0 PushBuilder kann mit der Handler-Methode empfangen werden: thumbsup:

[SPR-12674] PushBuilder wurde in Servlet 4.0, einer Komponente von Java EE 8, als Argument der Handler-Methode von Spring MVC hinzugefügt. Sie können empfangen (API für HTTP / 2 Server Push).

Lassen Sie uns ein einfaches Beispiel implementieren und HTTP / 2 Server Push in Aktion ausprobieren. Erstellen Sie zunächst eine HTML-Datei mit einem Link in der CSS-Datei und der CSS-Datei.

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"/> <!--★★★ Link zur CSS-Datei-->
</head>
<body>
<h1>Hello World !!</h1>
</body>
</html>

Erstellen Sie als Nächstes einen Controller.

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) { //★★★ Deklarieren Sie PushBuilder als Argument
		builder.path("/style.css").push(); //★★★ Stellen Sie die CSS-Datei auf den Pfad ein, der verschoben und verschoben werden soll
		return "forward:/hello.html"; //Name der Ansicht für den Übergang zu HTML zurückgeben
	}

}

Ich möchte sagen: "Starten wir den Server und überprüfen ihn!", Aber ich glaube nicht, dass er kompiliert werden kann, da die API ("PushBuilder") von Servlet 4.0 überhaupt nicht gefunden wird. Also ... Ich möchte die Datei "pom.xml" so ändern, dass ein Anwendungsserver verwendet wird, der Servlet 4.0 unterstützt. Zuerst dachte ich, ich würde es mit Tomcat 9.0 versuchen, aber Undertow schien einfacher in HTTP / 2 zu konvertieren, also verwendete ich hier Undertow 2.0 (eine in Entwicklung befindliche Version), die Servlet 4.0 unterstützt. Um den Betrieb zu überprüfen.

Note:

Undertow 2.0.0.Alpha1 wurde für Maven Central freigegeben, aber diese Version ist leider nicht verfügbar. Der Grund dafür ist, dass sich die API-Spezifikationen von Servlet 4.0 in der Mitte geändert haben und die Änderungen nicht übernommen wurden. Also ... Holen Sie sich in diesem Eintrag den Quellcode von GitHub und installieren Sie Undertow 2.0.0.Alpha2-SNAPSHOT in Ihrem lokalen Repository.

$ git clone https://github.com/undertow-io/undertow.git
$ cd undertow
$ mvn -U install -DskipTests=true

Ändern Sie die Datei pom.xml, um Undertow 2.0.0.Alpha2-SNAPSHOT anstelle von Tomcat zu verwenden.

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 Geben Sie die Support-Version an(Version im lokalen Repository installiert) -->
</properties>

<!-- ... -->

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <!--★★★ Tomcat ausschließen-->
    <exclusions>
      <exclusion>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
      </exclusion>
    </exclusions>
  </dependency>

  <!-- ... -->

  <!--★★★ Undertow hinzugefügt-->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
  </dependency>

  <!-- ★★★ alpn-Boot hinzufügen-->
  <!-- 
    ALPN (Application Layer Protocol Negotiation)Einstellungen zum Herunterladen der Implementierungsbibliothek von.
Die heruntergeladene Datei ist eine Startoption, wenn die Anwendung ausgeführt wird.(-Xbootclasspath)Angegeben als.
  -->
  <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 zum Starten der ALPN-Implementierungsbibliothek(-Xbootclasspath)Bezeichnet für-->
        <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>

<!-- ... -->

Um mit HTTP / 2 zu kommunizieren, muss die Kommunikation zwischen dem Server und dem Browser eine SSL / TLS-Kommunikation über HTTPS sein. Erstellen Sie daher einen Schlüsselspeicher, in dem das selbstsignierte Zertifikat gespeichert ist.

$ keytool -genkey -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore src/main/resources/keystore.p12 -validity 3650
Bitte geben Sie das Keystore-Passwort ein:  
Bitte geben Sie Ihr neues Passwort erneut ein: 
Wie lautet Ihr Vor- und Nachname?
 [Unknown]:  Sample
Wie lautet der Name der Organisationseinheit?
 [Unknown]:  Sample
Wie lautet der Name der Organisation?
 [Unknown]:  Sample
Wie heißt die Stadt oder Region?
 [Unknown]:  Sample
Wie lautet der Staat oder der Name des Staates?
 [Unknown]:  Sample
Wie lautet der aus zwei Buchstaben bestehende Ländercode für dieses Gerät?
 [Unknown]:  JP
CN=Sample, OU=Sample, O=Sample, L=Sample, ST=Sample, C=Bist du sicher, dass du JP willst?
 [Nein]:  Y

Setzen Sie den Schlüsselspeicher, in dem das erstellte selbstsignierte Zertifikat gespeichert ist, auf Undertow und machen Sie den Server zu HTTPS.

src/main/resources/application.properties


server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=password

Wenn Sie die folgende Bean-Definition hinzufügen, ist die HTTP / 2-Unterstützung von Undertow abgeschlossen.

// ...
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));
		};
	}
}

Starten Sie Spring Boot und überprüfen Sie, ob Undertow HTTP / 2 ist.

$ ./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: Der Mac cURL-Befehl war nicht HTTP / 2-kompatibel, daher habe ich den folgenden Befehl ausgeführt, um die HTTP / 2-kompatible cURL zu installieren.

$ brew reinstall curl -- --with-nghttp2
$ brew link curl --force
$ hash -r

Nachdem wir bestätigt haben, dass es in HTTP / 2 konvertiert wurde, verwenden wir Chrome, um auf den Pfad zuzugreifen, der tatsächlich HTTP / 2 Server Push ausführt.

spring50-push-builder.png

Es ist ein wenig schwierig, aber Sie können sehen, dass der Initiator von "/style.css" "Push / Push-Builder" ist.

Warning:

Allerdings ... Es scheint, dass Server Push nach dem zweiten Mal nicht aktiviert ist ... + Das folgende Protokoll wird im Serverprotokoll angezeigt ... Es stimmt also etwas nicht Kann sein: weinen:

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

Die Funktion zum Hochladen von Servlet 3.0-Dateien löst jetzt "MaxUploadSizeExceededException" aus: thumbsup:

[SPR-9294]: MaxUploadSizeExceededException ( MultipartException), wenn in der von Servlet 3.0 unterstützten Datei-Upload-Funktion ein Übergrößenfehler auftritt. Die `Unterausnahme) wird jetzt ausgelöst. Da jedoch festgestellt wird, ob die Fehlermeldung bestimmte Wörter enthält ("Größe" und "aufgeregt"), wird abhängig von der Implementierung des Anwendungsservers wie zuvor "MultipartException" ausgelöst. Ich denke, Sie sollten sich bewusst sein, dass es eine Möglichkeit gibt.

Note:

Das Spring Framework bietet eine Komponente, die mit dem Hochladen von Commons-Dateien funktioniert. Wenn beim Hochladen von Commons-Dateien eine Größe überschritten wird, wurde eine "MaxUploadSizeExceededException" ausgelöst.

Lassen Sie uns nun tatsächlich einen Übergrößenfehler generieren. Das Erstellen eines Bildschirms zum Hochladen von Dateien ist mühsam. In diesem Eintrag wird die Datei mit dem Befehl cURL hochgeladen.

Erstellen Sie zunächst einen Controller, der die hochgeladene Datei empfängt, und starten Sie 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 !";
	}
}

Versuchen Sie als Nächstes, eine Datei innerhalb von 1 MB (eine Datei, die kleiner als die maximale Größe ist) hochzuladen, wie unten gezeigt.

$ 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 !

Laden Sie jetzt eine Datei mit 1 MB oder mehr hoch. Übrigens ... 1 MB oder mehr Dateien (target / spring5-web-demo-0.0.1-SNAPSHOT.jar) können mit ./mvnw package erstellt werden.

$ ./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"}

Ich habe einen Übergrößenfehler erhalten, weiß aber nicht, ob die "MaxUploadSizeExceededException" aufgetreten ist. Schauen wir uns also das Serverprotokoll an.

Serverprotokoll(Konsole)


...
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]
...

Wenn Sie sich die Protokollnachricht ansehen, scheint es, dass "MaxUploadSizeExceededException" aufgetreten ist. Fügen Sie also einen Ausnahmebehandlungsprozess für "MaxUploadSizeExceededException" hinzu.

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) //★★★ Hinzufügung der Ausnahmebehandlung
	@ResponseStatus(HttpStatus.PAYLOAD_TOO_LARGE)
	String handleMaxUploadSizeExceededException() {
		return "Upload file size is too large.";
	}
}

Darüber hinaus ... wird eine mehrteilige Anforderungsanalyse durchgeführt (verzögerte Analyse), nachdem die Handler-Methode bestimmt wurde. Dadurch kann die Methode @ @ ExceptionHandler Ausnahmen für das Hochladen von Dateien verarbeiten.

src/main/resources/application.properties(Aktivieren Sie die Verzögerungsanalyse für mehrteilige Anforderungen)


spring.servlet.multipart.resolve-lazily=true

Starten Sie Spring Boot neu und versuchen Sie erneut, eine Datei mit mehr als 1 MB hochzuladen.

$ 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:

Übrigens ... Bei Verwendung von Spring Boot ist die Datei-Upload-Funktion von Servlet 3.0 standardmäßig aktiviert, die maximale Größe jeder Datei beträgt 1 MB und die maximale Größe der gesamten Anforderung ist auf 10 MB begrenzt. Sie können die maximale Größe in application.properties ändern.

#Maximale Größe pro Datei
spring.servlet.multipart.max-file-size=2MB
#Maximale Größe der Anfrage
spring.servlet.multipart.max-request-size=20MB

Ein einheitlicher Mechanismus zur Auflösung von Medientypen wird unterstützt: thumbsup:

[SPR-14484] [SPR-14908]: Vereinigung Der Mechanismus zur Auflösung von Medientypen (MediaTypeFactory-Klasse) wird hinzugefügt, und JAF-abhängiger Code (Java Beans Activation Framework) wird entfernt. Lösen Sie in der neuen Klasse Medientypen gemäß der Definition von "/ org / springframework / http / mime.types" (Medientyp- und Erweiterungszuordnungsdatei) auf, die in "spring-web" gespeichert ist. ..

Wenn Sie beispielsweise die folgende Handler-Methode erstellen und die Datei "pom.xml" hochladen ...

@PostMapping("/upload")
String upload(MultipartFile file) {
	MediaTypeFactory.getMediaTypes(file.getOriginalFilename())
		.forEach(System.out::println);
	return file.getOriginalFilename() + " is uploaded !";
}

Die folgenden Medientypen werden an die Konsole ausgegeben.

application/xml

Darüber hinaus können Sie die von spring-web bereitgestellte Mapping-Definition anpassen, indem Sie in Ihrem Projekt / org / springframework / http / mime.types erstellen (kopieren).

src/main/resources/org/springframework/http/mime.types


...(Kürzung)
text/xml    xml

Wenn Sie die Datei "pom.xml" erneut hochladen ... Die folgenden Medientypen werden an die Konsole ausgegeben.

application/xml
text/xml

Daten können an unveränderliche Objekte gebunden werden: thumbsup:

[SPR-15199]: Ermöglicht die Datenbindung von Anforderungsparametern an unveränderliche Objekte. Wenn im Konstruktorargument Informationen zum Parameternamen vorhanden sind (wenn der Kompilierungsoption -d oder -parameters hinzugefügt wird), wird der Anforderungsparameterwert, der mit dem Parameternamen des Konstruktorarguments und dem Anforderungsparameternamen übereinstimmt, gebunden. Getan werden.

Note:

Wenn mehrere Konstruktoren vorhanden sind, wird der Standardkonstruktor wie zuvor aufgerufen. Wenn Sie die Parameternameninformationen nicht im Konstruktorargument belassen möchten, können Sie das von Java Beans bereitgestellte "@ java.beans.ConstructorProperties" verwenden, um das Konstruktorargument dem Anforderungsparameter zuzuordnen.

Erstellen wir nun eine unveränderliche Klasse und geben diese Klasse als Argument der Handler-Methode an.

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;
		}

	}

}

Wenn im Standardzustand "LocalDate" auf JSON gesetzt ist, ist die Lesbarkeit sehr schlecht. Geben Sie also einen so formatierten Wert aus.

pom.xml


<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-jsr310</artifactId> <!-- JSR-Modul zur Unterstützung der Serialisierung und Deserialisierung von 310 Klassen hinzugefügt-->
</dependency>

src/main/resources/application.properties


spring.jackson.serialization.write-dates-as-timestamps=false

Versuchen Sie zunächst, eine Anfrage mit allen normalen Werten zu senden.

$ 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"}

Du bist richtig gebunden. Als nächstes überprüfen wir, ob die Validierung durchgeführt wird, ohne name anzugeben.

$ 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"}

Dies scheint auch richtig zu funktionieren. Geben Sie abschließend ein ungültiges Datum für "baseDate" ("LocalDate") an und prüfen Sie, ob ein Bindungsfehler auftritt.

$ 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"}

Es scheint, dass dies auch richtig funktioniert.

Warning:

Ich habe den Stil geändert, um die Fehlerinformationen zum Zeitpunkt des Bindungsfehlers und den Validierungsfehler als "BindingResult" wie folgt zu erhalten ... Ich konnte den Validierungsfehler empfangen, aber zum Zeitpunkt des Bindungsfehlers ist eine Ausnahme aufgetreten Ich konnte es nicht empfangen. (Ist das eine Spezifikation!? ...)

@GetMapping("/immutable")
Query search(@Validated Query query, BindingResult bindingResult) {
	// ...
	return query;
}

Ich war mir nicht sicher, ob es sich um eine Spezifikation oder einen Fehler handelte, daher gab ich Spring JIRA ein Problem (SPR-15542).

Jackson 2.9 unterstützt: thumbsup:

[SPR-14925]: Anscheinend Jackson 2.9 ) Scheint die erforderliche Version von Spring Framework 5.0 zu sein. Jackson 2.9 wurde zu diesem Zeitpunkt (14. Mai 2017) noch nicht offiziell veröffentlicht, aber es scheint, dass Spring Framework 5.0 zum Zeitpunkt der offiziellen Veröffentlichung veröffentlicht wird. Betrachten der Änderungen im Spring Framework ... Wenn in Jackson 2.9 ein Fehler aufgrund eines Problems mit der serverseitigen Implementierung (POJO-Implementierung) auftritt, wird "InvalidDefinitionException" ausgelöst, sodass diese Ausnahme aufgetreten ist. Es scheint, dass ich es manchmal behoben habe, so dass es sich um einen Serverfehler (500 Internal Server Error) anstelle eines Clientfehlers (400 Bad Request) handelte.

JSR-367 JSON-Bindung unterstützt: thumbsup:

[SPR-14923]: Java EE 8-Komponente JSON-Bindung Es wird unterstützt und kann als Alternative zu Jackson und GSON verwendet werden.

Erstens hängt Spring Boot (Spring-Boot-Starter-Web) von Jackson ab. Wenn Sie also JSON-B verwenden möchten, müssen Sie Jackson aus den abhängigen Bibliotheken entfernen und die Bibliothek für JSON-B hinzufügen. Es gibt.

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> <!--Jackson ausschließen-->
      </exclusion>
    </exclusions>
  </dependency>

  <!-- ... -->

  <!--Dieser Eintrag ist die offizielle Referenzimplementierung von Yasson(+JSON-P Implementierung)Hinzufügen-->
  <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>
  <!-- ... -->
  <!--Yasson Release Repository hinzugefügt(Um die Meilensteinversion erhalten zu können) -->
  <repository>
    <id>yasson-releases</id>
    <name>Yasson Release repository</name>
    <url>https://repo.eclipse.org/content/repositories/yasson-releases/</url>
  </repository>
</repositories>

Erstellen Sie als Nächstes die Controller-Klasse. Es gibt keine besonderen Vorsichtsmaßnahmen zum Erstellen der Controller-Klasse, aber es scheint, dass die Bindung an unveränderliche Klassen nicht unterstützt wird.

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;
		}

	}

}

Wenn Sie auf den von Ihnen erstellten Endpunkt zugreifen, können Sie sehen, dass der angeforderte JSON in ein Java-Objekt konvertiert (deserialisiert) und dieses Objekt in JSON konvertiert (serialisiert) wurde.

$ 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 ist auch in RestTemplate verfügbar, einer Klasse für HTTP-Clients. Sie können JSON-B auch mit dem von Spring Boot bereitgestellten Test-HTTP-Client (TestRestTemplate) verwenden.

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) //★★★ Starten Sie den Server über einen freien Port, wenn Sie den Test ausführen
public class JsonbTests {

	@LocalServerPort private int port; //★★★ Der Boot-Port des Servers wird injiziert

	@Autowired TestRestTemplate testRestTemplate; //★★★ HTTP-Client wird injiziert, um auf den Startport des Servers zuzugreifen

	@Test
	public void testUsingRestTemplate() {
		LocalDate now = LocalDate.now();

		RestOperations restOperations = new RestTemplate(); //★★★ JSON im Standardzustand-Die mit B verknüpfte Komponente wird registriert
		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); //★★★ Wenn Sie den HTTP-Client zum Testen verwenden, können Sie die URL aus dem Pfad angeben.

		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;
		}

	}

}

Google Protobuf 3.x unterstützt: thumbsup:

[SPR-13589]: ProtobufHttpMessageConverter unterstützt jetzt Google Protobuf 3.x. Anscheinend sind die unterstützten Standardformate (com.google.protobuf: protobuf-java) nur" application / x-protobuf" und " text / plain "und andere Formate (" application /") Für json "" application / xml" " text / html ") separate Bibliotheken (" com.google.protobuf: protobuf-java-util (offiziell)" und "` com.googlecode.protobuf- ") Java-Format: protobuf-Java-Format "(Drittanbieter)") scheint hinzugefügt werden zu müssen.

Die Klassen Reactor 3.1 und RxJava 1.3 / 2.1 können mit der Handler-Methode zurückgegeben werden: thumbsup:

[SPR-15365]: In Verbindung mit der Unterstützung des reaktiven Programmiermodells (Spring WebFlux wird später beschrieben) verfügt Spring MVC auch über die Reactor 3.1-Klasse (Flux). Die Klassen , Mono), RxJava 1.3 und 2.1 (Observable, Sigle, Flowable usw.) können jetzt als Rückgabewert der Handler-Methode behandelt werden. Wenn diese Klassen (im Folgenden als "reaktive Klasse" bezeichnet) zurückgegeben werden, ist Spring MVC ein Mechanismus zur Verarbeitung unter Verwendung der asynchronen Verarbeitung von Spring MVC (Verknüpfungsfunktion mit asynchroner Verarbeitung, die von Servlet 3.0 unterstützt wird). Es ist geworden.

Zuvor veröffentlicht "[Grundlegendes zu asynchronen Anforderungen in 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 Die asynchrone Verarbeitung von wird in die folgenden zwei Methoden unterteilt.

Welche Methode verwendet wird, hängt von der Kombination aus reaktivem Klassentyp und zurückzugebendem Medientyp ab. Insbesondere "[Spring Framework-Referenz -Async-Anforderungen mit reaktiven Typen-](http://docs.spring.io/spring/docs/5.0.0.RC1/spring-framework-reference/web.html#mvc -ann-async-reaktive-Typen) ".

Lassen Sie uns nun die Reactive-Klasse zurückgeben. Da dieser Eintrag die von Spring WebFlux verwendete Reactor-Klasse verwendet, fügen Sie zuerst Reactor zur abhängigen Bibliothek hinzu.

<dependency>
  <groupId>io.projectreactor</groupId>
  <artifactId>reactor-core</artifactId>
  <version>3.1.0.M1</version>
</dependency>

Implementieren Sie als Nächstes die Handler-Methode, die "Reao" und "Flux" des Reaktors zurückgibt.

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() {
		//Wenn Sie abonniert sind, geben Sie die Ressource mit einer auf eine Zufallszahl festgelegten ID zurück
		//Rufen Sie subscribeOn auf, damit sich die Abonnentenverarbeitung in einem separaten Thread befindet
		//    ->Es ist nicht erforderlich, diesen Beispielcode in einem separaten Thread zu verarbeiten.
		//    ->Tatsächlich wird angenommen, dass "schwere Verarbeitung = CPU-gebundene Verarbeitung" durchgeführt wird.
		return Mono.fromSupplier(() -> new Resource(new Random().nextInt()))
				.subscribeOn(Schedulers.parallel());
	}

	@GetMapping("/reactor/flux")
	Flux<Resource> getFlux() {
		//Wenn Sie abonniert sind, setzen Sie Resource mit der ID 0 bis 0.Kehre alle 5 Sekunden zurück
		//Wenn das Intervall verwendet wird, erfolgt die Abonnentenverarbeitung in einem separaten Thread.
		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;
		}
	}

}

Lassen Sie uns abschließend auf die von uns erstellte Handler-Methode zugreifen.

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}

Übrigens ... Wenn Sie Flux mit application / json zurückgeben ...

$ 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}]

Und der JSON wird beantwortet, nachdem alle Daten an den Abonnenten übergeben wurden (ca. 2,5 Sekunden später).

ParsingPathMatcher wurde hinzugefügt: thumbsup:

[SPR-14544]: "ParsingPathMatcher" wird als Alternative zu "AntPathMatcher" hinzugefügt. Spring MVC verwendet standardmäßig "AntPathMatcher", aber Spring WebFlux-bezogene Komponenten scheinen standardmäßig "ParsingPathMatcher" zu verwenden. Ich habe es nicht richtig gesehen ...: heat_smile: Es scheint, dass das, was mit "AntPathMatcher" ausgedrückt werden könnte, auch mit "ParsingPathMatcher" ausgedrückt werden kann, und "ParsingPathMatcher" heißt neu "{{Variablenname}" Ausdrücke werden unterstützt.

Nehmen Sie das folgende Beispiel als Beispiel ... Die Pfadanpassungsspezifikation ist dieselbe wie "/ users / **", aber wenn Sie "{* Variablenname}" verwenden, lautet sie " / Der Teil "" kann als Variable behandelt werden. Beachten Sie, dass Sie nach " { Variablenname} " keine Zeichen wie " / users / { userPaths} .json` "angeben können.

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", "");
		}
	}

}

Wenn Sie "ParsingPathMatcher" im Anforderungsabgleichprozess von Spring MVC verwenden möchten, können Sie dies tun, indem Sie die folgende Bean definieren.

@Bean
public WebMvcConfigurer webMvcConfigurer() {
	return new WebMvcConfigurer() {
		@Override
		public void configurePathMatch(PathMatchConfigurer configurer) {
			//★★★ ParsingPathMatcher anwenden
			configurer.setPathMatcher(new ParsingPathMatcher());
			//★★★ Deaktivieren Sie useSuffixPatternMatch
			//★★★ Wenn useSuffixPatternMatch aktiviert ist, "" am Ende des Musters während der Verarbeitung des Anforderungsabgleichs`.*`Wird angegeben und während der Musteranalyse tritt ein Fehler auf
			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}") // ★★★ {*Variablennamen}Um einen Variablenwert als Methodenargument zu erhalten, verwenden Sie
	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:

Übrigens ... Als ich in Spring WebFlux "{{Variablenname}" verwendet habe, wobei "ParsingPathMatcher" standardmäßig angewendet wird, ist beim Anforderungszuordnungsprozess ein Fehler aufgetreten ...: enttäuscht_relieved: Wenn Sie "{* Variablenname}" verwenden, ist dies ein Fehler, der auf " useSuffixPatternMatch = false "gesetzt werden muss? Ich habe Lust dazu, also habe ich Spring JIRA (SPR-15558) erstellt.

RedirectAttributes kann von der @ ExceptionHandler -Methode empfangen werden: thumbsup:

[SPR-14651]: Sie können jetzt "RedirectAttributes" als Argument für die "@ ExceptionHandler" -Methode empfangen und über "RedirectAttributes" umleiten. Sie können Daten mit verknüpfen.

Note:

Mit Blick auf das Problem wird es auch auf Spring Framework 4.3 zurückportiert ... Ich frage mich, ob dies ein Problem ist ...

Normalerweise ist es besser, einen Bildschirm zu erstellen und zu überprüfen, aber ... Es ist mühsam, einen Bildschirm zu erstellen. Überprüfen Sie ihn daher mit 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) { //Der in RedirectAttributes festgelegte Wert kann als Methodenargument abgerufen werden
		return id + " : " + message;
	}

	@GetMapping("/redirect")
	String get() {
		throw new MyException("error.", 100);
	}

	@ExceptionHandler
	String handleMyException(MyException e, RedirectAttributes redirectAttributes) {
		redirectAttributes.addFlashAttribute("message", e.getMessage()); //★★★ Legen Sie eine Nachricht im Bereich Spülen fest
		redirectAttributes.addAttribute("id", e.getId()); //★★★ Pfadvariable des Umleitungspfads "{id}Legen Sie den Wert fest, in den eingebettet werden soll
		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 wurde hinzugefügt: thumbsup:

[SPR-14895]: Die für Spring WebFlux erstellte ResponseStatusException ist jetzt in Spring MVC mit Statuscode und Begründung verfügbar Durch Angeben und Generieren / Auslösen einer Ausnahme wird es möglich, den zu antwortenden HTTP-Status zu bestimmen.

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."); //★★★ Lösen Sie eine Ausnahme aus, indem Sie einen beliebigen Statuscode und eine beliebige Begründung angeben
	}

}
$ 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"}

Die für "Internationalisierung" und "Vorlagenfragmentierung" erforderlichen Objekte sind jetzt in "ScriptTemplateView" verknüpft: thumbsup:

[SPR-15064]: "Internationalisierung von Nachrichten" in "ScriptTemplateView" (Implementierung mit der Skript-Engine von JSR-223 anzeigen) " Die Objekte ("Gebietsschema", "MessageSource" usw.), die zur Realisierung der "Fragmentierung der Vorlage" erforderlich sind, werden an die Seite der Vorlagen-Engine übergeben. Die Implementierung selbst für "Internationalisierung" und "Fragmentierung von Vorlagen" wird von Spring nicht bereitgestellt.

In diesem Eintrag wird "nashorn" von Java SE als Skriptmodul und "mustache.js" als Vorlagenmodul verwendet. Und "Fragmentierung der Vorlage" wird realisiert.

Konfigurieren Sie zunächst die Verwendung von "ScriptTemplateView".

pom.xml(Installieren Sie Moustachejs)


<dependency>
  <groupId>org.webjars.bower</groupId>
  <artifactId>mustache</artifactId>
  <version>2.3.0</version> <!--Neueste Version zum Zeitpunkt des Schreibens-->
</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;
	}
}

Erstellen Sie eine JavaScript-Datei, um den Standard-Rendering-Prozess für Moustache anzupassen. Ich habe den Begriff Anpassung verwendet, aber in Wirklichkeit habe ich dem Modell nur eine benutzerdefinierte Funktion hinzugefügt und die Renderfunktion von Moustache aufgerufen.

/main/resources/META-INF/js/render.js


function render(template, model, renderingContext) {
    model["message"] = function () { //★★★ Benutzerdefinierte Funktion für "Internationalisierung von Nachrichten" hinzugefügt
        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 () { //★★★ Benutzerdefinierte Funktion für "Vorlagenfragmentierung" hinzugefügt
        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" im dritten Argument der Rendering-Methode Eine Instanz von /view/script/RenderingContext.java) wird übergeben. Holen Sie sich also die erforderlichen Objekte und verarbeiten Sie sie.

Jetzt erstellen wir eine Vorlagendatei und einen Controller und führen sie tatsächlich aus.

src/main/resources/templates/script-template/home.html


<html>
<head>
    <title>{{title}}</title>
</head>
<body>

<p>{{body}}</p>

<p>{{#message}}welcome{{/message}}</p> <!--★★★ Geben Sie eine Nachricht mit einer benutzerdefinierten Funktion aus-->

{{#include}}script-template/footer{{/include}} <!--★★★ Fragmente mit benutzerdefinierten Funktionen einschließen-->

</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";
	}

}

Sie müssen auch eine Nachrichtendefinitionsdatei erstellen.

src/main/resources/messages.properties


welcome=Hallo!

src/main/resources/messages_en.properties


welcome=Hello!

Wenn Sie Spring Boot starten und auf http: // localhost: 8080 / script-template zugreifen ...

$ 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>Hallo!</p>

<footer>
    Copyright (c) 2017 kazuki43zoo.com
</footer>

</body>
</html>

Wenn Sie Accept-Language in en (Englisch) ändern und erneut zugreifen ...

$ 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>

Sie können bestätigen, dass "Internationalisierung der Nachricht" und "Fragment einschließen" durchgeführt wurden.

Zusammenfassung

Dieses Mal haben wir die wichtigsten Änderungen in Bezug auf WebMVC vorgestellt. Das Webframework für reaktive Programmiermodelle (Spring WebFlux) wurde aus Spring Framework 5.0 hinzugefügt und zieht die Aufmerksamkeit auf sich, aber ... Spring MVC ist noch aktiv (vorerst ... Ich verwende Spring MVC für die Arbeit. Ich denke, es wird überwiegend mehr geben: heat_smile :). Nächstes Mal werde ich "Wichtige Änderungen im Zusammenhang mit Test" vorstellen.

Recommended Posts

Wichtige Änderungen in Bezug auf Spring Framework 5.0 Web MVC
Wichtige Änderungen im Zusammenhang mit Spring Framework 5.0 Test
Wichtige Änderungen im Zusammenhang mit dem Spring Framework 5.0 DI-Container
Spring Framework 5.0 Zusammenfassung der wichtigsten Änderungen
Wichtige Änderungen in der Kernfunktionalität von Spring Framework 5.0
Wichtige Änderungen in Spring Boot 1.5
So erhalten Sie eine leere Anfrage mit Spring Web MVC @RequestBody
Änderungen bei der Migration von Spring Boot 1.5 auf Spring Boot 2.0
Übergang von Struts2 zu Spring MVC (Controller)
Änderungen bei der Migration von Spring Boot 2.0 zu Spring Boot 2.2
[Spring MVC] Übergeben von Pfadvariablen
SameSite-Cookie im Spring Boot (Spring Web MVC + Tomcat)
Ich habe versucht, JavaFX und Spring Framework zu verknüpfen.
Der offizielle Name von Spring MVC ist Spring Web MVC
Memorandum (Spring Web)
Ich habe versucht, das Hochladen von Dateien mit Spring MVC zu implementieren
Verwendung von Struts2 * Spring Framework (Spring Plugin) Version Juni 2017