![JAR search and dependency download from the Maven repository](/logo.png)
org.apache.juneau.rest.RestRequest Maven / Gradle / Ivy
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
// * with the License. You may obtain a copy of the License at *
// * *
// * http://www.apache.org/licenses/LICENSE-2.0 *
// * *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
package org.apache.juneau.rest;
import static org.apache.juneau.common.internal.IOUtils.*;
import static org.apache.juneau.common.internal.ThrowableUtils.*;
import static org.apache.juneau.httppart.HttpPartType.*;
import static org.apache.juneau.internal.CollectionUtils.*;
import static java.lang.Integer.*;
import static java.util.Optional.*;
import java.io.*;
import java.lang.reflect.*;
import java.lang.reflect.Proxy;
import java.net.*;
import java.nio.charset.*;
import java.text.*;
import java.util.*;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import org.apache.http.*;
import org.apache.http.message.*;
import org.apache.juneau.*;
import org.apache.juneau.assertions.*;
import org.apache.juneau.common.internal.*;
import org.apache.juneau.config.*;
import org.apache.juneau.cp.Messages;
import org.apache.juneau.dto.swagger.*;
import org.apache.juneau.dto.swagger.Swagger;
import org.apache.juneau.http.annotation.Content;
import org.apache.juneau.http.annotation.FormData;
import org.apache.juneau.http.annotation.Header;
import org.apache.juneau.httppart.*;
import org.apache.juneau.httppart.bean.*;
import org.apache.juneau.rest.annotation.*;
import org.apache.juneau.rest.assertions.*;
import org.apache.juneau.rest.guard.*;
import org.apache.juneau.rest.httppart.*;
import org.apache.juneau.rest.logger.*;
import org.apache.juneau.http.header.*;
import org.apache.juneau.http.header.Date;
import org.apache.juneau.http.response.*;
import org.apache.juneau.http.response.BasicHttpException;
import org.apache.juneau.rest.staticfile.*;
import org.apache.juneau.rest.swagger.*;
import org.apache.juneau.rest.util.*;
import org.apache.juneau.svl.*;
import org.apache.juneau.uon.*;
/**
* Represents an HTTP request for a REST resource.
*
*
* The {@link RestRequest} object is an extension of the HttpServletRequest class
* with various built-in convenience methods for use in building REST interfaces.
* It can be accessed by passing it as a parameter on your REST Java method:
*
*
*
* @RestPost (...)
* public Object myMethod(RestRequest req ) {...}
*
*
*
* The primary methods on this class are:
*
*
* - {@link RestRequest}
*
* - Methods for accessing the request content:
*
* - {@link RestRequest#getContent() getContent()}
*
- {@link RestRequest#getInputStream() getInputStream()}
*
- {@link RestRequest#getReader() getReader()}
*
* - Methods for accessing HTTP parts:
*
* - {@link RestRequest#containsFormParam(String) containsFormParam(String)}
*
- {@link RestRequest#containsHeader(String) containsHeader(String)}
*
- {@link RestRequest#containsQueryParam(String) containsQueryParam(String)}
*
- {@link RestRequest#getHeader(Class) getHeader(Class)}
*
- {@link RestRequest#getHeader(String) getHeader(String)}
*
- {@link RestRequest#getHeaders() getHeaders()}
*
- {@link RestRequest#getFormParam(Class) getFormParam(Class)}
*
- {@link RestRequest#getFormParam(String) getFormParam(String)}
*
- {@link RestRequest#getFormParams() getFormParams()}
*
- {@link RestRequest#getPathParam(Class) getPathParam(Class)}
*
- {@link RestRequest#getPathParam(String) getPathParam(String)}
*
- {@link RestRequest#getPathParams() getPathParams()}
*
- {@link RestRequest#getPathRemainder() getPathRemainder()}
*
- {@link RestRequest#getQueryParam(Class) getQueryParam(Class)}
*
- {@link RestRequest#getQueryParam(String) getQueryParam(String)}
*
- {@link RestRequest#getQueryParams() getQueryParams()}
*
- {@link RestRequest#getQueryString() getQueryString()}
*
* - Methods for localization:
*
* - {@link RestRequest#getLocale() getLocale()}
*
- {@link RestRequest#getMessage(String,Object...) getMessage(String,Object...)}
*
- {@link RestRequest#getMessages() getMessages()}
*
- {@link RestRequest#getTimeZone() getTimeZone()}
*
* - Methods for accessing static files:
*
* - {@link RestRequest#getStaticFiles() getStaticFiles()}
*
- {@link RestRequest#getVarResolverSession() getVarResolverSession()}
*
* - Methods for assertions:
*
* - {@link RestRequest#assertContent() assertContent()}
*
- {@link RestRequest#assertCharset() assertCharset()}
*
- {@link RestRequest#assertFormParam(String) assertFormParam(String)}
*
- {@link RestRequest#assertHeader(String) assertHeader(String)}
*
- {@link RestRequest#assertQueryParam(String) assertQueryParam(String)}
*
- {@link RestRequest#assertRequestLine() assertRequestLine()}
*
* - Other:
*
* - {@link RestRequest#getAttribute(String) getAttribute(String)}
*
- {@link RestRequest#getAttributes() getAttributes()}
*
- {@link RestRequest#getAuthorityPath() getAuthorityPath()}
*
- {@link RestRequest#getBeanSession() getBeanSession()}
*
- {@link RestRequest#getCharset() getCharset()}
*
- {@link RestRequest#getConfig() getConfig()}
*
- {@link RestRequest#getContext() getContext()}
*
- {@link RestRequest#getContextPath() getContextPath()}
*
- {@link RestRequest#getHttpServletRequest() getHttpServletRequest()}
*
- {@link RestRequest#getMethod() getMethod()}
*
- {@link RestRequest#getOpContext() getOpContext()}
*
- {@link RestRequest#getOperationSwagger() getOperationSwagger()}
*
- {@link RestRequest#getPartParserSession() getPartParserSession()}
*
- {@link RestRequest#getPartSerializerSession() getPartSerializerSession()}
*
- {@link RestRequest#getPathInfo() getPathInfo()}
*
- {@link RestRequest#getProtocolVersion() getProtocolVersion()}
*
- {@link RestRequest#getRequest(Class) getRequest(Class)}
*
- {@link RestRequest#getRequestLine() getRequestLine()}
*
- {@link RestRequest#getRequestURI() getRequestURI()}
*
- {@link RestRequest#getRequestURL() getRequestURL()}
*
- {@link RestRequest#getServletPath() getServletPath()}
*
- {@link RestRequest#getSession() getSession()}
*
- {@link RestRequest#getSwagger() getSwagger()}
*
- {@link RestRequest#getUriContext() getUriContext()}
*
- {@link RestRequest#getUriResolver() getUriResolver()}
*
- {@link RestRequest#isDebug() isDebug()}
*
- {@link RestRequest#isPlainText() isPlainText()}
*
- {@link RestRequest#isUserInRole(String) isUserInRole(String)}
*
- {@link RestRequest#setAttribute(String,Object) setAttribute(String,Object)}
*
- {@link RestRequest#setCharset(Charset) setCharset(Charset)}
*
- {@link RestRequest#setDebug() setDebug()}
*
- {@link RestRequest#setException(Throwable) setException(Throwable)}
*
- {@link RestRequest#setNoTrace() setNoTrace()}
*
*
*
*
* See Also:
*
*/
@SuppressWarnings({ "unchecked", "unused" })
public final class RestRequest extends HttpServletRequestWrapper {
// Constructor initialized.
private HttpServletRequest inner;
private final RestContext context;
private final RestOpContext opContext;
private final RequestContent content;
private final BeanSession beanSession;
private final RequestQueryParams queryParams;
private final RequestPathParams pathParams;
private final RequestHeaders headers;
private final RequestAttributes attrs;
private final HttpPartParserSession partParserSession;
private final RestSession session;
// Lazy initialized.
private VarResolverSession varSession;
private RequestFormParams formParams;
private UriContext uriContext;
private String authorityPath;
private Config config;
private Swagger swagger;
private Charset charset;
/**
* Constructor.
*/
RestRequest(RestOpContext opContext, RestSession session) throws Exception {
super(session.getRequest());
this.session = session;
this.opContext = opContext;
inner = session.getRequest();
context = session.getContext();
attrs = new RequestAttributes(this);
queryParams = new RequestQueryParams(this, session.getQueryParams(), true);
headers = new RequestHeaders(this, queryParams, false);
content = new RequestContent(this);
if (context.isAllowContentParam()) {
String b = queryParams.get("content").asString().orElse(null);
if (b != null) {
headers.set("Content-Type", UonSerializer.DEFAULT.getResponseContentType());
content.mediaType(MediaType.UON).parser(UonParser.DEFAULT).content(b.getBytes(UTF8));
}
}
pathParams = new RequestPathParams(session, this, true);
beanSession = opContext.getBeanContext().getSession();
partParserSession = opContext.getPartParser().getPartSession();
pathParams.parser(partParserSession);
queryParams
.addDefault(opContext.getDefaultRequestQueryData().getAll())
.parser(partParserSession);
headers
.addDefault(opContext.getDefaultRequestHeaders().getAll())
.addDefault(context.getDefaultRequestHeaders().getAll())
.parser(partParserSession);
content
.encoders(opContext.getEncoders())
.parsers(opContext.getParsers())
.maxInput(opContext.getMaxInput());
attrs
.addDefault(opContext.getDefaultRequestAttributes())
.addDefault(context.getDefaultRequestAttributes());
if (isDebug())
inner = CachingHttpServletRequest.wrap(inner);
}
//-----------------------------------------------------------------------------------------------------------------
// Request line.
//-----------------------------------------------------------------------------------------------------------------
/**
* Returns the request line of this request.
*
* @return The request line of this request.
*/
public RequestLine getRequestLine() {
String x = inner.getProtocol();
int i = x.indexOf('/');
int j = x.indexOf('.', i);
ProtocolVersion pv = new ProtocolVersion(x.substring(0,i), parseInt(x.substring(i+1,j)), parseInt(x.substring(j+1)));
return new BasicRequestLine(inner.getMethod(), inner.getRequestURI(), pv);
}
/**
* Returns the protocol version from the request line of this request.
*
* @return The protocol version from the request line of this request.
*/
public ProtocolVersion getProtocolVersion() {
return getRequestLine().getProtocolVersion();
}
//-----------------------------------------------------------------------------------------------------------------
// Assertions
//-----------------------------------------------------------------------------------------------------------------
/**
* Returns an assertion on the request line returned by {@link #getRequestLine()}.
*
* Example:
*
* // Validates the request content contains "foo".
* request
* .assertRequestLine().protocol().minor().is(1);
*
*
* @return A new assertion object.
*/
public FluentRequestLineAssertion assertRequestLine() {
return new FluentRequestLineAssertion<>(getRequestLine(), this);
}
/**
* Returns a fluent assertion for the request content.
*
* Example:
*
* // Validates the request content contains "foo".
* request
* .assertContent().asString().is("foo" );
*
*
* @return A new fluent assertion on the content, never null .
*/
public FluentRequestContentAssertion assertContent() {
return new FluentRequestContentAssertion<>(getContent(), this);
}
/**
* Returns a fluent assertion for the specified header.
*
* Example:
*
* // Validates the content type is JSON.
* request
* .assertHeader("Content-Type" ).asString().is("application/json" );
*
*
* @param name The header name.
* @return A new fluent assertion on the parameter, never null .
*/
public FluentRequestHeaderAssertion assertHeader(String name) {
return new FluentRequestHeaderAssertion<>(getHeaderParam(name), this);
}
/**
* Returns a fluent assertion for the specified query parameter.
*
* Example:
*
* // Validates the content type is JSON.
* request
* .assertQueryParam("foo" ).asString().contains("bar" );
*
*
* @param name The query parameter name.
* @return A new fluent assertion on the parameter, never null .
*/
public FluentRequestQueryParamAssertion assertQueryParam(String name) {
return new FluentRequestQueryParamAssertion<>(getQueryParam(name), this);
}
/**
* Returns a fluent assertion for the specified form parameter.
*
* Example:
*
* // Validates the content type is JSON.
* request
* .assertFormParam("foo" ).asString().contains("bar" );
*
*
* @param name The query parameter name.
* @return A new fluent assertion on the parameter, never null .
*/
public FluentRequestFormParamAssertion assertFormParam(String name) {
return new FluentRequestFormParamAssertion<>(getFormParam(name), this);
}
//-----------------------------------------------------------------------------------------------------------------
// Headers
//-----------------------------------------------------------------------------------------------------------------
/**
* Request headers.
*
*
* Returns a {@link RequestHeaders} object that encapsulates access to HTTP headers on the request.
*
*
Example:
*
* @RestPost (...)
* public Object myMethod(RestRequest req ) {
*
* // Get access to headers.
* RequestHeaders headers = req .getRequestHeaders();
*
* // Add a default value.
* headers .addDefault("ETag" , DEFAULT_UUID );
*
* // Get a header value as a POJO.
* UUID etag = headers .get("ETag" ).as(UUID.class ).orElse(null );
*
* // Get a standard header.
* Optional<CacheControl> = headers .getCacheControl();
* }
*
*
* Notes:
* -
* This object is modifiable.
*
-
* Values are converted from strings using the registered part parser on the resource class.
*
-
* The {@link RequestHeaders} object can also be passed as a parameter on the method.
*
-
* The {@link Header @Header} annotation can be used to access individual header values.
*
*
* See Also:
* - HTTP Parts
*
*
* @return
* The headers on this request.
*
Never null .
*/
public RequestHeaders getHeaders() {
return headers;
}
/**
* Returns the last header with a specified name of this message.
*
*
* If there is more than one matching header in the message the last element of getHeaders(String) is returned.
*
If there is no matching header in the message, an empty request header object is returned.
*
*
Example:
*
* // Gets a header and throws a BadRequest if it doesn't exist.
* request
* .getHeader("Foo" )
* .assertValue().exists()
* .get();
*
*
* @param name The header name.
* @return The request header object, never null .
*/
public RequestHeader getHeaderParam(String name) {
return headers.getLast(name);
}
/**
* Returns true if this request contains the specified header.
*
* @param name The header name.
* @return true if this request contains the specified header.
*/
public boolean containsHeader(String name) {
return headers.contains(name);
}
/**
* Provides the ability to perform fluent-style assertions on the response character encoding.
*
* Examples:
*
* // Validates that the response content charset is UTF-8.
* request
* .assertCharset().is("utf-8" );
*
*
* @return A new fluent assertion object.
* @throws BasicHttpException If REST call failed.
*/
public FluentStringAssertion assertCharset() {
return new FluentStringAssertion<>(getCharset().name(), this);
}
/**
* Sets the charset to expect on the request content.
*
* @param value The new value to use for the request content.
*/
public void setCharset(Charset value) {
this.charset = value;
}
/**
* Returns the charset specified on the Content-Type header, or "UTF-8" if not specified.
*
* @return The charset to use to decode the request content.
*/
public Charset getCharset() {
if (charset == null) {
// Determine charset
// NOTE: Don't use super.getCharacterEncoding() because the spec is implemented inconsistently.
// Jetty returns the default charset instead of null if the character is not specified on the request.
String h = getHeaderParam("Content-Type").orElse(null);
if (h != null) {
int i = h.indexOf(";charset=");
if (i > 0)
charset = Charset.forName(h.substring(i+9).trim());
}
if (charset == null)
charset = opContext.getDefaultCharset();
if (charset == null)
charset = Charset.forName("UTF-8");
}
return charset;
}
/**
* Returns the preferred Locale that the client will accept content in, based on the Accept-Language header.
*
*
* If the client request doesn't provide an Accept-Language header, this method returns the default locale for the server.
*
* @return The preferred Locale that the client will accept content in. Never null .
*/
@Override
public Locale getLocale() {
Locale best = inner.getLocale();
String h = headers.get("Accept-Language").asString().orElse(null);
if (h != null) {
StringRanges sr = StringRanges.of(h);
float qValue = 0;
for (StringRange r : sr.toList()) {
if (r.getQValue() > qValue) {
best = toLocale(r.getName());
qValue = r.getQValue();
}
}
}
return best;
}
//-----------------------------------------------------------------------------------------------------------------
// Standard headers.
//-----------------------------------------------------------------------------------------------------------------
/**
* Returns the request header of the specified type.
*
*
* Type must have a name specified via the {@link org.apache.juneau.http.annotation.Header} annotation
* and a public constructor that takes in either value or name,value as strings.
*
*
* Typically any of the following:
*
* - {@link Accept}
*
- {@link AcceptCharset}
*
- {@link AcceptEncoding}
*
- {@link AcceptLanguage}
*
- {@link AcceptRanges}
*
- {@link Authorization}
*
- {@link CacheControl}
*
- {@link ClientVersion}
*
- {@link Connection}
*
- {@link ContentDisposition}
*
- {@link ContentEncoding}
*
- {@link ContentLength}
*
- {@link ContentType}
*
- {@link Date}
*
- {@link Debug}
*
- {@link Expect}
*
- {@link Forwarded}
*
- {@link From}
*
- {@link Host}
*
- {@link IfMatch}
*
- {@link IfModifiedSince}
*
- {@link IfNoneMatch}
*
- {@link IfRange}
*
- {@link IfUnmodifiedSince}
*
- {@link MaxForwards}
*
- {@link NoTrace}
*
- {@link Origin}
*
- {@link Pragma}
*
- {@link ProxyAuthorization}
*
- {@link Range}
*
- {@link Referer}
*
- {@link TE}
*
- {@link Thrown}
*
- {@link Upgrade}
*
- {@link UserAgent}
*
- {@link Warning}
*
*
* @param The bean type to create.
* @param type The bean type to create.
* @return The parsed header on the request, never null .
*/
public Optional getHeader(Class type) {
return headers.get(type);
}
/**
* Returns the Time-Zone header value on the request if there is one.
*
*
* Example: "GMT" .
*
* @return The parsed header on the request, never null .
*/
public Optional getTimeZone() {
String tz = headers.get("Time-Zone").asString().orElse(null);
if (tz != null)
return optional(TimeZone.getTimeZone(tz));
return Optional.empty();
}
//-----------------------------------------------------------------------------------------------------------------
// Attributes
//-----------------------------------------------------------------------------------------------------------------
/**
* Request attributes.
*
*
* Returns a {@link RequestAttributes} object that encapsulates access to attributes on the request.
*
*
Example:
*
* @RestPost (...)
* public Object myMethod(RestRequest req ) {
*
* // Get access to attributes.
* RequestAttributes attributes = req .getAttributes();
*
* // Get a header value as a POJO.
* UUID etag = attributes .get("ETag" , UUID.class );
* }
*
*
* Notes:
* -
* This object is modifiable.
*
-
* Values are converted from strings using the registered part parser on the resource class.
*
-
* The {@link RequestAttributes} object can also be passed as a parameter on the method.
*
-
* The {@link Attr @Attr} annotation can be used to access individual attribute values.
*
*
* See Also:
* - HTTP Parts
*
*
* @return
* The headers on this request.
*
Never null .
*/
public RequestAttributes getAttributes() {
return attrs;
}
/**
* Returns the request attribute with the specified name.
*
* @param name The attribute name.
* @return The attribute value, never null .
*/
@Override
public RequestAttribute getAttribute(String name) {
return attrs.get(name);
}
/**
* Sets a request attribute.
*
* @param name The attribute name.
* @param value The attribute value.
*/
@Override
public void setAttribute(String name, Object value) {
attrs.set(name, value);
}
//-----------------------------------------------------------------------------------------------------------------
// Query parameters
//-----------------------------------------------------------------------------------------------------------------
/**
* Query parameters.
*
*
* Returns a {@link RequestQueryParams} object that encapsulates access to URL GET parameters.
*
*
* Similar to {@link HttpServletRequest#getParameterMap()} but only looks for query parameters in the URL and not form posts.
*
*
Example:
*
* @RestGet (...)
* public void doGet(RestRequest req ) {
*
* // Get access to query parameters on the URL.
* RequestQueryParams query = req .getQuery();
*
* // Get query parameters converted to various types.
* int p1/ = query .getInteger("p1" ).orElse(null );
* String p2 = query .getString("p2" ).orElse(null );
* UUID p3 = query .get("p3" ).as(UUID.class ).orElse(null );
* }
*
*
* Notes:
* -
* This object is modifiable.
*
*
* See Also:
* - HTTP Parts
*
*
* @return
* The query parameters as a modifiable map.
*
Never null .
*/
public RequestQueryParams getQueryParams() {
return queryParams;
}
/**
* Shortcut for calling getRequestQuery().getLast(name ) .
*
* @param name The query parameter name.
* @return The query parameter, never null .
*/
public RequestQueryParam getQueryParam(String name) {
return queryParams.get(name);
}
/**
* Returns the request query parameter of the specified type.
*
*
* Type must have a name specified via the {@link org.apache.juneau.http.annotation.Query} annotation
* and a public constructor that takes in either value or name,value as strings.
*
* @param The bean type to create.
* @param type The bean type to create.
* @return The parsed query parameter on the request, never null .
*/
public Optional getQueryParam(Class type) {
return queryParams.get(type);
}
/**
* Returns true if this request contains the specified header.
*
* @param name The header name.
* @return true if this request contains the specified header.
*/
public boolean containsQueryParam(String name) {
return queryParams.contains(name);
}
//-----------------------------------------------------------------------------------------------------------------
// Form data parameters
//-----------------------------------------------------------------------------------------------------------------
/**
* Form-data.
*
*
* Returns a {@link RequestFormParams} object that encapsulates access to form post parameters.
*
*
* Similar to {@link HttpServletRequest#getParameterMap()}, but only looks for form data in the HTTP content.
*
*
Example:
*
* @RestPost (...)
* public void doPost(RestRequest req ) {
*
* // Get access to parsed form data parameters.
* RequestFormParams formParams = req .getFormParams();
*
* // Get form data parameters converted to various types.
* int p1 = formParams .get("p1" ).asInteger().orElse(0);
* String p2 = formParams .get("p2" ).asString().orElse(null );
* UUID p3 = formParams .get("p3" ).as(UUID.class ).orElse(null );
* }
*
*
* Notes:
* -
* This object is modifiable.
*
-
* Values are converted from strings using the registered part parser on the resource class.
*
-
* The {@link RequestFormParams} object can also be passed as a parameter on the method.
*
-
* The {@link FormData @FormDAta} annotation can be used to access individual form data parameter values.
*
*
* See Also:
* - HTTP Parts
*
*
* @return
* The URL-encoded form data from the request.
*
Never null .
* @throws InternalServerError If query parameters could not be parsed.
* @see org.apache.juneau.http.annotation.FormData
*/
public RequestFormParams getFormParams() throws InternalServerError {
try {
if (formParams == null)
formParams = new RequestFormParams(this, true).parser(partParserSession);
formParams.addDefault(opContext.getDefaultRequestFormData().getAll());
return formParams;
} catch (Exception e) {
throw new InternalServerError(e);
}
}
/**
* Shortcut for calling getFormData().getString(name) .
*
* @param name The form data parameter name.
* @return The form data parameter value, or null if not found.
*/
public RequestFormParam getFormParam(String name) {
return getFormParams().get(name);
}
/**
* Returns the request form-data parameter of the specified type.
*
*
* Type must have a name specified via the {@link org.apache.juneau.http.annotation.FormData} annotation
* and a public constructor that takes in either value or name,value as strings.
*
* @param The bean type to create.
* @param type The bean type to create.
* @return The parsed form-data parameter on the request, never null .
*/
public Optional getFormParam(Class type) {
return getFormParams().get(type);
}
/**
* Returns true if this request contains the specified header.
*
* @param name The header name.
* @return true if this request contains the specified header.
*/
public boolean containsFormParam(String name) {
return getFormParams().contains(name);
}
//-----------------------------------------------------------------------------------------------------------------
// Path parameters
//-----------------------------------------------------------------------------------------------------------------
/**
* Path parameters.
*
*
* Returns a {@link RequestPathParams} object that encapsulates access to URL path parameters.
*
*
Example:
*
* @RestGet ("/{foo}/{bar}/{baz}/*" )
* public void doGet(RestRequest req ) {
*
* // Get access to path data.
* RequestPathParams pathParams = req .getPathParams();
*
* // Example URL: /123/qux/true/quux
*
* int foo = pathParams .get("foo" ).asInteger().orElse(-1); // =123
* String bar = pathParams .get("bar" ).orElse(null ); // =qux
* boolean baz = pathParams .get("baz" ).asBoolean().orElse(false ); // =true
* String remainder = pathParams .getRemainder().orElse(null ); // =quux
* }
*
*
* Notes:
* -
* This object is modifiable.
*
*
* @return
* The path parameters.
*
Never null .
*/
public RequestPathParams getPathParams() {
return pathParams;
}
/**
* Shortcut for calling getPathParams().get(name ) .
*
* @param name The path parameter name.
* @return The path parameter, never null .
*/
public RequestPathParam getPathParam(String name) {
return pathParams.get(name);
}
/**
* Returns the request path parameter of the specified type.
*
*
* Type must have a name specified via the {@link org.apache.juneau.http.annotation.Path} annotation
* and a public constructor that takes in either value or name,value as strings.
*
* @param The bean type to create.
* @param type The bean type to create.
* @return The parsed form-data parameter on the request, never null .
*/
public Optional getPathParam(Class type) {
return pathParams.get(type);
}
/**
* Shortcut for calling getPathParams().getRemainder() .
*
* @return The path remainder value, never null .
*/
public RequestPathParam getPathRemainder() {
return pathParams.getRemainder();
}
//-----------------------------------------------------------------------------------------------------------------
// Content methods
//-----------------------------------------------------------------------------------------------------------------
/**
* Request content.
*
*
* Returns a {@link RequestContent} object that encapsulates access to the HTTP request content.
*
*
Example:
*
* @RestPost (...)
* public void doPost(RestRequest req ) {
*
* // Convert content to a linked list of Person objects.
* List<Person> list = req .getContent().as(LinkedList.class , Person.class );
* ..
* }
*
*
* Notes:
* -
* The {@link RequestContent} object can also be passed as a parameter on the method.
*
-
* The {@link Content @Content} annotation can be used to access the content as well.
*
*
* See Also:
* - HTTP Parts
*
*
* @return
* The content of this HTTP request.
*
Never null .
*/
public RequestContent getContent() {
return content;
}
/**
* Returns the HTTP content content as a {@link Reader}.
*
*
* If {@code allowHeaderParams} init parameter is true, then first looks for {@code &content=xxx} in the URL query
* string.
*
*
* Automatically handles GZipped input streams.
*
*
* This method is equivalent to calling getContent().getReader() .
*
* @return The HTTP content content as a {@link Reader}.
* @throws IOException If content could not be read.
*/
@Override
public BufferedReader getReader() throws IOException {
return getContent().getReader();
}
/**
* Returns the HTTP content content as an {@link InputStream}.
*
*
* Automatically handles GZipped input streams.
*
*
* This method is equivalent to calling getContent().getInputStream() .
*
* @return The negotiated input stream.
* @throws IOException If any error occurred while trying to get the input stream or wrap it in the GZIP wrapper.
*/
@Override
public ServletInputStream getInputStream() throws IOException {
return getContent().getInputStream();
}
//-----------------------------------------------------------------------------------------------------------------
// URI-related methods
//-----------------------------------------------------------------------------------------------------------------
/**
* Returns the portion of the request URI that indicates the context of the request.
*
*
The context path always comes first in a request URI.
* The path starts with a "/" character but does not end with a "/" character.
* For servlets in the default (root) context, this method returns "" .
* The container does not decode this string.
*
* @return The context path, never null .
* @see HttpServletRequest#getContextPath()
*/
@Override
public String getContextPath() {
String cp = context.getUriContext();
return cp == null ? inner.getContextPath() : cp;
}
/**
* Returns the URI authority portion of the request.
*
* @return The URI authority portion of the request.
*/
public String getAuthorityPath() {
if (authorityPath == null)
authorityPath = context.getUriAuthority();
if (authorityPath == null) {
String scheme = inner.getScheme();
int port = inner.getServerPort();
StringBuilder sb = new StringBuilder(inner.getScheme()).append("://").append(inner.getServerName());
if (! (port == 80 && "http".equals(scheme) || port == 443 && "https".equals(scheme)))
sb.append(':').append(port);
authorityPath = sb.toString();
}
return authorityPath;
}
/**
* Returns the part of this request's URL that calls the servlet.
*
*
* This path starts with a "/" character and includes either the servlet name or a path to the servlet,
* but does not include any extra path information or a query string.
*
* @return The servlet path, never null .
* @see HttpServletRequest#getServletPath()
*/
@Override
public String getServletPath() {
String cp = context.getUriContext();
String sp = inner.getServletPath();
return cp == null || ! sp.startsWith(cp) ? sp : sp.substring(cp.length());
}
/**
* Returns the URI context of the request.
*
*
* The URI context contains all the information about the URI of the request, such as the servlet URI, context
* path, etc...
*
* @return The URI context of the request.
*/
public UriContext getUriContext() {
if (uriContext == null)
uriContext = UriContext.of(getAuthorityPath(), getContextPath(), getServletPath(), StringUtils.urlEncodePath(inner.getPathInfo()));
return uriContext;
}
/**
* Returns a URI resolver that can be used to convert URIs to absolute or root-relative form.
*
* @param resolution The URI resolution rule.
* @param relativity The relative URI relativity rule.
* @return The URI resolver for this request.
*/
public UriResolver getUriResolver(UriResolution resolution, UriRelativity relativity) {
return UriResolver.of(resolution, relativity, getUriContext());
}
/**
* Shortcut for calling {@link #getUriResolver()} using {@link UriResolution#ROOT_RELATIVE} and
* {@link UriRelativity#RESOURCE}
*
* @return The URI resolver for this request.
*/
public UriResolver getUriResolver() {
return UriResolver.of(context.getUriResolution(), context.getUriRelativity(), getUriContext());
}
/**
* Returns the URI for this request.
*
*
* Similar to {@link #getRequestURI()} but returns the value as a {@link URI}.
* It also gives you the capability to override the query parameters (e.g. add new query parameters to the existing
* URI).
*
* @param includeQuery If true include the query parameters on the request.
* @param addQueryParams Augment the request URI with the specified query parameters.
* @return A new URI.
*/
public URI getUri(boolean includeQuery, Map addQueryParams) {
String uri = inner.getRequestURI();
if (includeQuery || addQueryParams != null) {
StringBuilder sb = new StringBuilder(uri);
RequestQueryParams rq = this.queryParams.copy();
if (addQueryParams != null)
for (Map.Entry e : addQueryParams.entrySet())
rq.set(e.getKey(), e.getValue());
if (! rq.isEmpty())
sb.append('?').append(rq.asQueryString());
uri = sb.toString();
}
try {
return new URI(uri);
} catch (URISyntaxException e) {
// Shouldn't happen.
throw asRuntimeException(e);
}
}
//-----------------------------------------------------------------------------------------------------------------
// Labels
//-----------------------------------------------------------------------------------------------------------------
/**
* Returns the localized swagger associated with the resource.
*
*
* A shortcut for calling getInfoProvider().getSwagger(request);
*
*
Example:
*
* @RestGet
* public List<Tag> swaggerTags(RestRequest req ) {
* return req .getSwagger().getTags();
* }
*
*
* Notes:
* -
* The {@link Swagger} object can also be passed as a parameter on the method.
*
*
* See Also:
* - {@link RestContext.Builder#swaggerProvider(Class)}
*
- {@link RestContext.Builder#swaggerProvider(SwaggerProvider)}
*
- Swagger
*
*
* @return
* The swagger associated with the resource.
*
Never null .
*/
public Optional getSwagger() {
return context.getSwagger(getLocale());
}
/**
* Returns the swagger for the Java method invoked.
*
* @return The swagger for the Java method as an {@link Optional}. Never null .
*/
public Optional getOperationSwagger() {
Optional swagger = getSwagger();
if (! swagger.isPresent())
return Optional.empty();
return ofNullable(swagger.get().getOperation(opContext.getPathPattern(), getMethod().toLowerCase()));
}
//-----------------------------------------------------------------------------------------------------------------
// Other methods
//-----------------------------------------------------------------------------------------------------------------
/**
* Returns the part serializer associated with this request.
*
* @return The part serializer associated with this request.
*/
public HttpPartParserSession getPartParserSession() {
return partParserSession;
}
/**
* Returns the HTTP method of this request.
*
*
* If allowHeaderParams init parameter is true , then first looks for
* &method=xxx in the URL query string.
*
* @return The HTTP method of this request.
*/
@Override
public String getMethod() {
return session.getMethod();
}
/**
* Returns true if &plainText=true was specified as a URL parameter.
*
*
* This indicates that the Content-Type of the output should always be set to "text/plain"
* to make it easy to render in a browser.
*
*
* This feature is useful for debugging.
*
* @return true if {@code &plainText=true} was specified as a URL parameter
*/
public boolean isPlainText() {
return "true".equals(queryParams.get("plainText").asString().orElse("false"));
}
/**
* Returns the resource bundle for the request locale.
*
*
Example:
*
* @RestGet
* public String hello(RestRequest req , @Query ("user" ) String user ) {
*
* // Return a localized message.
* return req .getMessages().getString("hello.message" , user );
* }
*
*
* Notes:
* -
* The {@link Messages} object can also be passed as a parameter on the method.
*
*
* See Also:
* - {@link org.apache.juneau.rest.RestRequest#getMessage(String,Object...)}
*
- Localized Messages
*
*
* @return
* The resource bundle.
*
Never null .
*/
public Messages getMessages() {
return context.getMessages().forLocale(getLocale());
}
/**
* Shortcut method for calling {@link RestRequest#getMessages()} and {@link Messages#getString(String,Object...)}.
*
* @param key The message key.
* @param args Optional {@link MessageFormat}-style arguments.
* @return The localized message.
*/
public String getMessage(String key, Object...args) {
return getMessages().getString(key, args);
}
/**
* Returns the resource context handling the request.
*
*
* Can be used to access servlet-init parameters or annotations during requests, such as in calls to
* {@link RestGuard#guard(RestRequest, RestResponse)}..
*
* @return The resource context handling the request.
*/
public RestContext getContext() {
return context;
}
/**
* Returns access to the inner {@link RestOpContext} of this method.
*
* @return The {@link RestOpContext} of this method. May be null if method has not yet been found.
*/
public RestOpContext getOpContext() {
return opContext;
}
/**
* Returns the {@link BeanSession} associated with this request.
*
* @return The request bean session.
*/
public BeanSession getBeanSession() {
return beanSession;
}
/**
* Returns true if debug mode is enabled.
*
* Debug mode is enabled by simply adding "?debug=true" to the query string or adding a Debug: true header on the request.
*
* @return true if debug mode is enabled.
*/
public boolean isDebug() {
return getAttribute("Debug").as(Boolean.class).orElse(false);
}
/**
* Sets the "Exception" attribute to the specified throwable.
*
*
* This exception is used by {@link CallLogger} for logging purposes.
*
* @param t The attribute value.
* @return This object.
*/
public RestRequest setException(Throwable t) {
setAttribute("Exception", t);
return this;
}
/**
* Sets the "NoTrace" attribute to the specified boolean.
*
*
* This flag is used by {@link CallLogger} and tells it not to log the current request.
*
* @param b The attribute value.
* @return This object.
*/
public RestRequest setNoTrace(Boolean b) {
setAttribute("NoTrace", b);
return this;
}
/**
* Shortcut for calling setNoTrace(true ) .
*
* @return This object.
*/
public RestRequest setNoTrace() {
return setNoTrace(true);
}
/**
* Sets the "Debug" attribute to the specified boolean.
*
*
* This flag is used by {@link CallLogger} to help determine how a request should be logged.
*
* @param b The attribute value.
* @return This object.
* @throws IOException If content could not be cached.
*/
public RestRequest setDebug(Boolean b) throws IOException {
setAttribute("Debug", b);
if (b)
inner = CachingHttpServletRequest.wrap(inner);
return this;
}
/**
* Shortcut for calling setDebug(true ) .
*
* @return This object.
* @throws IOException If content could not be cached.
*/
public RestRequest setDebug() throws IOException {
return setDebug(true);
}
/**
* Request-level variable resolver session.
*
*
* Used to resolve SVL variables in text.
*
*
Example:
*
* @RestGet
* public String hello(RestRequest req ) {
*
* // Get var resolver session.
* VarResolverSession session = getVarResolverSession();
*
* // Use it to construct a customized message from a query parameter.
* return session .resolve("Hello $RQ{user}!" );
* }
*
*
* Notes:
* -
* The {@link VarResolverSession} object can also be passed as a parameter on the method.
*
*
* See Also:
* - {@link org.apache.juneau.rest.RestContext#getVarResolver()}
*
- SVL Variables
*
*
* @return The variable resolver for this request.
*/
public VarResolverSession getVarResolverSession() {
if (varSession == null)
varSession = context
.getVarResolver()
.createSession(session.getBeanStore())
.bean(RestRequest.class, this)
.bean(RestSession.class, session);
return varSession;
}
/**
* Returns the static files registered on the REST resource context object.
*
*
* Used to retrieve localized files to be served up as static files through the REST API.
*
* @return This object.
*/
public StaticFiles getStaticFiles() {
return context.getStaticFiles();
}
/**
* Config file associated with the resource.
*
*
* Returns a config file with session-level variable resolution.
*
* The config file is identified via one of the following:
*
* - {@link Rest#config()}
*
- {@link RestContext.Builder#config(Config)}
*
*
* Example:
*
* @RestGet (...)
* public void doGet(RestRequest req ) {
*
* // Get config file.
* Config config = req .getConfig();
*
* // Get simple values from config file.
* int timeout = config .get("MyResource/timeout" ).asInteger().orElse(=10000);
*
* // Get complex values from config file.
* MyBean bean = config .get("MyResource/myBean" ).as(MyBean.class ).orElse(null );
* }
*
*
* Notes:
* -
* The {@link Config} object can also be passed as a parameter on the method.
*
*
* See Also:
*
* @return
* The config file associated with the resource, or null if resource does not have a config file
* associated with it.
*/
public Config getConfig() {
if (config == null)
config = context.getConfig().resolving(getVarResolverSession());
return config;
}
/**
* Creates a proxy interface to retrieve HTTP parts of this request as a proxy bean.
*
* Examples:
*
* @RestPost ("/mypath/{p1}/{p2}/*" )
* public void myMethod(@Request MyRequest requestBean ) {...}
*
* public interface MyRequest {
*
* @Path // Path variable name inferred from getter.
* String getP1();
*
* @Path ("p2" )
* String getX();
*
* @Path ("/*" )
* String getRemainder();
*
* @Query
* String getQ1();
*
* // Schema-based query parameter: Pipe-delimited lists of comma-delimited lists of integers.
* @Query (
* collectionFormat="pipes"
* items=@Items (
* items=@SubItems (
* collectionFormat="csv"
* type="integer"
* )
* )
* )
* int [][] getQ3();
*
* @Header ("*" )
* Map<String,Object> getHeaders();
*
*
* @param The request bean interface to instantiate.
* @param c The request bean interface to instantiate.
* @return A new request bean proxy for this REST request.
*/
public T getRequest(Class c) {
return getRequest(RequestBeanMeta.create(c, getContext().getAnnotations()));
}
/**
* Same as {@link #getRequest(Class)} but used on pre-instantiated {@link RequestBeanMeta} objects.
*
* @param The request bean interface to instantiate.
* @param rbm The metadata about the request bean interface to create.
* @return A new request bean proxy for this REST request.
*/
public T getRequest(final RequestBeanMeta rbm) {
try {
Class c = (Class)rbm.getClassMeta().getInnerClass();
final BeanSession bs = getBeanSession();
final BeanMeta bm = bs.getBeanMeta(c);
return (T)Proxy.newProxyInstance(
c.getClassLoader(),
new Class[] { c },
(InvocationHandler) (proxy, method, args) -> {
RequestBeanPropertyMeta pm = rbm.getProperty(method.getName());
if (pm != null) {
HttpPartParserSession pp = pm.getParser(getPartParserSession());
HttpPartSchema schema = pm.getSchema();
String name = pm.getPartName();
ClassMeta> type = bs.getClassMeta(method.getGenericReturnType());
HttpPartType pt = pm.getPartType();
if (pt == HttpPartType.BODY)
return getContent().setSchema(schema).as(type);
if (pt == QUERY)
return getQueryParam(name).parser(pp).schema(schema).as(type).orElse(null);
if (pt == FORMDATA)
return getFormParam(name).parser(pp).schema(schema).as(type).orElse(null);
if (pt == HEADER)
return getHeaderParam(name).parser(pp).schema(schema).as(type).orElse(null);
if (pt == PATH)
return getPathParam(name).parser(pp).schema(schema).as(type).orElse(null);
}
return null;
});
} catch (Exception e) {
throw asRuntimeException(e);
}
}
/* Called by RestSession.finish() */
void close() {
if (config != null) {
try {
config.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Returns the wrapped servlet request.
*
* @return The wrapped servlet request.
*/
public HttpServletRequest getHttpServletRequest() {
return inner;
}
/**
* Returns the part serializer session for this request.
*
* @return The part serializer session for this request.
*/
public HttpPartSerializerSession getPartSerializerSession() {
return opContext.getPartSerializer().getPartSession();
}
@Override /* Object */
public String toString() {
StringBuilder sb = new StringBuilder("\n").append(getRequestLine()).append("\n");
sb.append("---Headers---\n");
getHeaders().forEach(x -> sb.append("\t").append(x).append("\n"));
String m = getMethod();
if (m.equals("PUT") || m.equals("POST")) {
try {
sb.append("---Content UTF-8---\n");
sb.append(content.asString()).append("\n");
sb.append("---Content Hex---\n");
sb.append(content.asSpacedHex()).append("\n");
} catch (Exception e1) {
sb.append(e1.getLocalizedMessage());
}
}
return sb.toString();
}
//-----------------------------------------------------------------------------------------------------------------
// Utility methods
//-----------------------------------------------------------------------------------------------------------------
/*
* Converts an Accept-Language value entry to a Locale.
*/
private static Locale toLocale(String lang) {
String country = "";
int i = lang.indexOf('-');
if (i > -1) {
country = lang.substring(i+1).trim();
lang = lang.substring(0,i).trim();
}
return new Locale(lang, country);
}
}