Update reference with examples of multipart requests
Issue: SPR-16118
This commit is contained in:
parent
4ec60f08ad
commit
14f02d7192
|
|
@ -615,6 +615,17 @@ public interface WebTestClient {
|
||||||
/**
|
/**
|
||||||
* Set the body of the request to the given synchronous {@code Object} and
|
* Set the body of the request to the given synchronous {@code Object} and
|
||||||
* perform the request.
|
* perform the request.
|
||||||
|
* <p>This method is a convenient shortcut for:
|
||||||
|
* <pre class="code">
|
||||||
|
* .body(BodyInserters.fromObject(object))
|
||||||
|
* </pre>
|
||||||
|
* <p>The body can be a
|
||||||
|
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create
|
||||||
|
* a multipart request. The values in the {@code MultiValueMap} can be
|
||||||
|
* any Object representing the body of the part, or an
|
||||||
|
* {@link org.springframework.http.HttpEntity HttpEntity} representing a
|
||||||
|
* part with body and headers. The {@code MultiValueMap} can be built
|
||||||
|
* conveniently using
|
||||||
* @param body the {@code Object} to write to the request
|
* @param body the {@code Object} to write to the request
|
||||||
* @return a {@code Mono} with the response
|
* @return a {@code Mono} with the response
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -149,9 +149,13 @@ public interface RestOperations {
|
||||||
* the {@code Location} header. This header typically indicates where the new resource is stored.
|
* the {@code Location} header. This header typically indicates where the new resource is stored.
|
||||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||||
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
||||||
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
|
* add additional HTTP headers to the request.
|
||||||
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
|
* <p>The body of the entity, or {@code request} itself, can be a
|
||||||
* simulate a multipart from submission.
|
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
|
||||||
|
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
|
||||||
|
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
|
||||||
|
* and headers. The {@code MultiValueMap} can be built conveniently using
|
||||||
|
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
|
||||||
* @param url the URL
|
* @param url the URL
|
||||||
* @param request the Object to be POSTed (may be {@code null})
|
* @param request the Object to be POSTed (may be {@code null})
|
||||||
* @param uriVariables the variables to expand the template
|
* @param uriVariables the variables to expand the template
|
||||||
|
|
@ -166,9 +170,13 @@ public interface RestOperations {
|
||||||
* the {@code Location} header. This header typically indicates where the new resource is stored.
|
* the {@code Location} header. This header typically indicates where the new resource is stored.
|
||||||
* <p>URI Template variables are expanded using the given map.
|
* <p>URI Template variables are expanded using the given map.
|
||||||
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
||||||
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
|
* add additional HTTP headers to the request
|
||||||
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
|
* <p>The body of the entity, or {@code request} itself, can be a
|
||||||
* simulate a multipart from submission.
|
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
|
||||||
|
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
|
||||||
|
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
|
||||||
|
* and headers. The {@code MultiValueMap} can be built conveniently using
|
||||||
|
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
|
||||||
* @param url the URL
|
* @param url the URL
|
||||||
* @param request the Object to be POSTed (may be {@code null})
|
* @param request the Object to be POSTed (may be {@code null})
|
||||||
* @param uriVariables the variables to expand the template
|
* @param uriVariables the variables to expand the template
|
||||||
|
|
@ -183,9 +191,13 @@ public interface RestOperations {
|
||||||
* Create a new resource by POSTing the given object to the URL, and returns the value of the
|
* Create a new resource by POSTing the given object to the URL, and returns the value of the
|
||||||
* {@code Location} header. This header typically indicates where the new resource is stored.
|
* {@code Location} header. This header typically indicates where the new resource is stored.
|
||||||
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
||||||
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
|
* add additional HTTP headers to the request.
|
||||||
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
|
* <p>The body of the entity, or {@code request} itself, can be a
|
||||||
* simulate a multipart from submission.
|
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
|
||||||
|
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
|
||||||
|
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
|
||||||
|
* and headers. The {@code MultiValueMap} can be built conveniently using
|
||||||
|
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
|
||||||
* @param url the URL
|
* @param url the URL
|
||||||
* @param request the Object to be POSTed (may be {@code null})
|
* @param request the Object to be POSTed (may be {@code null})
|
||||||
* @return the value for the {@code Location} header
|
* @return the value for the {@code Location} header
|
||||||
|
|
@ -199,9 +211,13 @@ public interface RestOperations {
|
||||||
* and returns the representation found in the response.
|
* and returns the representation found in the response.
|
||||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||||
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
||||||
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
|
* add additional HTTP headers to the request.
|
||||||
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
|
* <p>The body of the entity, or {@code request} itself, can be a
|
||||||
* simulate a multipart from submission.
|
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
|
||||||
|
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
|
||||||
|
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
|
||||||
|
* and headers. The {@code MultiValueMap} can be built conveniently using
|
||||||
|
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
|
||||||
* @param url the URL
|
* @param url the URL
|
||||||
* @param request the Object to be POSTed (may be {@code null})
|
* @param request the Object to be POSTed (may be {@code null})
|
||||||
* @param responseType the type of the return value
|
* @param responseType the type of the return value
|
||||||
|
|
@ -218,9 +234,13 @@ public interface RestOperations {
|
||||||
* and returns the representation found in the response.
|
* and returns the representation found in the response.
|
||||||
* <p>URI Template variables are expanded using the given map.
|
* <p>URI Template variables are expanded using the given map.
|
||||||
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
||||||
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
|
* add additional HTTP headers to the request.
|
||||||
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
|
* <p>The body of the entity, or {@code request} itself, can be a
|
||||||
* simulate a multipart from submission.
|
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
|
||||||
|
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
|
||||||
|
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
|
||||||
|
* and headers. The {@code MultiValueMap} can be built conveniently using
|
||||||
|
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
|
||||||
* @param url the URL
|
* @param url the URL
|
||||||
* @param request the Object to be POSTed (may be {@code null})
|
* @param request the Object to be POSTed (may be {@code null})
|
||||||
* @param responseType the type of the return value
|
* @param responseType the type of the return value
|
||||||
|
|
@ -236,9 +256,13 @@ public interface RestOperations {
|
||||||
* Create a new resource by POSTing the given object to the URL,
|
* Create a new resource by POSTing the given object to the URL,
|
||||||
* and returns the representation found in the response.
|
* and returns the representation found in the response.
|
||||||
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
||||||
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
|
* add additional HTTP headers to the request.
|
||||||
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
|
* <p>The body of the entity, or {@code request} itself, can be a
|
||||||
* simulate a multipart from submission.
|
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
|
||||||
|
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
|
||||||
|
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
|
||||||
|
* and headers. The {@code MultiValueMap} can be built conveniently using
|
||||||
|
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
|
||||||
* @param url the URL
|
* @param url the URL
|
||||||
* @param request the Object to be POSTed (may be {@code null})
|
* @param request the Object to be POSTed (may be {@code null})
|
||||||
* @param responseType the type of the return value
|
* @param responseType the type of the return value
|
||||||
|
|
@ -253,9 +277,13 @@ public interface RestOperations {
|
||||||
* and returns the response as {@link ResponseEntity}.
|
* and returns the response as {@link ResponseEntity}.
|
||||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||||
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
||||||
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
|
* add additional HTTP headers to the request.
|
||||||
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
|
* <p>The body of the entity, or {@code request} itself, can be a
|
||||||
* simulate a multipart from submission.
|
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
|
||||||
|
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
|
||||||
|
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
|
||||||
|
* and headers. The {@code MultiValueMap} can be built conveniently using
|
||||||
|
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
|
||||||
* @param url the URL
|
* @param url the URL
|
||||||
* @param request the Object to be POSTed (may be {@code null})
|
* @param request the Object to be POSTed (may be {@code null})
|
||||||
* @param uriVariables the variables to expand the template
|
* @param uriVariables the variables to expand the template
|
||||||
|
|
@ -271,9 +299,13 @@ public interface RestOperations {
|
||||||
* and returns the response as {@link HttpEntity}.
|
* and returns the response as {@link HttpEntity}.
|
||||||
* <p>URI Template variables are expanded using the given map.
|
* <p>URI Template variables are expanded using the given map.
|
||||||
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
||||||
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
|
* add additional HTTP headers to the request.
|
||||||
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
|
* <p>The body of the entity, or {@code request} itself, can be a
|
||||||
* simulate a multipart from submission.
|
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
|
||||||
|
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
|
||||||
|
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
|
||||||
|
* and headers. The {@code MultiValueMap} can be built conveniently using
|
||||||
|
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
|
||||||
* @param url the URL
|
* @param url the URL
|
||||||
* @param request the Object to be POSTed (may be {@code null})
|
* @param request the Object to be POSTed (may be {@code null})
|
||||||
* @param uriVariables the variables to expand the template
|
* @param uriVariables the variables to expand the template
|
||||||
|
|
@ -288,9 +320,13 @@ public interface RestOperations {
|
||||||
* Create a new resource by POSTing the given object to the URL,
|
* Create a new resource by POSTing the given object to the URL,
|
||||||
* and returns the response as {@link ResponseEntity}.
|
* and returns the response as {@link ResponseEntity}.
|
||||||
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
|
||||||
* add additional HTTP headers to the request. The body of the entity, or {@code request} itself,
|
* add additional HTTP headers to the request.
|
||||||
* can be a {@link org.springframework.http.client.MultipartBodyBuilder MultiValueMap} to
|
* <p>The body of the entity, or {@code request} itself, can be a
|
||||||
* simulate a multipart from submission.
|
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
|
||||||
|
* The values in the {@code MultiValueMap} can be any Object representing the body of the part,
|
||||||
|
* or an {@link org.springframework.http.HttpEntity HttpEntity} representing a part with body
|
||||||
|
* and headers. The {@code MultiValueMap} can be built conveniently using
|
||||||
|
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
|
||||||
* @param url the URL
|
* @param url the URL
|
||||||
* @param request the Object to be POSTed (may be {@code null})
|
* @param request the Object to be POSTed (may be {@code null})
|
||||||
* @return the converted object
|
* @return the converted object
|
||||||
|
|
|
||||||
|
|
@ -220,10 +220,13 @@ public abstract class BodyInserters {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a {@code FormInserter} that writes the given {@code MultiValueMap}
|
* Return a {@code FormInserter} that writes the given {@code MultiValueMap}
|
||||||
* as multipart data. The {@code multipartData} parameter can conveniently be built using the
|
* as multipart data. The values in the {@code MultiValueMap} can be any
|
||||||
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
|
* Object representing the body of the part, or an
|
||||||
* Note that the returned inserter allows for additional entries to be added
|
* {@link org.springframework.http.HttpEntity HttpEntity} representing a part
|
||||||
* via {@link FormInserter#with(String, Object)}.
|
* with body and headers. The {@code MultiValueMap} can be built conveniently
|
||||||
|
* using {@link org.springframework.http.client.MultipartBodyBuilder
|
||||||
|
* MultipartBodyBuilder}. Also the returned inserter allows for additional
|
||||||
|
* entries to be added via {@link FormInserter#with(String, Object)}.
|
||||||
*
|
*
|
||||||
* <p><strong>Note:</strong> you can also use the {@code syncBody(Object)}
|
* <p><strong>Note:</strong> you can also use the {@code syncBody(Object)}
|
||||||
* method in the request builders of both the {@code WebClient} and
|
* method in the request builders of both the {@code WebClient} and
|
||||||
|
|
@ -245,15 +248,13 @@ public abstract class BodyInserters {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a {@code FormInserter} that writes the key-value pair as multipart data. The
|
* A variant of {@link #fromMultipartData(MultiValueMap)} for adding
|
||||||
* {@code multipartData} parameter can conveniently be built using the
|
* parts as name-value pairs in-line vs building a {@code MultiValueMap}
|
||||||
* {@link org.springframework.http.client.MultipartBodyBuilder MultipartBodyBuilder}.
|
* and passing it in.
|
||||||
* Note that the returned inserter allows for additional entries to be added
|
* @param key the part name
|
||||||
* via {@link FormInserter#with(String, Object)}.
|
* @param value the part value, an Object or {@code HttpEntity}
|
||||||
* {@link FormInserter#with(String, Object)}.
|
* @return a {@code FormInserter} that can writes the provided multipart
|
||||||
* @param key the key to add to the form
|
* data and also allows adding more parts
|
||||||
* @param value the value to add to the form
|
|
||||||
* @return a {@code FormInserter} that writes multipart data
|
|
||||||
*/
|
*/
|
||||||
// Note that the returned BodyInserter is parameterized to ClientHttpRequest, not
|
// Note that the returned BodyInserter is parameterized to ClientHttpRequest, not
|
||||||
// ReactiveHttpOutputMessage like other methods, since sending form data only typically happens
|
// ReactiveHttpOutputMessage like other methods, since sending form data only typically happens
|
||||||
|
|
|
||||||
|
|
@ -544,9 +544,17 @@ public interface WebClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the body of the request to the given synchronous {@code Object}.
|
* Set the body of the request to the given synchronous {@code Object}.
|
||||||
* <p>This method is a convenient shortcut for {@link #body(BodyInserter)} with a
|
* <p>This method is a convenient shortcut for:
|
||||||
* {@linkplain org.springframework.web.reactive.function.BodyInserters#fromObject
|
* <pre class="code">
|
||||||
* Object body inserter}.
|
* .body(BodyInserters.fromObject(object))
|
||||||
|
* </pre>
|
||||||
|
* <p>The body can be a
|
||||||
|
* {@link org.springframework.util.MultiValueMap MultiValueMap} to create
|
||||||
|
* a multipart request. The values in the {@code MultiValueMap} can be
|
||||||
|
* any Object representing the body of the part, or an
|
||||||
|
* {@link org.springframework.http.HttpEntity HttpEntity} representing a
|
||||||
|
* part with body and headers. The {@code MultiValueMap} can be built
|
||||||
|
* conveniently using
|
||||||
* @param body the {@code Object} to write to the request
|
* @param body the {@code Object} to write to the request
|
||||||
* @return this builder
|
* @return this builder
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,12 @@ import reactor.core.publisher.Mono;
|
||||||
import reactor.test.StepVerifier;
|
import reactor.test.StepVerifier;
|
||||||
|
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.client.MultipartBodyBuilder;
|
||||||
import org.springframework.http.codec.multipart.FilePart;
|
import org.springframework.http.codec.multipart.FilePart;
|
||||||
import org.springframework.http.codec.multipart.FormFieldPart;
|
import org.springframework.http.codec.multipart.FormFieldPart;
|
||||||
import org.springframework.http.codec.multipart.Part;
|
import org.springframework.http.codec.multipart.Part;
|
||||||
|
|
@ -75,16 +77,11 @@ public class MultipartIntegrationTests extends AbstractRouterFunctionIntegration
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private MultiValueMap<String, Object> generateBody() {
|
private MultiValueMap<String, HttpEntity<?>> generateBody() {
|
||||||
HttpHeaders fooHeaders = new HttpHeaders();
|
MultipartBodyBuilder builder = new MultipartBodyBuilder();
|
||||||
fooHeaders.setContentType(MediaType.TEXT_PLAIN);
|
builder.part("fooPart", new ClassPathResource("org/springframework/http/codec/multipart/foo.txt"));
|
||||||
ClassPathResource fooResource = new ClassPathResource("org/springframework/http/codec/multipart/foo.txt");
|
builder.part("barPart", "bar");
|
||||||
HttpEntity<ClassPathResource> fooPart = new HttpEntity<>(fooResource, fooHeaders);
|
return builder.build();
|
||||||
HttpEntity<String> barPart = new HttpEntity<>("bar");
|
|
||||||
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
|
|
||||||
parts.add("fooPart", fooPart);
|
|
||||||
parts.add("barPart", barPart);
|
|
||||||
return parts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -32,16 +32,14 @@ import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.client.MultipartBodyBuilder;
|
||||||
import org.springframework.http.codec.multipart.FilePart;
|
import org.springframework.http.codec.multipart.FilePart;
|
||||||
import org.springframework.http.codec.multipart.FormFieldPart;
|
import org.springframework.http.codec.multipart.FormFieldPart;
|
||||||
import org.springframework.http.codec.multipart.MultipartHttpMessageReader;
|
import org.springframework.http.codec.multipart.MultipartHttpMessageReader;
|
||||||
import org.springframework.http.codec.multipart.Part;
|
import org.springframework.http.codec.multipart.Part;
|
||||||
import org.springframework.http.server.reactive.AbstractHttpHandlerIntegrationTests;
|
import org.springframework.http.server.reactive.AbstractHttpHandlerIntegrationTests;
|
||||||
import org.springframework.http.server.reactive.HttpHandler;
|
import org.springframework.http.server.reactive.HttpHandler;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
|
@ -136,27 +134,13 @@ public class MultipartIntegrationTests extends AbstractHttpHandlerIntegrationTes
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MultiValueMap<String, HttpEntity<?>> generateBody() {
|
||||||
private MultiValueMap<String, Object> generateBody() {
|
MultipartBodyBuilder builder = new MultipartBodyBuilder();
|
||||||
|
builder.part("fieldPart", "fieldValue");
|
||||||
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
|
builder.part("fileParts", new ClassPathResource("foo.txt", MultipartHttpMessageReader.class));
|
||||||
parts.add("fieldPart", "fieldValue");
|
builder.part("fileParts", new ClassPathResource("logo.png", getClass()));
|
||||||
|
builder.part("jsonPart", new Person("Jason"));
|
||||||
HttpHeaders headers = new HttpHeaders();
|
return builder.build();
|
||||||
headers.setContentType(MediaType.TEXT_PLAIN);
|
|
||||||
ClassPathResource resource = new ClassPathResource("foo.txt", MultipartHttpMessageReader.class);
|
|
||||||
parts.add("fileParts", new HttpEntity<>(resource, headers));
|
|
||||||
|
|
||||||
headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.IMAGE_PNG);
|
|
||||||
resource = new ClassPathResource("logo.png", getClass());
|
|
||||||
parts.add("fileParts", new HttpEntity<>(resource, headers));
|
|
||||||
|
|
||||||
headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
|
||||||
parts.add("jsonPart", new HttpEntity<>(new Person("Jason"), headers));
|
|
||||||
|
|
||||||
return parts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -293,3 +293,13 @@ from the `reactor-test` module to do that, for example:
|
||||||
.thenCancel()
|
.thenCancel()
|
||||||
.verify();
|
.verify();
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
||||||
|
[[webtestclient-request-body]]
|
||||||
|
=== Request body
|
||||||
|
|
||||||
|
When it comes to building requests, the `WebTestClient` offers an identical API as the
|
||||||
|
`WebClient` and the implementation is mostly a simple pass-through. Please refer
|
||||||
|
to the <<web-reactive.adoc#webflux-client-body,WebClient documentation>> for examples on
|
||||||
|
how to prepare a request with a body including submitting form data, multipart requests,
|
||||||
|
and more.
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,84 @@ Or if you have the actual value, use the `syncBody` shortcut method:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[webflux-client-body-form]]
|
||||||
|
=== Form data
|
||||||
|
|
||||||
|
To send form data, provide a `MultiValueMap<String, String>` as the body. Note that the
|
||||||
|
content is automatically set to `"application/x-www-form-urlencoded"` by the
|
||||||
|
`FormHttpMessageWriter`:
|
||||||
|
|
||||||
|
[source,java,intent=0]
|
||||||
|
[subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
MultiValueMap<String, String> formData = ... ;
|
||||||
|
|
||||||
|
Mono<Void> result = client.post()
|
||||||
|
.uri("/path", id)
|
||||||
|
.syncBody(formData)
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(Void.class);
|
||||||
|
----
|
||||||
|
|
||||||
|
You can also supply form data in-line via `BodyInserters`:
|
||||||
|
|
||||||
|
[source,java,intent=0]
|
||||||
|
[subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
import static org.springframework.web.reactive.function.BodyInserters.*;
|
||||||
|
|
||||||
|
Mono<Void> result = client.post()
|
||||||
|
.uri("/path", id)
|
||||||
|
.body(fromFormData("k1", "v1").with("k2", "v2"))
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(Void.class);
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[webflux-client-body-multipart]]
|
||||||
|
=== Multipart data
|
||||||
|
|
||||||
|
To send multipart data, provide a `MultiValueMap<String, ?>` where values are either an
|
||||||
|
Object representing the part body, or an `HttpEntity` representing the part body and
|
||||||
|
headers. `MultipartBodyBuilder` can be used to build the parts:
|
||||||
|
|
||||||
|
[source,java,intent=0]
|
||||||
|
[subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
MultipartBodyBuilder builder = new MultipartBodyBuilder();
|
||||||
|
builder.part("fieldPart", "fieldValue");
|
||||||
|
builder.part("filePart", new FileSystemResource("...logo.png"));
|
||||||
|
builder.part("jsonPart", new Person("Jason"));
|
||||||
|
|
||||||
|
MultiValueMap<String, HttpEntity<?>> parts = builder.build();
|
||||||
|
|
||||||
|
Mono<Void> result = client.post()
|
||||||
|
.uri("/path", id)
|
||||||
|
.syncBody(parts)
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(Void.class);
|
||||||
|
----
|
||||||
|
|
||||||
|
Note that the content type for each part is automatically set based on the extension of the
|
||||||
|
file being written or the type of Object. If you prefer you can also be more explicit and
|
||||||
|
specify the content type for each part.
|
||||||
|
|
||||||
|
You can also supply multipart data in-line via `BodyInserters`:
|
||||||
|
|
||||||
|
[source,java,intent=0]
|
||||||
|
[subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
import static org.springframework.web.reactive.function.BodyInserters.*;
|
||||||
|
|
||||||
|
Mono<Void> result = client.post()
|
||||||
|
.uri("/path", id)
|
||||||
|
.body(fromMultipartData("fieldPart", "value").with("filePart", resource))
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(Void.class);
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[webflux-client-builder]]
|
[[webflux-client-builder]]
|
||||||
== Builder options
|
== Builder options
|
||||||
|
|
|
||||||
|
|
@ -2,24 +2,9 @@
|
||||||
= WebSockets
|
= WebSockets
|
||||||
:doc-spring-security: {doc-root}/spring-security/site/docs/current/reference
|
:doc-spring-security: {doc-root}/spring-security/site/docs/current/reference
|
||||||
|
|
||||||
This part of the reference documentation covers the Spring Framework support for Servlet stack
|
This part of the reference documentation covers support for Servlet stack WebSocket
|
||||||
based WebSocket messaging including use of STOMP as a a WebSocket sub-protocol.
|
messaging, SockJS-based fallback options, and the use of STOMP as a WebSocket messaging
|
||||||
|
sub-protocol.
|
||||||
<<websocket-intro>> establishes a frame of mind in which to think about
|
|
||||||
WebSocket, covering adoption challenges, design considerations, and thoughts on
|
|
||||||
when it is a good fit.
|
|
||||||
|
|
||||||
<<websocket-server>> reviews the Spring WebSocket API on the server-side, while
|
|
||||||
<<websocket-fallback>> explains the SockJS protocol and shows how to configure
|
|
||||||
and use it.
|
|
||||||
|
|
||||||
<<websocket-stomp-overview>> introduces the STOMP messaging protocol.
|
|
||||||
<<websocket-stomp-enable>> demonstrates how to configure STOMP support in Spring.
|
|
||||||
<<websocket-stomp-handle-annotations>> and the following sections explain how to
|
|
||||||
write annotated message handling methods, send messages, choose message broker
|
|
||||||
options, as well as work with the special "user" destinations. Finally,
|
|
||||||
<<websocket-stomp-testing>> lists three approaches to testing STOMP/WebSocket
|
|
||||||
applications.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -27,52 +12,54 @@ applications.
|
||||||
[[websocket-intro]]
|
[[websocket-intro]]
|
||||||
== Introduction
|
== Introduction
|
||||||
|
|
||||||
The WebSocket protocol http://tools.ietf.org/html/rfc6455[RFC 6455] defines an important
|
The WebSocket protocol http://tools.ietf.org/html/rfc6455[RFC 6455] provides a standardized
|
||||||
new capability for web applications: full-duplex, two-way communication between client
|
way to establish a full-duplex, two-way communication channel between client and server
|
||||||
and server. It is an exciting new capability on the heels of a long history of
|
over a single TCP connection. It is a different TCP protocol from HTTP but is designed to
|
||||||
techniques to make the web more interactive including Java Applets, XMLHttpRequest,
|
work over HTTP ports 80 and 443 thus allowing use of existing firewall rules.
|
||||||
Adobe Flash, ActiveXObject, various Comet techniques, server-sent events, and others.
|
|
||||||
|
|
||||||
A proper introduction to the WebSocket protocol is beyond the scope of this
|
A WebSocket interaction begins with an HTTP compatible handshake request that uses the
|
||||||
document. At a minimum however it's important to understand that HTTP is used only for
|
HTTP `"Upgrade"` header to request switching to the WebSocket protocol:
|
||||||
the initial handshake, which relies on a mechanism built into HTTP to request
|
|
||||||
a protocol upgrade (or in this case a protocol switch) to which the server can respond with
|
|
||||||
HTTP status 101 (switching protocols) if it agrees. Assuming the handshake succeeds
|
|
||||||
the TCP socket underlying the HTTP upgrade request remains open and both client and
|
|
||||||
server can use it to send messages to each other.
|
|
||||||
|
|
||||||
Spring Framework 4 includes a new `spring-websocket` module with comprehensive
|
[subs="quotes"]
|
||||||
WebSocket support. It is compatible with the Java WebSocket API standard
|
----
|
||||||
(http://jcp.org/en/jsr/detail?id=356[JSR-356])
|
GET /spring-websocket-portfolio/portfolio HTTP/1.1
|
||||||
and also provides additional value-add as explained in the rest of the introduction.
|
Host: localhost:8080
|
||||||
|
**Upgrade: websocket**
|
||||||
|
**Connection: Upgrade**
|
||||||
|
Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==
|
||||||
|
Sec-WebSocket-Protocol: v10.stomp, v11.stomp
|
||||||
|
Sec-WebSocket-Version: 13
|
||||||
|
Origin: http://localhost:8080
|
||||||
|
----
|
||||||
|
|
||||||
|
The response from a server with WebSocket support:
|
||||||
|
|
||||||
|
[subs="quotes"]
|
||||||
|
----
|
||||||
|
**HTTP/1.1 101 Switching Protocols**
|
||||||
|
Upgrade: websocket
|
||||||
|
Connection: Upgrade
|
||||||
|
Sec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0=
|
||||||
|
Sec-WebSocket-Protocol: v10.stomp
|
||||||
|
----
|
||||||
|
|
||||||
|
After a successful handshake the TCP socket underlying the HTTP upgrade request remains
|
||||||
|
open and both client and server can use send and receive messages.
|
||||||
|
|
||||||
|
If you Servlet container is running behind a web server (e.g. nginx) you will likely need
|
||||||
|
to configure it to send WebSocket upgrades to the backend server. Likewise if running in a
|
||||||
|
cloud environment check the instructions for WebSocket support of your cloud provider.
|
||||||
|
|
||||||
|
Beyond that, what do you do about restrictive proxies, outside your control, that preclude
|
||||||
|
WebSocket interactions either because they are not configured to pass on the `Upgrade`
|
||||||
|
header or because they close long lived connections that appear idle? The answer to this
|
||||||
|
is WebSocket emulation that tries to use WebSocket first and then falls back HTTP-based
|
||||||
|
techniques that WebSocket-like communication. See <<websocket-fallback>>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[websocket-into-fallback-options]]
|
|
||||||
=== Fallback Options
|
|
||||||
|
|
||||||
An important challenge to adoption is the lack of support for WebSocket in some
|
WebSocket brings up important design considerations that are important to recognize
|
||||||
browsers. Notably the first Internet Explorer version to support WebSocket is
|
|
||||||
version 10 (see http://caniuse.com/websockets for support by browser versions).
|
|
||||||
Furthermore, some restrictive proxies may be configured in ways that either
|
|
||||||
preclude the attempt to do an HTTP upgrade or otherwise break connection after
|
|
||||||
some time because it has remained opened for too long. A good overview on this
|
|
||||||
topic from Peter Lubbers is available in the InfoQ article
|
|
||||||
http://www.infoq.com/articles/Web-Sockets-Proxy-Servers["How HTML5 Web Sockets Interact With Proxy Servers"].
|
|
||||||
|
|
||||||
Therefore to build a WebSocket application today, fallback options are required in
|
|
||||||
order to simulate the WebSocket API where necessary. The Spring Framework provides
|
|
||||||
such transparent fallback options based on the https://github.com/sockjs/sockjs-protocol[SockJS protocol].
|
|
||||||
These options can be enabled through configuration and do not require modifying the
|
|
||||||
application otherwise.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[websocket-intro-architecture]]
|
|
||||||
=== Messaging
|
|
||||||
|
|
||||||
Aside from short-to-midterm adoption challenges, using WebSocket
|
|
||||||
brings up important design considerations that are important to recognize
|
|
||||||
early on, especially in contrast to what we know about building web applications today.
|
early on, especially in contrast to what we know about building web applications today.
|
||||||
|
|
||||||
Today REST is a widely accepted, understood, and supported
|
Today REST is a widely accepted, understood, and supported
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue