Structural Fixes
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
In the context of web applications, _data binding_ involves the binding of HTTP request
|
||||
parameters (that is, form data or query parameters) to properties in a model object and
|
||||
its nested objects.
|
||||
|
||||
Only `public` properties following the
|
||||
https://www.oracle.com/java/technologies/javase/javabeans-spec.html[JavaBeans naming conventions]
|
||||
are exposed for data binding — for example, `public String getFirstName()` and
|
||||
`public void setFirstName(String)` methods for a `firstName` property.
|
||||
|
||||
TIP: The model object, and its nested object graph, is also sometimes referred to as a
|
||||
_command object_, _form-backing object_, or _POJO_ (Plain Old Java Object).
|
||||
|
||||
By default, Spring permits binding to all public properties in the model object graph.
|
||||
This means you need to carefully consider what public properties the model has, since a
|
||||
client could target any public property path, even some that are not expected to be
|
||||
targeted for a given use case.
|
||||
|
||||
For example, given an HTTP form data endpoint, a malicious client could supply values for
|
||||
properties that exist in the model object graph but are not part of the HTML form
|
||||
presented in the browser. This could lead to data being set on the model object and any
|
||||
of its nested objects, that is not expected to be updated.
|
||||
|
||||
The recommended approach is to use a _dedicated model object_ that exposes only
|
||||
properties that are relevant for the form submission. For example, on a form for changing
|
||||
a user's email address, the model object should declare a minimum set of properties such
|
||||
as in the following `ChangeEmailForm`.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public class ChangeEmailForm {
|
||||
|
||||
private String oldEmailAddress;
|
||||
private String newEmailAddress;
|
||||
|
||||
public void setOldEmailAddress(String oldEmailAddress) {
|
||||
this.oldEmailAddress = oldEmailAddress;
|
||||
}
|
||||
|
||||
public String getOldEmailAddress() {
|
||||
return this.oldEmailAddress;
|
||||
}
|
||||
|
||||
public void setNewEmailAddress(String newEmailAddress) {
|
||||
this.newEmailAddress = newEmailAddress;
|
||||
}
|
||||
|
||||
public String getNewEmailAddress() {
|
||||
return this.newEmailAddress;
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
If you cannot or do not want to use a _dedicated model object_ for each data
|
||||
binding use case, you **must** limit the properties that are allowed for data binding.
|
||||
Ideally, you can achieve this by registering _allowed field patterns_ via the
|
||||
`setAllowedFields()` method on `WebDataBinder`.
|
||||
|
||||
For example, to register allowed field patterns in your application, you can implement an
|
||||
`@InitBinder` method in a `@Controller` or `@ControllerAdvice` component as shown below:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Controller
|
||||
public class ChangeEmailController {
|
||||
|
||||
@InitBinder
|
||||
void initBinder(WebDataBinder binder) {
|
||||
binder.setAllowedFields("oldEmailAddress", "newEmailAddress");
|
||||
}
|
||||
|
||||
// @RequestMapping methods, etc.
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
In addition to registering allowed patterns, it is also possible to register _disallowed
|
||||
field patterns_ via the `setDisallowedFields()` method in `DataBinder` and its subclasses.
|
||||
Please note, however, that an "allow list" is safer than a "deny list". Consequently,
|
||||
`setAllowedFields()` should be favored over `setDisallowedFields()`.
|
||||
|
||||
Note that matching against allowed field patterns is case-sensitive; whereas, matching
|
||||
against disallowed field patterns is case-insensitive. In addition, a field matching a
|
||||
disallowed pattern will not be accepted even if it also happens to match a pattern in the
|
||||
allowed list.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
It is extremely important to properly configure allowed and disallowed field patterns
|
||||
when exposing your domain model directly for data binding purposes. Otherwise, it is a
|
||||
big security risk.
|
||||
|
||||
Furthermore, it is strongly recommended that you do **not** use types from your domain
|
||||
model such as JPA or Hibernate entities as the model object in data binding scenarios.
|
||||
====
|
||||
330
framework-docs/modules/ROOT/partials/web/web-uris.adoc
Normal file
330
framework-docs/modules/ROOT/partials/web/web-uris.adoc
Normal file
@@ -0,0 +1,330 @@
|
||||
[[uricomponents]]
|
||||
= UriComponents
|
||||
[.small]#Spring MVC and Spring WebFlux#
|
||||
|
||||
`UriComponentsBuilder` helps to build URI's from URI templates with variables, as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
UriComponents uriComponents = UriComponentsBuilder
|
||||
.fromUriString("https://example.com/hotels/{hotel}") // <1>
|
||||
.queryParam("q", "{q}") // <2>
|
||||
.encode() // <3>
|
||||
.build(); // <4>
|
||||
|
||||
URI uri = uriComponents.expand("Westin", "123").toUri(); // <5>
|
||||
----
|
||||
<1> Static factory method with a URI template.
|
||||
<2> Add or replace URI components.
|
||||
<3> Request to have the URI template and URI variables encoded.
|
||||
<4> Build a `UriComponents`.
|
||||
<5> Expand variables and obtain the `URI`.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
val uriComponents = UriComponentsBuilder
|
||||
.fromUriString("https://example.com/hotels/{hotel}") // <1>
|
||||
.queryParam("q", "{q}") // <2>
|
||||
.encode() // <3>
|
||||
.build() // <4>
|
||||
|
||||
val uri = uriComponents.expand("Westin", "123").toUri() // <5>
|
||||
----
|
||||
<1> Static factory method with a URI template.
|
||||
<2> Add or replace URI components.
|
||||
<3> Request to have the URI template and URI variables encoded.
|
||||
<4> Build a `UriComponents`.
|
||||
<5> Expand variables and obtain the `URI`.
|
||||
|
||||
The preceding example can be consolidated into one chain and shortened with `buildAndExpand`,
|
||||
as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
URI uri = UriComponentsBuilder
|
||||
.fromUriString("https://example.com/hotels/{hotel}")
|
||||
.queryParam("q", "{q}")
|
||||
.encode()
|
||||
.buildAndExpand("Westin", "123")
|
||||
.toUri();
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
val uri = UriComponentsBuilder
|
||||
.fromUriString("https://example.com/hotels/{hotel}")
|
||||
.queryParam("q", "{q}")
|
||||
.encode()
|
||||
.buildAndExpand("Westin", "123")
|
||||
.toUri()
|
||||
----
|
||||
|
||||
You can shorten it further by going directly to a URI (which implies encoding),
|
||||
as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
URI uri = UriComponentsBuilder
|
||||
.fromUriString("https://example.com/hotels/{hotel}")
|
||||
.queryParam("q", "{q}")
|
||||
.build("Westin", "123");
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
val uri = UriComponentsBuilder
|
||||
.fromUriString("https://example.com/hotels/{hotel}")
|
||||
.queryParam("q", "{q}")
|
||||
.build("Westin", "123")
|
||||
----
|
||||
|
||||
You can shorten it further still with a full URI template, as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
URI uri = UriComponentsBuilder
|
||||
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
|
||||
.build("Westin", "123");
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
val uri = UriComponentsBuilder
|
||||
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
|
||||
.build("Westin", "123")
|
||||
----
|
||||
|
||||
|
||||
|
||||
[[uribuilder]]
|
||||
= UriBuilder
|
||||
[.small]#Spring MVC and Spring WebFlux#
|
||||
|
||||
<<web-uricomponents, `UriComponentsBuilder`>> implements `UriBuilder`. You can create a
|
||||
`UriBuilder`, in turn, with a `UriBuilderFactory`. Together, `UriBuilderFactory` and
|
||||
`UriBuilder` provide a pluggable mechanism to build URIs from URI templates, based on
|
||||
shared configuration, such as a base URL, encoding preferences, and other details.
|
||||
|
||||
You can configure `RestTemplate` and `WebClient` with a `UriBuilderFactory`
|
||||
to customize the preparation of URIs. `DefaultUriBuilderFactory` is a default
|
||||
implementation of `UriBuilderFactory` that uses `UriComponentsBuilder` internally and
|
||||
exposes shared configuration options.
|
||||
|
||||
The following example shows how to configure a `RestTemplate`:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
|
||||
|
||||
String baseUrl = "https://example.org";
|
||||
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
|
||||
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
|
||||
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
restTemplate.setUriTemplateHandler(factory);
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode
|
||||
|
||||
val baseUrl = "https://example.org"
|
||||
val factory = DefaultUriBuilderFactory(baseUrl)
|
||||
factory.encodingMode = EncodingMode.TEMPLATE_AND_VALUES
|
||||
|
||||
val restTemplate = RestTemplate()
|
||||
restTemplate.uriTemplateHandler = factory
|
||||
----
|
||||
|
||||
The following example configures a `WebClient`:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
|
||||
|
||||
String baseUrl = "https://example.org";
|
||||
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
|
||||
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
|
||||
|
||||
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode
|
||||
|
||||
val baseUrl = "https://example.org"
|
||||
val factory = DefaultUriBuilderFactory(baseUrl)
|
||||
factory.encodingMode = EncodingMode.TEMPLATE_AND_VALUES
|
||||
|
||||
val client = WebClient.builder().uriBuilderFactory(factory).build()
|
||||
----
|
||||
|
||||
In addition, you can also use `DefaultUriBuilderFactory` directly. It is similar to using
|
||||
`UriComponentsBuilder` but, instead of static factory methods, it is an actual instance
|
||||
that holds configuration and preferences, as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
String baseUrl = "https://example.com";
|
||||
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);
|
||||
|
||||
URI uri = uriBuilderFactory.uriString("/hotels/{hotel}")
|
||||
.queryParam("q", "{q}")
|
||||
.build("Westin", "123");
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
val baseUrl = "https://example.com"
|
||||
val uriBuilderFactory = DefaultUriBuilderFactory(baseUrl)
|
||||
|
||||
val uri = uriBuilderFactory.uriString("/hotels/{hotel}")
|
||||
.queryParam("q", "{q}")
|
||||
.build("Westin", "123")
|
||||
----
|
||||
|
||||
|
||||
[[uri-encoding]]
|
||||
= URI Encoding
|
||||
[.small]#Spring MVC and Spring WebFlux#
|
||||
|
||||
`UriComponentsBuilder` exposes encoding options at two levels:
|
||||
|
||||
* {api-spring-framework}/web/util/UriComponentsBuilder.html#encode--[UriComponentsBuilder#encode()]:
|
||||
Pre-encodes the URI template first and then strictly encodes URI variables when expanded.
|
||||
* {api-spring-framework}/web/util/UriComponents.html#encode--[UriComponents#encode()]:
|
||||
Encodes URI components _after_ URI variables are expanded.
|
||||
|
||||
Both options replace non-ASCII and illegal characters with escaped octets. However, the first option
|
||||
also replaces characters with reserved meaning that appear in URI variables.
|
||||
|
||||
TIP: Consider ";", which is legal in a path but has reserved meaning. The first option replaces
|
||||
";" with "%3B" in URI variables but not in the URI template. By contrast, the second option never
|
||||
replaces ";", since it is a legal character in a path.
|
||||
|
||||
For most cases, the first option is likely to give the expected result, because it treats URI
|
||||
variables as opaque data to be fully encoded, while the second option is useful if URI
|
||||
variables do intentionally contain reserved characters. The second option is also useful
|
||||
when not expanding URI variables at all since that will also encode anything that
|
||||
incidentally looks like a URI variable.
|
||||
|
||||
The following example uses the first option:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
|
||||
.queryParam("q", "{q}")
|
||||
.encode()
|
||||
.buildAndExpand("New York", "foo+bar")
|
||||
.toUri();
|
||||
|
||||
// Result is "/hotel%20list/New%20York?q=foo%2Bbar"
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
|
||||
.queryParam("q", "{q}")
|
||||
.encode()
|
||||
.buildAndExpand("New York", "foo+bar")
|
||||
.toUri()
|
||||
|
||||
// Result is "/hotel%20list/New%20York?q=foo%2Bbar"
|
||||
----
|
||||
|
||||
You can shorten the preceding example by going directly to the URI (which implies encoding),
|
||||
as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
|
||||
.queryParam("q", "{q}")
|
||||
.build("New York", "foo+bar");
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
|
||||
.queryParam("q", "{q}")
|
||||
.build("New York", "foo+bar")
|
||||
----
|
||||
|
||||
You can shorten it further still with a full URI template, as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
URI uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
|
||||
.build("New York", "foo+bar");
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
val uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
|
||||
.build("New York", "foo+bar")
|
||||
----
|
||||
|
||||
The `WebClient` and the `RestTemplate` expand and encode URI templates internally through
|
||||
the `UriBuilderFactory` strategy. Both can be configured with a custom strategy,
|
||||
as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
String baseUrl = "https://example.com";
|
||||
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl)
|
||||
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
|
||||
|
||||
// Customize the RestTemplate..
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
restTemplate.setUriTemplateHandler(factory);
|
||||
|
||||
// Customize the WebClient..
|
||||
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
val baseUrl = "https://example.com"
|
||||
val factory = DefaultUriBuilderFactory(baseUrl).apply {
|
||||
encodingMode = EncodingMode.TEMPLATE_AND_VALUES
|
||||
}
|
||||
|
||||
// Customize the RestTemplate..
|
||||
val restTemplate = RestTemplate().apply {
|
||||
uriTemplateHandler = factory
|
||||
}
|
||||
|
||||
// Customize the WebClient..
|
||||
val client = WebClient.builder().uriBuilderFactory(factory).build()
|
||||
----
|
||||
|
||||
The `DefaultUriBuilderFactory` implementation uses `UriComponentsBuilder` internally to
|
||||
expand and encode URI templates. As a factory, it provides a single place to configure
|
||||
the approach to encoding, based on one of the below encoding modes:
|
||||
|
||||
* `TEMPLATE_AND_VALUES`: Uses `UriComponentsBuilder#encode()`, corresponding to
|
||||
the first option in the earlier list, to pre-encode the URI template and strictly encode URI variables when
|
||||
expanded.
|
||||
* `VALUES_ONLY`: Does not encode the URI template and, instead, applies strict encoding
|
||||
to URI variables through `UriUtils#encodeUriVariables` prior to expanding them into the
|
||||
template.
|
||||
* `URI_COMPONENT`: Uses `UriComponents#encode()`, corresponding to the second option in the earlier list, to
|
||||
encode URI component value _after_ URI variables are expanded.
|
||||
* `NONE`: No encoding is applied.
|
||||
|
||||
The `RestTemplate` is set to `EncodingMode.URI_COMPONENT` for historic
|
||||
reasons and for backwards compatibility. The `WebClient` relies on the default value
|
||||
in `DefaultUriBuilderFactory`, which was changed from `EncodingMode.URI_COMPONENT` in
|
||||
5.0.x to `EncodingMode.TEMPLATE_AND_VALUES` in 5.1.
|
||||
103
framework-docs/modules/ROOT/partials/web/websocket-intro.adoc
Normal file
103
framework-docs/modules/ROOT/partials/web/websocket-intro.adoc
Normal file
@@ -0,0 +1,103 @@
|
||||
[[introduction-to-websocket]]
|
||||
= Introduction to WebSocket
|
||||
|
||||
The WebSocket protocol, https://tools.ietf.org/html/rfc6455[RFC 6455], provides a standardized
|
||||
way to establish a full-duplex, two-way communication channel between client and server
|
||||
over a single TCP connection. It is a different TCP protocol from HTTP but is designed to
|
||||
work over HTTP, using ports 80 and 443 and allowing re-use of existing firewall rules.
|
||||
|
||||
A WebSocket interaction begins with an HTTP request that uses the HTTP `Upgrade` header
|
||||
to upgrade or, in this case, to switch to the WebSocket protocol. The following example
|
||||
shows such an interaction:
|
||||
|
||||
[source,yaml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
GET /spring-websocket-portfolio/portfolio HTTP/1.1
|
||||
Host: localhost:8080
|
||||
Upgrade: websocket <1>
|
||||
Connection: Upgrade <2>
|
||||
Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==
|
||||
Sec-WebSocket-Protocol: v10.stomp, v11.stomp
|
||||
Sec-WebSocket-Version: 13
|
||||
Origin: http://localhost:8080
|
||||
----
|
||||
<1> The `Upgrade` header.
|
||||
<2> Using the `Upgrade` connection.
|
||||
|
||||
|
||||
Instead of the usual 200 status code, a server with WebSocket support returns output
|
||||
similar to the following:
|
||||
|
||||
[source,yaml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
HTTP/1.1 101 Switching Protocols <1>
|
||||
Upgrade: websocket
|
||||
Connection: Upgrade
|
||||
Sec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0=
|
||||
Sec-WebSocket-Protocol: v10.stomp
|
||||
----
|
||||
<1> Protocol switch
|
||||
|
||||
|
||||
After a successful handshake, the TCP socket underlying the HTTP upgrade request remains
|
||||
open for both the client and the server to continue to send and receive messages.
|
||||
|
||||
A complete introduction of how WebSockets work is beyond the scope of this document.
|
||||
See RFC 6455, the WebSocket chapter of HTML5, or any of the many introductions and
|
||||
tutorials on the Web.
|
||||
|
||||
Note that, if a WebSocket server is running behind a web server (e.g. nginx), you
|
||||
likely need to configure it to pass WebSocket upgrade requests on to the WebSocket
|
||||
server. Likewise, if the application runs in a cloud environment, check the
|
||||
instructions of the cloud provider related to WebSocket support.
|
||||
|
||||
|
||||
|
||||
|
||||
[[http-versus-websocket]]
|
||||
== HTTP Versus WebSocket
|
||||
|
||||
Even though WebSocket is designed to be HTTP-compatible and starts with an HTTP request,
|
||||
it is important to understand that the two protocols lead to very different
|
||||
architectures and application programming models.
|
||||
|
||||
In HTTP and REST, an application is modeled as many URLs. To interact with the application,
|
||||
clients access those URLs, request-response style. Servers route requests to the
|
||||
appropriate handler based on the HTTP URL, method, and headers.
|
||||
|
||||
By contrast, in WebSockets, there is usually only one URL for the initial connect.
|
||||
Subsequently, all application messages flow on that same TCP connection. This points to
|
||||
an entirely different asynchronous, event-driven, messaging architecture.
|
||||
|
||||
WebSocket is also a low-level transport protocol, which, unlike HTTP, does not prescribe
|
||||
any semantics to the content of messages. That means that there is no way to route or process
|
||||
a message unless the client and the server agree on message semantics.
|
||||
|
||||
WebSocket clients and servers can negotiate the use of a higher-level, messaging protocol
|
||||
(for example, STOMP), through the `Sec-WebSocket-Protocol` header on the HTTP handshake request.
|
||||
In the absence of that, they need to come up with their own conventions.
|
||||
|
||||
|
||||
|
||||
|
||||
[[when-to-use-websockets]]
|
||||
== When to Use WebSockets
|
||||
|
||||
WebSockets can make a web page be dynamic and interactive. However, in many cases,
|
||||
a combination of AJAX and HTTP streaming or long polling can provide a simple and
|
||||
effective solution.
|
||||
|
||||
For example, news, mail, and social feeds need to update dynamically, but it may be
|
||||
perfectly okay to do so every few minutes. Collaboration, games, and financial apps, on
|
||||
the other hand, need to be much closer to real-time.
|
||||
|
||||
Latency alone is not a deciding factor. If the volume of messages is relatively low (for example,
|
||||
monitoring network failures) HTTP streaming or polling can provide an effective solution.
|
||||
It is the combination of low latency, high frequency, and high volume that make the best
|
||||
case for the use of WebSocket.
|
||||
|
||||
Keep in mind also that over the Internet, restrictive proxies that are outside of your control
|
||||
may 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. This
|
||||
means that the use of WebSocket for internal applications within the firewall is a more
|
||||
straightforward decision than it is for public facing applications.
|
||||
Reference in New Issue
Block a user