001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.servlet.function;
018
019import java.io.IOException;
020import java.net.URI;
021import java.time.Instant;
022import java.time.ZonedDateTime;
023import java.util.Collection;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027import java.util.concurrent.CompletionStage;
028import java.util.function.BiFunction;
029import java.util.function.Consumer;
030
031import javax.servlet.ServletException;
032import javax.servlet.http.Cookie;
033import javax.servlet.http.HttpServletRequest;
034import javax.servlet.http.HttpServletResponse;
035
036import org.reactivestreams.Publisher;
037
038import org.springframework.core.ParameterizedTypeReference;
039import org.springframework.http.CacheControl;
040import org.springframework.http.HttpHeaders;
041import org.springframework.http.HttpMethod;
042import org.springframework.http.HttpStatus;
043import org.springframework.http.MediaType;
044import org.springframework.http.converter.HttpMessageConverter;
045import org.springframework.lang.Nullable;
046import org.springframework.util.MultiValueMap;
047import org.springframework.web.servlet.ModelAndView;
048
049/**
050 * Represents a typed server-side HTTP response, as returned
051 * by a {@linkplain HandlerFunction handler function} or
052 * {@linkplain HandlerFilterFunction filter function}.
053 *
054 * @author Arjen Poutsma
055 * @since 5.2
056 */
057public interface ServerResponse {
058
059        /**
060         * Return the status code of this response.
061         * @return the status as an HttpStatus enum value
062         * @throws IllegalArgumentException in case of an unknown HTTP status code
063         * @see HttpStatus#valueOf(int)
064         */
065        HttpStatus statusCode();
066
067        /**
068         * Return the (potentially non-standard) status code of this response.
069         * @return the status as an integer
070         * @see #statusCode()
071         * @see HttpStatus#valueOf(int)
072         */
073        int rawStatusCode();
074
075        /**
076         * Return the headers of this response.
077         */
078        HttpHeaders headers();
079
080        /**
081         * Return the cookies of this response.
082         */
083        MultiValueMap<String, Cookie> cookies();
084
085        /**
086         * Write this response to the given servlet response.
087         * @param request the current request
088         * @param response the response to write to
089         * @param context the context to use when writing
090         * @return a {@code ModelAndView} to render, or {@code null} if handled directly
091         */
092        @Nullable
093        ModelAndView writeTo(HttpServletRequest request, HttpServletResponse response, Context context)
094                throws ServletException, IOException;
095
096
097        // Static methods
098
099        /**
100         * Create a builder with the status code and headers of the given response.
101         * @param other the response to copy the status and headers from
102         * @return the created builder
103         */
104        static BodyBuilder from(ServerResponse other) {
105                return new DefaultServerResponseBuilder(other);
106        }
107
108        /**
109         * Create a builder with the given HTTP status.
110         * @param status the response status
111         * @return the created builder
112         */
113        static BodyBuilder status(HttpStatus status) {
114                return new DefaultServerResponseBuilder(status);
115        }
116
117        /**
118         * Create a builder with the given HTTP status.
119         * @param status the response status
120         * @return the created builder
121         */
122        static BodyBuilder status(int status) {
123                return new DefaultServerResponseBuilder(status);
124        }
125
126        /**
127         * Create a builder with the status set to {@linkplain HttpStatus#OK 200 OK}.
128         * @return the created builder
129         */
130        static BodyBuilder ok() {
131                return status(HttpStatus.OK);
132        }
133
134        /**
135         * Create a builder with a {@linkplain HttpStatus#CREATED 201 Created} status
136         * and a location header set to the given URI.
137         * @param location the location URI
138         * @return the created builder
139         */
140        static BodyBuilder created(URI location) {
141                BodyBuilder builder = status(HttpStatus.CREATED);
142                return builder.location(location);
143        }
144
145        /**
146         * Create a builder with a {@linkplain HttpStatus#ACCEPTED 202 Accepted} status.
147         * @return the created builder
148         */
149        static BodyBuilder accepted() {
150                return status(HttpStatus.ACCEPTED);
151        }
152
153        /**
154         * Create a builder with a {@linkplain HttpStatus#NO_CONTENT 204 No Content} status.
155         * @return the created builder
156         */
157        static HeadersBuilder<?> noContent() {
158                return status(HttpStatus.NO_CONTENT);
159        }
160
161        /**
162         * Create a builder with a {@linkplain HttpStatus#SEE_OTHER 303 See Other}
163         * status and a location header set to the given URI.
164         * @param location the location URI
165         * @return the created builder
166         */
167        static BodyBuilder seeOther(URI location) {
168                BodyBuilder builder = status(HttpStatus.SEE_OTHER);
169                return builder.location(location);
170        }
171
172        /**
173         * Create a builder with a {@linkplain HttpStatus#TEMPORARY_REDIRECT 307 Temporary Redirect}
174         * status and a location header set to the given URI.
175         * @param location the location URI
176         * @return the created builder
177         */
178        static BodyBuilder temporaryRedirect(URI location) {
179                BodyBuilder builder = status(HttpStatus.TEMPORARY_REDIRECT);
180                return builder.location(location);
181        }
182
183        /**
184         * Create a builder with a {@linkplain HttpStatus#PERMANENT_REDIRECT 308 Permanent Redirect}
185         * status and a location header set to the given URI.
186         * @param location the location URI
187         * @return the created builder
188         */
189        static BodyBuilder permanentRedirect(URI location) {
190                BodyBuilder builder = status(HttpStatus.PERMANENT_REDIRECT);
191                return builder.location(location);
192        }
193
194        /**
195         * Create a builder with a {@linkplain HttpStatus#BAD_REQUEST 400 Bad Request} status.
196         * @return the created builder
197         */
198        static BodyBuilder badRequest() {
199                return status(HttpStatus.BAD_REQUEST);
200        }
201
202        /**
203         * Create a builder with a {@linkplain HttpStatus#NOT_FOUND 404 Not Found} status.
204         * @return the created builder
205         */
206        static HeadersBuilder<?> notFound() {
207                return status(HttpStatus.NOT_FOUND);
208        }
209
210        /**
211         * Create a builder with a
212         * {@linkplain HttpStatus#UNPROCESSABLE_ENTITY 422 Unprocessable Entity} status.
213         * @return the created builder
214         */
215        static BodyBuilder unprocessableEntity() {
216                return status(HttpStatus.UNPROCESSABLE_ENTITY);
217        }
218
219
220        /**
221         * Defines a builder that adds headers to the response.
222         * @param <B> the builder subclass
223         */
224        interface HeadersBuilder<B extends HeadersBuilder<B>> {
225
226                /**
227                 * Add the given header value(s) under the given name.
228                 * @param headerName   the header name
229                 * @param headerValues the header value(s)
230                 * @return this builder
231                 * @see HttpHeaders#add(String, String)
232                 */
233                B header(String headerName, String... headerValues);
234
235                /**
236                 * Manipulate this response's headers with the given consumer. The
237                 * headers provided to the consumer are "live", so that the consumer can be used to
238                 * {@linkplain HttpHeaders#set(String, String) overwrite} existing header values,
239                 * {@linkplain HttpHeaders#remove(Object) remove} values, or use any of the other
240                 * {@link HttpHeaders} methods.
241                 * @param headersConsumer a function that consumes the {@code HttpHeaders}
242                 * @return this builder
243                 */
244                B headers(Consumer<HttpHeaders> headersConsumer);
245
246                /**
247                 * Add the given cookie to the response.
248                 * @param cookie the cookie to add
249                 * @return this builder
250                 */
251                B cookie(Cookie cookie);
252
253                /**
254                 * Manipulate this response's cookies with the given consumer. The
255                 * cookies provided to the consumer are "live", so that the consumer can be used to
256                 * {@linkplain MultiValueMap#set(Object, Object) overwrite} existing cookies,
257                 * {@linkplain MultiValueMap#remove(Object) remove} cookies, or use any of the other
258                 * {@link MultiValueMap} methods.
259                 * @param cookiesConsumer a function that consumes the cookies
260                 * @return this builder
261                 */
262                B cookies(Consumer<MultiValueMap<String, Cookie>> cookiesConsumer);
263
264                /**
265                 * Set the set of allowed {@link HttpMethod HTTP methods}, as specified
266                 * by the {@code Allow} header.
267                 *
268                 * @param allowedMethods the allowed methods
269                 * @return this builder
270                 * @see HttpHeaders#setAllow(Set)
271                 */
272                B allow(HttpMethod... allowedMethods);
273
274                /**
275                 * Set the set of allowed {@link HttpMethod HTTP methods}, as specified
276                 * by the {@code Allow} header.
277                 * @param allowedMethods the allowed methods
278                 * @return this builder
279                 * @see HttpHeaders#setAllow(Set)
280                 */
281                B allow(Set<HttpMethod> allowedMethods);
282
283                /**
284                 * Set the entity tag of the body, as specified by the {@code ETag} header.
285                 * @param eTag the new entity tag
286                 * @return this builder
287                 * @see HttpHeaders#setETag(String)
288                 */
289                B eTag(String eTag);
290
291                /**
292                 * Set the time the resource was last changed, as specified by the
293                 * {@code Last-Modified} header.
294                 * @param lastModified the last modified date
295                 * @return this builder
296                 * @see HttpHeaders#setLastModified(long)
297                 */
298                B lastModified(ZonedDateTime lastModified);
299
300                /**
301                 * Set the time the resource was last changed, as specified by the
302                 * {@code Last-Modified} header.
303                 * @param lastModified the last modified date
304                 * @return this builder
305                 * @see HttpHeaders#setLastModified(long)
306                 */
307                B lastModified(Instant lastModified);
308                /**
309                 * Set the location of a resource, as specified by the {@code Location} header.
310                 * @param location the location
311                 * @return this builder
312                 * @see HttpHeaders#setLocation(URI)
313                 */
314                B location(URI location);
315
316                /**
317                 * Set the caching directives for the resource, as specified by the HTTP 1.1
318                 * {@code Cache-Control} header.
319                 * <p>A {@code CacheControl} instance can be built like
320                 * {@code CacheControl.maxAge(3600).cachePublic().noTransform()}.
321                 * @param cacheControl a builder for cache-related HTTP response headers
322                 * @return this builder
323                 * @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2">RFC-7234 Section 5.2</a>
324                 */
325                B cacheControl(CacheControl cacheControl);
326
327                /**
328                 * Configure one or more request header names (e.g. "Accept-Language") to
329                 * add to the "Vary" response header to inform clients that the response is
330                 * subject to content negotiation and variances based on the value of the
331                 * given request headers. The configured request header names are added only
332                 * if not already present in the response "Vary" header.
333                 * @param requestHeaders request header names
334                 * @return this builder
335                 */
336                B varyBy(String... requestHeaders);
337
338                /**
339                 * Build the response entity with no body.
340                 */
341                ServerResponse build();
342
343                /**
344                 * Build the response entity with a custom write function.
345                 * @param writeFunction the function used to write to the {@link HttpServletResponse}
346                 */
347                ServerResponse build(BiFunction<HttpServletRequest, HttpServletResponse,
348                                ModelAndView> writeFunction);
349
350        }
351
352
353        /**
354         * Defines a builder that adds a body to the response.
355         */
356        interface BodyBuilder extends HeadersBuilder<BodyBuilder> {
357
358                /**
359                 * Set the length of the body in bytes, as specified by the
360                 * {@code Content-Length} header.
361                 * @param contentLength the content length
362                 * @return this builder
363                 * @see HttpHeaders#setContentLength(long)
364                 */
365                BodyBuilder contentLength(long contentLength);
366
367                /**
368                 * Set the {@linkplain MediaType media type} of the body, as specified by the
369                 * {@code Content-Type} header.
370                 * @param contentType the content type
371                 * @return this builder
372                 * @see HttpHeaders#setContentType(MediaType)
373                 */
374                BodyBuilder contentType(MediaType contentType);
375
376                /**
377                 * Set the body of the response to the given {@code Object} and return it.
378                 *
379                 * <p>Asynchronous response bodies are supported by providing a {@link CompletionStage} or
380                 * {@link Publisher} as body.
381                 * @param body the body of the response
382                 * @return the built response
383                 */
384                ServerResponse body(Object body);
385
386                /**
387                 * Set the body of the response to the given {@code Object} and return it. The parameter
388                 * {@code bodyType} is used to capture the generic type.
389                 *
390                 * @param body the body of the response
391                 * @param bodyType the type of the body, used to capture the generic type
392                 * @return the built response
393                 */
394                <T> ServerResponse body(T body, ParameterizedTypeReference<T> bodyType);
395
396                /**
397                 * Render the template with the given {@code name} using the given {@code modelAttributes}.
398                 * The model attributes are mapped under a
399                 * {@linkplain org.springframework.core.Conventions#getVariableName generated name}.
400                 * <p><em>Note: Empty {@link Collection Collections} are not added to
401                 * the model when using this method because we cannot correctly determine
402                 * the true convention name.</em>
403                 * @param name the name of the template to be rendered
404                 * @param modelAttributes the modelAttributes used to render the template
405                 * @return the built response
406                 */
407                ServerResponse render(String name, Object... modelAttributes);
408
409                /**
410                 * Render the template with the given {@code name} using the given {@code model}.
411                 * @param name the name of the template to be rendered
412                 * @param model the model used to render the template
413                 * @return the built response
414                 */
415                ServerResponse render(String name, Map<String, ?> model);
416        }
417
418
419        /**
420         * Defines the context used during the {@link #writeTo(HttpServletRequest, HttpServletResponse, Context)}.
421         */
422        interface Context {
423
424                /**
425                 * Return the {@link HttpMessageConverter HttpMessageConverters} to be used for response body conversion.
426                 * @return the list of message writers
427                 */
428                List<HttpMessageConverter<?>> messageConverters();
429
430        }
431
432}