// *************************************************************************************************************************** | |
// * 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 java.util.Collections.*; | |
import static java.util.Optional.*; | |
import static java.util.logging.Level.*; | |
import static org.apache.juneau.Enablement.*; | |
import static org.apache.juneau.html.HtmlDocSerializer.*; | |
import static org.apache.juneau.httppart.HttpPartType.*; | |
import static org.apache.juneau.internal.ExceptionUtils.*; | |
import static org.apache.juneau.internal.IOUtils.*; | |
import static org.apache.juneau.serializer.Serializer.*; | |
import static org.apache.juneau.rest.HttpRuntimeException.*; | |
import static java.lang.Integer.*; | |
import static java.util.Collections.*; | |
import java.io.*; | |
import java.lang.reflect.*; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Proxy; | |
import java.net.*; | |
import java.nio.charset.*; | |
import java.text.*; | |
import java.util.*; | |
import java.util.logging.*; | |
import java.util.stream.*; | |
import javax.servlet.*; | |
import javax.servlet.http.*; | |
import org.apache.http.*; | |
import org.apache.http.message.*; | |
import org.apache.http.params.*; | |
import org.apache.juneau.*; | |
import org.apache.juneau.assertions.*; | |
import org.apache.juneau.config.*; | |
import org.apache.juneau.cp.*; | |
import org.apache.juneau.cp.Messages; | |
import org.apache.juneau.dto.swagger.*; | |
import org.apache.juneau.dto.swagger.Swagger; | |
import org.apache.juneau.http.*; | |
import org.apache.juneau.http.annotation.*; | |
import org.apache.juneau.http.annotation.Body; | |
import org.apache.juneau.http.annotation.FormData; | |
import org.apache.juneau.http.annotation.Header; | |
import org.apache.juneau.http.annotation.Path; | |
import org.apache.juneau.http.annotation.Query; | |
import org.apache.juneau.http.annotation.Response; | |
import org.apache.juneau.httppart.*; | |
import org.apache.juneau.httppart.bean.*; | |
import org.apache.juneau.internal.*; | |
import org.apache.juneau.jsonschema.*; | |
import org.apache.juneau.marshall.*; | |
import org.apache.juneau.oapi.*; | |
import org.apache.juneau.parser.*; | |
import org.apache.juneau.reflect.*; | |
import org.apache.juneau.rest.annotation.*; | |
import org.apache.juneau.rest.assertions.*; | |
import org.apache.juneau.http.header.*; | |
import org.apache.juneau.http.header.BasicHeader; | |
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.helper.*; | |
import org.apache.juneau.rest.logging.*; | |
import org.apache.juneau.rest.util.*; | |
import org.apache.juneau.rest.vars.*; | |
import org.apache.juneau.rest.widget.*; | |
import org.apache.juneau.serializer.*; | |
import org.apache.juneau.svl.*; | |
import org.apache.juneau.uon.*; | |
import org.apache.juneau.utils.*; | |
/** | |
* Represents an HTTP request for a REST resource. | |
* | |
* <p> | |
* Equivalent to {@link HttpServletRequest} except with some additional convenience methods. | |
* | |
* <p> | |
* For reference, given the URL <js>"http://localhost:9080/contextRoot/servletPath/foo?bar=baz#qux"</js>, the | |
* following methods return the following values.... | |
* <table class='styled'> | |
* <tr><th>Method</th><th>Value</th></tr> | |
* <tr><td>{@code getContextPath()}</td><td>{@code /contextRoot}</td></tr> | |
* <tr><td>{@code getPathInfo()}</td><td>{@code /foo}</td></tr> | |
* <tr><td>{@code getPathTranslated()}</td><td>{@code path-to-deployed-war-on-filesystem/foo}</td></tr> | |
* <tr><td>{@code getQueryString()}</td><td>{@code bar=baz}</td></tr> | |
* <tr><td>{@code getRequestURI()}</td><td>{@code /contextRoot/servletPath/foo}</td></tr> | |
* <tr><td>{@code getRequestURL()}</td><td>{@code http://localhost:9080/contextRoot/servletPath/foo}</td></tr> | |
* <tr><td>{@code getServletPath()}</td><td>{@code /servletPath}</td></tr> | |
* </table> | |
* | |
* <ul class='seealso'> | |
* <li class='link'>{@doc RestmRestRequest} | |
* </ul> | |
*/ | |
@SuppressWarnings({ "unchecked", "unused" }) | |
public final class RestRequest { | |
// Constructor initialized. | |
private HttpServletRequest inner; | |
private final RestContext context; | |
private final RestOperationContext opContext; | |
private final RequestBody body; | |
private final BeanSession beanSession; | |
private final RequestQueryParams queryParams; | |
private final RequestPathParams pathParams; | |
private final RequestHeaders headers; | |
private final RequestAttributes attrs; | |
private final HttpPartSerializerSession partSerializerSession; | |
private final HttpPartParserSession partParserSession; | |
private final RestCall call; | |
private final SerializerSessionArgs serializerSessionArgs; | |
private final ParserSessionArgs parserSessionArgs; | |
// 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(RestCall call) throws Exception { | |
this.call = call; | |
this.opContext = call.getRestOperationContext(); | |
inner = call.getRequest(); | |
context = call.getContext(); | |
attrs = new RequestAttributes(this); | |
queryParams = new RequestQueryParams(this, call.getQueryParams(), true); | |
headers = new RequestHeaders(this, queryParams, false); | |
body = new RequestBody(this); | |
if (context.isAllowBodyParam()) { | |
String b = queryParams.getString("body").orElse(null); | |
if (b != null) { | |
headers.set("Content-Type", UonSerializer.DEFAULT.getResponseContentType()); | |
body.load(MediaType.UON, UonParser.DEFAULT, b.getBytes(UTF8)); | |
} | |
} | |
pathParams = new RequestPathParams(call, this, true); | |
beanSession = opContext.createSession(); | |
parserSessionArgs = | |
ParserSessionArgs | |
.create() | |
.javaMethod(opContext.getJavaMethod()) | |
.locale(getLocale()) | |
.timeZone(getTimeZone().orElse(null)) | |
.debug(isDebug() ? true : null); | |
partParserSession = opContext.getPartParser().createPartSession(parserSessionArgs); | |
serializerSessionArgs = SerializerSessionArgs | |
.create() | |
.javaMethod(opContext.getJavaMethod()) | |
.locale(getLocale()) | |
.timeZone(getTimeZone().orElse(null)) | |
.debug(isDebug() ? true : null) | |
.uriContext(getUriContext()) | |
.resolver(getVarResolverSession()) | |
.useWhitespace(isPlainText() ? true : null); | |
partSerializerSession = opContext.getPartSerializer().createPartSession(serializerSessionArgs); | |
pathParams.parser(partParserSession); | |
queryParams | |
.addDefault(opContext.getDefaultRequestQuery().getAll()) | |
.parser(partParserSession); | |
headers | |
.addDefault(opContext.getDefaultRequestHeaders().getAll()) | |
.addDefault(context.getDefaultRequestHeaders().getAll()) | |
.parser(partParserSession); | |
body | |
.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()}. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Validates the request body contains "foo".</jc> | |
* <jv>request</jv> | |
* .assertRequestLine().protocol().minor().is(1); | |
* </p> | |
* | |
* @return A new assertion object. | |
*/ | |
public FluentRequestLineAssertion<RestRequest> assertRequestLine() { | |
return new FluentRequestLineAssertion<RestRequest>(getRequestLine(), this); | |
} | |
/** | |
* Returns a fluent assertion for the request body. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Validates the request body contains "foo".</jc> | |
* <jv>request</jv> | |
* .assertBody().is(<js>"foo"</js>); | |
* </p> | |
* | |
* @return A new fluent assertion on the body, never <jk>null</jk>. | |
*/ | |
public FluentRequestBodyAssertion<RestRequest> assertBody() { | |
return new FluentRequestBodyAssertion<RestRequest>(getBody(), this); | |
} | |
/** | |
* Returns a fluent assertion for the specified header. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Validates the content type is JSON.</jc> | |
* <jv>request</jv> | |
* .assertHeader(<js>"Content-Type"</js>).is(<js>"application/json"</js>); | |
* </p> | |
* | |
* @param name The header name. | |
* @return A new fluent assertion on the parameter, never <jk>null</jk>. | |
*/ | |
public FluentRequestHeaderAssertion<RestRequest> assertHeader(String name) { | |
return new FluentRequestHeaderAssertion<RestRequest>(getHeader(name), this); | |
} | |
/** | |
* Returns a fluent assertion for the specified query parameter. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Validates the content type is JSON.</jc> | |
* <jv>request</jv> | |
* .assertQueryParam(<js>"foo"</js>).asString().contains(<js>"bar"</js>); | |
* </p> | |
* | |
* @param name The query parameter name. | |
* @return A new fluent assertion on the parameter, never <jk>null</jk>. | |
*/ | |
public FluentRequestQueryParamAssertion<RestRequest> assertQueryParam(String name) { | |
return new FluentRequestQueryParamAssertion<RestRequest>(getQueryParam(name), this); | |
} | |
/** | |
* Returns a fluent assertion for the specified form parameter. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Validates the content type is JSON.</jc> | |
* <jv>request</jv> | |
* .assertFormParam(<js>"foo"</js>).asString().contains(<js>"bar"</js>); | |
* </p> | |
* | |
* @param name The query parameter name. | |
* @return A new fluent assertion on the parameter, never <jk>null</jk>. | |
*/ | |
public FluentRequestFormParamAssertion<RestRequest> assertFormParam(String name) { | |
return new FluentRequestFormParamAssertion<RestRequest>(getFormParam(name), this); | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// Headers | |
//----------------------------------------------------------------------------------------------------------------- | |
/** | |
* Request headers. | |
* | |
* <p> | |
* Returns a {@link RequestHeaders} object that encapsulates access to HTTP headers on the request. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <ja>@RestPost</ja>(...) | |
* <jk>public</jk> Object myMethod(RestRequest <jv>req</jv>) { | |
* | |
* <jc>// Get access to headers.</jc> | |
* RequestHeaders <jv>headers</jv> = <jv>req</jv>.getRequestHeaders(); | |
* | |
* <jc>// Add a default value.</jc> | |
* <jv>headers</jv>.addDefault(<js>"ETag"</js>, <jsf>DEFAULT_UUID</jsf>); | |
* | |
* <jc>// Get a header value as a POJO.</jc> | |
* UUID etag = <jv>headers</jv>.get(<js>"ETag"</js>).asType(UUID.<jk>class</jk>).orElse(<jk>null</jk>); | |
* | |
* <jc>// Get a standard header.</jc> | |
* Optional<CacheControl> = <jv>headers</jv>.getCacheControl(); | |
* } | |
* </p> | |
* | |
* <ul class='notes'> | |
* <li> | |
* This object is modifiable. | |
* <li> | |
* Values are converted from strings using the registered {@link RestContext#REST_partParser part-parser} on the resource class. | |
* <li> | |
* The {@link RequestHeaders} object can also be passed as a parameter on the method. | |
* <li> | |
* The {@link Header @Header} annotation can be used to access individual header values. | |
* </ul> | |
* | |
* <ul class='seealso'> | |
* <li class='link'>{@doc RestmRequestHeaders} | |
* </ul> | |
* | |
* @return | |
* The headers on this request. | |
* <br>Never <jk>null</jk>. | |
*/ | |
public RequestHeaders getHeaders() { | |
return headers; | |
} | |
/** | |
* Returns the last header with a specified name of this message. | |
* | |
* <p> | |
* If there is more than one matching header in the message the last element of <c>getHeaders(String)</c> is returned. | |
* <br>If there is no matching header in the message, an empty request header object is returned. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Gets a header and throws a BadRequest if it doesn't exist.</jc> | |
* <jv>request</jv> | |
* .getHeader(<js>"Foo"</js>) | |
* .assertValue().exists() | |
* .get(); | |
* </p> | |
* | |
* @param name The header name. | |
* @return The request header object, never <jk>null</jk>. | |
*/ | |
public RequestHeader getHeader(String name) { | |
return headers.getLast(name); | |
} | |
/** | |
* Returns <jk>true</jk> if this request contains the specified header. | |
* | |
* @param name The header name. | |
* @return <jk>true</jk> 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. | |
* | |
* <h5 class='section'>Examples:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Validates that the response content charset is UTF-8.</jc> | |
* <jv>request</jv> | |
* .assertCharset().is(<js>"utf-8"</js>); | |
* </p> | |
* | |
* @return A new fluent assertion object. | |
* @throws BasicHttpException If REST call failed. | |
*/ | |
public FluentStringAssertion<RestRequest> assertCharset() { | |
return new FluentStringAssertion<>(getCharset().name(), this); | |
} | |
/** | |
* Sets the charset to expect on the request body. | |
* | |
* @param value The new value to use for the request body. | |
*/ | |
public void setCharset(Charset value) { | |
this.charset = value; | |
} | |
/** | |
* Returns the charset specified on the <c>Content-Type</c> header, or <js>"UTF-8"</js> if not specified. | |
* | |
* @return The charset to use to decode the request body. | |
*/ | |
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 = getHeader("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. | |
* | |
* <p> | |
* If the client request doesn't provide an <c>Accept-Language</c> header, this method returns the default locale for the server. | |
* | |
* @return The preferred Locale that the client will accept content in. Never <jk>null</jk>. | |
*/ | |
public Locale getLocale() { | |
Locale best = inner.getLocale(); | |
String h = headers.getString("Accept-Language").orElse(null); | |
if (h != null) { | |
StringRanges sr = StringRanges.of(h); | |
float qValue = 0; | |
for (StringRange r : sr.getRanges()) { | |
if (r.getQValue() > qValue) { | |
best = toLocale(r.getName()); | |
qValue = r.getQValue(); | |
} | |
} | |
} | |
return best; | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// Standard headers. | |
//----------------------------------------------------------------------------------------------------------------- | |
/** | |
* Returns the <c>Accept</c> header on the request. | |
* | |
* <p> | |
* Content-Types that are acceptable for the response. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Accept: text/plain | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public Accept getAccept() { | |
return headers.get("Accept").asHeader(Accept.class); | |
} | |
/** | |
* Returns the <c>Accept-Charset</c> header on the request. | |
* | |
* <p> | |
* Character sets that are acceptable. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Accept-Charset: utf-8 | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public AcceptCharset getAcceptCharset() { | |
return headers.get("Accept-Charset").asHeader(AcceptCharset.class); | |
} | |
/** | |
* Returns the <c>Accept-Encoding</c> header on the request. | |
* | |
* <p> | |
* List of acceptable encodings. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Accept-Encoding: gzip, deflate | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public AcceptEncoding getAcceptEncoding() { | |
return headers.get("Accept-Encoding").asHeader(AcceptEncoding.class); | |
} | |
/** | |
* Returns the <c>Accept-Language</c> header on the request. | |
* | |
* <p> | |
* List of acceptable human languages for response. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Accept-Language: en-US | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public AcceptLanguage getAcceptLanguage() { | |
return headers.get("Accept-Language").asHeader(AcceptLanguage.class); | |
} | |
/** | |
* Returns the <c>Authorization</c> header on the request. | |
* | |
* <p> | |
* Authentication credentials for HTTP authentication. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public Authorization getAuthorization() { | |
return headers.get("Authorization").asHeader(Authorization.class); | |
} | |
/** | |
* Returns the <c>Cache-Control</c> header on the request. | |
* | |
* <p> | |
* Used to specify directives that must be obeyed by all caching mechanisms along the request-response chain. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Cache-Control: no-cache | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public CacheControl getCacheControl() { | |
return headers.get("Cache-Control").asHeader(CacheControl.class); | |
} | |
/** | |
* Returns the <c>Connection</c> header on the request. | |
* | |
* <p> | |
* Control options for the current connection and list of hop-by-hop request fields. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Connection: keep-alive | |
* Connection: Upgrade | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public Connection getConnection() { | |
return headers.get("Connection").asHeader(Connection.class); | |
} | |
/** | |
* Returns the <c>Content-Length</c> header on the request. | |
* | |
* <p> | |
* The length of the request body in octets (8-bit bytes). | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Content-Length: 348 | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public ContentLength getContentLength() { | |
return headers.get("Content-Length").asHeader(ContentLength.class); | |
} | |
/** | |
* Returns the <c>Content-Type</c> header on the request. | |
* | |
* <p> | |
* The MIME type of the body of the request (used with POST and PUT requests). | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Content-Type: application/x-www-form-urlencoded | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public ContentType getContentType() { | |
return headers.get("Content-Type").asHeader(ContentType.class); | |
} | |
/** | |
* Returns the <c>Date</c> header on the request. | |
* | |
* <p> | |
* The date and time that the message was originated (in "HTTP-date" format as defined by RFC 7231 Date/Time Formats). | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Date: Tue, 15 Nov 1994 08:12:31 GMT | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public Date getDate() { | |
return headers.get("Date").asHeader(Date.class); | |
} | |
/** | |
* Returns the <c>Expect</c> header on the request. | |
* | |
* <p> | |
* Indicates that particular server behaviors are required by the client. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Expect: 100-continue | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public Expect getExpect() { | |
return headers.get("Expect").asHeader(Expect.class); | |
} | |
/** | |
* Returns the <c>From</c> header on the request. | |
* | |
* <p> | |
* The email address of the user making the request. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* From: user@example.com | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public From getFrom() { | |
return headers.get("From").asHeader(From.class); | |
} | |
/** | |
* Returns the <c>Host</c> header on the request. | |
* | |
* <p> | |
* The domain name of the server (for virtual hosting), and the TCP port number on which the server is listening. | |
* The port number may be omitted if the port is the standard port for the service requested. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Host: en.wikipedia.org:8080 | |
* Host: en.wikipedia.org | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public Host getHost() { | |
return headers.get("Host").asHeader(Host.class); | |
} | |
/** | |
* Returns the <c>If-Match</c> header on the request. | |
* | |
* <p> | |
* Only perform the action if the client supplied entity matches the same entity on the server. | |
* This is mainly for methods like PUT to only update a resource if it has not been modified since the user last | |
* updated it. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* If-Match: "737060cd8c284d8af7ad3082f209582d" | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public IfMatch getIfMatch() { | |
return headers.get("If-Match").asHeader(IfMatch.class); | |
} | |
/** | |
* Returns the <c>If-Modified-Since</c> header on the request. | |
* | |
* <p> | |
* Allows a 304 Not Modified to be returned if content is unchanged. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public IfModifiedSince getIfModifiedSince() { | |
return headers.get("If-Modified-Since").asHeader(IfModifiedSince.class); | |
} | |
/** | |
* Returns the <c>If-None-Match</c> header on the request. | |
* | |
* <p> | |
* Allows a 304 Not Modified to be returned if content is unchanged, see HTTP ETag. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* If-None-Match: "737060cd8c284d8af7ad3082f209582d" | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public IfNoneMatch getIfNoneMatch() { | |
return headers.get("If-None-Match").asHeader(IfNoneMatch.class); | |
} | |
/** | |
* Returns the <c>If-Range</c> header on the request. | |
* | |
* <p> | |
* If the entity is unchanged, send me the part(s) that I am missing; otherwise, send me the entire ofNullable(entity. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* If-Range: "737060cd8c284d8af7ad3082f209582d" | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public IfRange getIfRange() { | |
return headers.get("If-Range").asHeader(IfRange.class); | |
} | |
/** | |
* Returns the <c>If-Unmodified-Since</c> header on the request. | |
* | |
* <p> | |
* Only send the response if the entity has not been modified since a specific time. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public IfUnmodifiedSince getIfUnmodifiedSince() { | |
return headers.get("If-Unmodified-Since").asHeader(IfUnmodifiedSince.class); | |
} | |
/** | |
* Returns the <c>Max-Forwards</c> header on the request. | |
* | |
* <p> | |
* Limit the number of times the message can be forwarded through proxies or gateways. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Max-Forwards: 10 | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public MaxForwards getMaxForwards() { | |
return headers.get("Max-Forwards").asHeader(MaxForwards.class); | |
} | |
/** | |
* Returns the <c>Pragma</c> header on the request. | |
* | |
* <p> | |
* Implementation-specific fields that may have various effects anywhere along the request-response chain. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Pragma: no-cache | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public Pragma getPragma() { | |
return headers.get("Pragma").asHeader(Pragma.class); | |
} | |
/** | |
* Returns the <c>Proxy-Authorization</c> header on the request. | |
* | |
* <p> | |
* Authorization credentials for connecting to a proxy. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public ProxyAuthorization getProxyAuthorization() { | |
return headers.get("Proxy-Authorization").asHeader(ProxyAuthorization.class); | |
} | |
/** | |
* Returns the <c>Range</c> header on the request. | |
* | |
* <p> | |
* Request only part of an entity. Bytes are numbered from 0. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Range: bytes=500-999 | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public Range getRange() { | |
return headers.get("Range").asHeader(Range.class); | |
} | |
/** | |
* Returns the <c>Referer</c> header on the request. | |
* | |
* <p> | |
* This is the address of the previous web page from which a link to the currently requested page was followed. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Referer: http://en.wikipedia.org/wiki/Main_Page | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public Referer getReferer() { | |
return headers.get("Referer").asHeader(Referer.class); | |
} | |
/** | |
* Returns the <c>TE</c> header on the request. | |
* | |
* <p> | |
* The transfer encodings the user agent is willing to accept: the same values as for the response header field | |
* Transfer-Encoding can be used, plus the "trailers" value (related to the "chunked" transfer method) to notify the | |
* server it expects to receive additional fields in the trailer after the last, zero-sized, chunk. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* TE: trailers, deflate | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public TE getTE() { | |
return headers.get("TE").asHeader(TE.class); | |
} | |
/** | |
* Returns the <c>Time-Zone</c> header value on the request if there is one. | |
* | |
* <p> | |
* Example: <js>"GMT"</js>. | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public Optional<TimeZone> getTimeZone() { | |
String tz = headers.getString("Time-Zone").orElse(null); | |
if (tz != null) | |
return of(TimeZone.getTimeZone(tz)); | |
return empty(); | |
} | |
/** | |
* Returns the <c>User-Agent</c> header on the request. | |
* | |
* <p> | |
* The user agent string of the user agent. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/21.0 | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public UserAgent getUserAgent() { | |
return headers.get("Upgrade").asHeader(UserAgent.class); | |
} | |
/** | |
* Returns the <c>Upgrade</c> header on the request. | |
* | |
* <p> | |
* Ask the server to upgrade to another protocol. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Upgrade: HTTP/2.0, HTTPS/1.3, IRC/6.9, RTA/x11, websocket | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public Upgrade getUpgrade() { | |
return headers.get("Upgrade").asHeader(Upgrade.class); | |
} | |
/** | |
* Returns the <c>Via</c> header on the request. | |
* | |
* <p> | |
* Informs the server of proxies through which the request was sent. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Via: 1.0 fred, 1.1 example.com (Apache/1.1) | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public Via getVia() { | |
return headers.get("Via").asHeader(Via.class); | |
} | |
/** | |
* Returns the <c>Warning</c> header on the request. | |
* | |
* <p> | |
* A general warning about possible problems with the entity body. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* Warning: 199 Miscellaneous warning | |
* </p> | |
* | |
* @return The parsed header on the request, never <jk>null</jk>. | |
*/ | |
public Warning getWarning() { | |
return headers.get("Warning").asHeader(Warning.class); | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// Attributes | |
//----------------------------------------------------------------------------------------------------------------- | |
/** | |
* Request attributes. | |
* | |
* <p> | |
* Returns a {@link RequestAttributes} object that encapsulates access to attributes on the request. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <ja>@RestPost</ja>(...) | |
* <jk>public</jk> Object myMethod(RestRequest <jv>req</jv>) { | |
* | |
* <jc>// Get access to attributes.</jc> | |
* RequestAttributes <jv>attributes</jv> = <jv>req</jv>.getAttributes(); | |
* | |
* <jc>// Get a header value as a POJO.</jc> | |
* UUID <jv>etag</jv> = <jv>attributes</jv>.get(<js>"ETag"</js>, UUID.<jk>class</jk>); | |
* } | |
* </p> | |
* | |
* <ul class='notes'> | |
* <li> | |
* This object is modifiable. | |
* <li> | |
* Values are converted from strings using the registered {@link RestContext#REST_partParser part-parser} on the resource class. | |
* <li> | |
* The {@link RequestAttributes} object can also be passed as a parameter on the method. | |
* <li> | |
* The {@link Attr @Attr} annotation can be used to access individual attribute values. | |
* </ul> | |
* | |
* <ul class='seealso'> | |
* <li class='link'>{@doc RestmRequestAttributes} | |
* </ul> | |
* | |
* @return | |
* The headers on this request. | |
* <br>Never <jk>null</jk>. | |
*/ | |
public RequestAttributes getAttributes() { | |
return attrs; | |
} | |
/** | |
* Returns the request attribute with the specified name. | |
* | |
* @param name The attribute name. | |
* @return The attribute value, never <jk>null</jk>. | |
*/ | |
public RequestAttribute getAttribute(String name) { | |
return attrs.get(name); | |
} | |
/** | |
* Sets a request attribute. | |
* | |
* @param name The attribute name. | |
* @param value The attribute value. | |
*/ | |
public void setAttribute(String name, Object value) { | |
attrs.set(name, value); | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// Query parameters | |
//----------------------------------------------------------------------------------------------------------------- | |
/** | |
* Query parameters. | |
* | |
* <p> | |
* Returns a {@link RequestQueryParams} object that encapsulates access to URL GET parameters. | |
* | |
* <p> | |
* Similar to {@link HttpServletRequest#getParameterMap()} but only looks for query parameters in the URL and not form posts. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <ja>@RestGet</ja>(...) | |
* <jk>public void</jk> doGet(RestRequest <jv>req</jv>) { | |
* | |
* <jc>// Get access to query parameters on the URL.</jc> | |
* RequestQueryParams <jv>query</jv> = <jv>req</jv>.getQuery(); | |
* | |
* <jc>// Get query parameters converted to various types.</jc> | |
* <jk>int</jk> <jv>p1/<jv> = <jv>query</jv>.getInteger(<js>"p1"</js>).orElse(<jk>null</jk>); | |
* String <jv>p2</jv> = <jv>query</jv>.getString(<js>"p2"</js>).orElse(<jk>null</jk>); | |
* UUID <jv>p3</jv> = <jv>query</jv>.get(<js>"p3"</js>).as(UUID.<jk>class</jk>).orElse(<jk>null</jk>); | |
* } | |
* </p> | |
* | |
* <ul class='notes'> | |
* <li> | |
* This object is modifiable. | |
* </ul> | |
* | |
* <ul class='seealso'> | |
* <li class='link'>{@doc RestmRequestQuery} | |
* </ul> | |
* | |
* @return | |
* The query parameters as a modifiable map. | |
* <br>Never <jk>null</jk>. | |
*/ | |
public RequestQueryParams getQueryParams() { | |
return queryParams; | |
} | |
/** | |
* Shortcut for calling <c>getRequestQuery().getLast(<jv>name</jv>)</c>. | |
* | |
* @param name The query parameter name. | |
* @return The query parameter, never <jk>null</jk>. | |
*/ | |
public RequestQueryParam getQueryParam(String name) { | |
return queryParams.get(name); | |
} | |
/** | |
* Returns <jk>true</jk> if this request contains the specified header. | |
* | |
* @param name The header name. | |
* @return <jk>true</jk> if this request contains the specified header. | |
*/ | |
public boolean containsQueryParam(String name) { | |
return queryParams.contains(name); | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// Form data parameters | |
//----------------------------------------------------------------------------------------------------------------- | |
/** | |
* Form-data. | |
* | |
* <p> | |
* Returns a {@link RequestFormParams} object that encapsulates access to form post parameters. | |
* | |
* <p> | |
* Similar to {@link HttpServletRequest#getParameterMap()}, but only looks for form data in the HTTP body. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <ja>@RestPost</ja>(...) | |
* <jk>public void</jk> doPost(RestRequest <jv>req</jv>) { | |
* | |
* <jc>// Get access to parsed form data parameters.</jc> | |
* RequestFormData <jv>formData</jv> = <jv>req</jv>.getFormData(); | |
* | |
* <jc>// Get form data parameters converted to various types.</jc> | |
* <jk>int</jk> <jv>p1</jv> = <jv>formData</jv>.get(<js>"p1"</js>, 0, <jk>int</jk>.<jk>class</jk>); | |
* String <jv>p2</jv> = <jv>formData</jv>.get(<js>"p2"</js>, String.<jk>class</jk>); | |
* UUID <jv>p3</jv> = <jv>formData</jv>.get(<js>"p3"</js>, UUID.<jk>class</jk>); | |
* } | |
* </p> | |
* | |
* <ul class='notes'> | |
* <li> | |
* This object is modifiable. | |
* <li> | |
* Values are converted from strings using the registered {@link RestContext#REST_partParser part-parser} on the resource class. | |
* <li> | |
* The {@link RequestFormParams} object can also be passed as a parameter on the method. | |
* <li> | |
* The {@link FormData @FormDAta} annotation can be used to access individual form data parameter values. | |
* </ul> | |
* | |
* <ul class='seealso'> | |
* <li class='link'>{@doc RestmRequestFormData} | |
* </ul> | |
* | |
* @return | |
* The URL-encoded form data from the request. | |
* <br>Never <jk>null</jk>. | |
* @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 <c>getFormData().getString(name)</c>. | |
* | |
* @param name The form data parameter name. | |
* @return The form data parameter value, or <jk>null</jk> if not found. | |
*/ | |
public RequestFormParam getFormParam(String name) { | |
return getFormParams().get(name); | |
} | |
/** | |
* Returns <jk>true</jk> if this request contains the specified header. | |
* | |
* @param name The header name. | |
* @return <jk>true</jk> if this request contains the specified header. | |
*/ | |
public boolean containsFormParam(String name) { | |
return getFormParams().contains(name); | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// Path parameters | |
//----------------------------------------------------------------------------------------------------------------- | |
/** | |
* Path parameters. | |
* | |
* <p> | |
* Returns a {@link RequestPathParams} object that encapsulates access to URL path parameters. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <ja>@RestGet</ja>(<js>"/{foo}/{bar}/{baz}/*"</js>) | |
* <jk>public void</jk> doGet(RestRequest <jv>req</jv>) { | |
* | |
* <jc>// Get access to path data.</jc> | |
* RequestPathParams <jv>pathParams</jv> = <jv>req</jv>.getPathParams(); | |
* | |
* <jc>// Example URL: /123/qux/true/quux</jc> | |
* | |
* <jk>int</jk> <jv>foo</jv> = <jv>pathParams</jv>.get(<js>"foo"</js>).asInteger().orElse(-1); <jc>// =123</jc> | |
* String <jv>bar</jv> = <jv>pathParams</jv>.get(<js>"bar"</js>).orElse(null); <jc>// =qux</jc> | |
* <jk>boolean</jk> <jv>baz</jv> = <jv>pathParams</jv>.get(<js>"baz"</js>).asBoolean().orElse(<jk>false</jk>); <jc>// =true</jc> | |
* String <jv>remainder</jv> = <jv>pathParams</jv>.getRemainder().orElse(<jk>null</jk>); <jc>// =quux</jc> | |
* } | |
* </p> | |
* | |
* <ul class='notes'> | |
* <li> | |
* This object is modifiable. | |
* </ul> | |
* | |
* @return | |
* The path parameters. | |
* <br>Never <jk>null</jk>. | |
*/ | |
public RequestPathParams getPathParams() { | |
return pathParams; | |
} | |
/** | |
* Shortcut for calling <c>getPathParams().get(<jv>name</jv>)</c>. | |
* | |
* @param name The path parameter name. | |
* @return The path parameter, never <jk>null</jk>. | |
*/ | |
public RequestPathParam getPathParam(String name) { | |
return pathParams.get(name); | |
} | |
/** | |
* Shortcut for calling <c>getPathParams().getRemainder()</c>. | |
* | |
* @return The path remainder value, never <jk>null</jk>. | |
*/ | |
public RequestPathParam getPathRemainder() { | |
return pathParams.getRemainder(); | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// Body methods | |
//----------------------------------------------------------------------------------------------------------------- | |
/** | |
* Request body. | |
* | |
* <p> | |
* Returns a {@link RequestBody} object that encapsulates access to the HTTP request body. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <ja>@RestPost</ja>(...) | |
* <jk>public void</jk> doPost(RestRequest <jv>req</jv>) { | |
* | |
* <jc>// Convert body to a linked list of Person objects.</jc> | |
* List<Person> <jv>list</jv> = <jv>req</jv>.getBody().asType(LinkedList.<jk>class</jk>, Person.<jk>class</jk>); | |
* .. | |
* } | |
* </p> | |
* | |
* <ul class='notes'> | |
* <li> | |
* The {@link RequestBody} object can also be passed as a parameter on the method. | |
* <li> | |
* The {@link Body @Body} annotation can be used to access the body as well. | |
* </ul> | |
* | |
* <ul class='seealso'> | |
* <li class='link'>{@doc RestmRequestBody} | |
* </ul> | |
* | |
* @return | |
* The body of this HTTP request. | |
* <br>Never <jk>null</jk>. | |
*/ | |
public RequestBody getBody() { | |
return body; | |
} | |
/** | |
* Returns the HTTP body content as a {@link Reader}. | |
* | |
* <p> | |
* If {@code allowHeaderParams} init parameter is true, then first looks for {@code &body=xxx} in the URL query | |
* string. | |
* | |
* <p> | |
* Automatically handles GZipped input streams. | |
* | |
* <p> | |
* This method is equivalent to calling <c>getBody().getReader()</c>. | |
* | |
* @return The HTTP body content as a {@link Reader}. | |
* @throws IOException If body could not be read. | |
*/ | |
public BufferedReader getReader() throws IOException { | |
return getBody().getReader(); | |
} | |
/** | |
* Returns the HTTP body content as an {@link InputStream}. | |
* | |
* <p> | |
* Automatically handles GZipped input streams. | |
* | |
* <p> | |
* This method is equivalent to calling <c>getBody().getInputStream()</c>. | |
* | |
* @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. | |
*/ | |
public ServletInputStream getInputStream() throws IOException { | |
return getBody().getInputStream(); | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// URI-related methods | |
//----------------------------------------------------------------------------------------------------------------- | |
/** | |
* Returns the portion of the request URI that indicates the context of the request. | |
* | |
* <p>The context path always comes first in a request URI. | |
* The path starts with a <js>"/"</js> character but does not end with a <js>"/"</js> character. | |
* For servlets in the default (root) context, this method returns <js>""</js>. | |
* The container does not decode this string. | |
* | |
* @return The context path, never <jk>null</jk>. | |
* @see HttpServletRequest#getContextPath() | |
*/ | |
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. | |
* | |
* <p> | |
* This path starts with a <js>"/"</js> 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 <jk>null</jk>. | |
* @see HttpServletRequest#getServletPath() | |
*/ | |
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. | |
* | |
* <p> | |
* 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. | |
* | |
* <p> | |
* 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 <jk>true</jk> 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<String,?> 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<String,?> 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 runtimeException(e); | |
} | |
} | |
/** | |
* Returns any extra path information associated with the URL the client sent when it made this request. | |
* | |
* <p> | |
* The extra path information follows the servlet path but precedes the query string and will start with a <js>"/"</js> character. | |
* This method returns <jk>null</jk> if there was no extra path information. | |
* | |
* @return The extra path information. | |
* @see HttpServletRequest#getPathInfo() | |
*/ | |
public String getPathInfo() { | |
return inner.getPathInfo(); | |
} | |
/** | |
* Returns the part of this request's URL from the protocol name up to the query string in the first line of the HTTP request. | |
* | |
* The web container does not decode this String | |
* | |
* @return The request URI. | |
* @see HttpServletRequest#getRequestURI() | |
*/ | |
public String getRequestURI() { | |
return inner.getRequestURI(); | |
} | |
/** | |
* Returns the query string that is contained in the request URL after the path. | |
* | |
* <p> | |
* This method returns <jk>null</jk> if the URL does not have a query string. | |
* | |
* @return The query string. | |
* @see HttpServletRequest#getQueryString() | |
*/ | |
public String getQueryString() { | |
return inner.getQueryString(); | |
} | |
/** | |
* Reconstructs the URL the client used to make the request. | |
* | |
* <p> | |
* The returned URL contains a protocol, server name, port number, and server path, but it does not include query string parameters. | |
* | |
* @return The request URL. | |
* @see HttpServletRequest#getRequestURL() | |
*/ | |
public StringBuffer getRequestURL() { | |
return inner.getRequestURL(); | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// Labels | |
//----------------------------------------------------------------------------------------------------------------- | |
/** | |
* Returns the localized swagger associated with the resource. | |
* | |
* <p> | |
* A shortcut for calling <c>getInfoProvider().getSwagger(request);</c> | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <ja>@RestGet</ja> | |
* <jk>public</jk> List<Tag> swaggerTags(RestRequest <jv>req</jv>) { | |
* <jk>return</jk> <jv>req</jv>.getSwagger().getTags(); | |
* } | |
* </p> | |
* | |
* <ul class='notes'> | |
* <li> | |
* The {@link Swagger} object can also be passed as a parameter on the method. | |
* </ul> | |
* | |
* <ul class='seealso'> | |
* <li class='jf'>{@link org.apache.juneau.rest.RestContext#REST_swaggerProvider} | |
* <li class='link'>{@doc RestSwagger} | |
* </ul> | |
* | |
* @return | |
* The swagger associated with the resource. | |
* <br>Never <jk>null</jk>. | |
*/ | |
public Optional<Swagger> 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 <jk>null</jk>. | |
*/ | |
public Optional<Operation> getOperationSwagger() { | |
Optional<Swagger> swagger = getSwagger(); | |
if (! swagger.isPresent()) | |
return Optional.empty(); | |
return swagger.get().operation(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 part serializer associated with this request. | |
* | |
* @return The part serializer associated with this request. | |
*/ | |
public HttpPartSerializerSession getPartSerializerSession() { | |
return partSerializerSession; | |
} | |
/** | |
* Returns the HTTP method of this request. | |
* | |
* <p> | |
* If <c>allowHeaderParams</c> init parameter is <jk>true</jk>, then first looks for | |
* <c>&method=xxx</c> in the URL query string. | |
* | |
* @return The HTTP method of this request. | |
*/ | |
public String getMethod() { | |
return call.getMethod(); | |
} | |
/** | |
* Returns <jk>true</jk> if <c>&plainText=true</c> was specified as a URL parameter. | |
* | |
* <p> | |
* This indicates that the <c>Content-Type</c> of the output should always be set to <js>"text/plain"</js> | |
* to make it easy to render in a browser. | |
* | |
* <p> | |
* This feature is useful for debugging. | |
* | |
* @return <jk>true</jk> if {@code &plainText=true} was specified as a URL parameter | |
*/ | |
public boolean isPlainText() { | |
return "true".equals(queryParams.getString("plainText").orElse("false")); | |
} | |
/** | |
* Returns the resource bundle for the request locale. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <ja>@RestGet</ja> | |
* <jk>public</jk> String hello(RestRequest <jv>req</jv>, <ja>@Query</ja>(<js>"user"</js>) String <jv>user</jv>) { | |
* | |
* <jc>// Return a localized message.</jc> | |
* <jk>return</jk> <jv>req</jv>.getMessages().getString(<js>"hello.message"</js>, <jv>user</jv>); | |
* } | |
* </p> | |
* | |
* <ul class='notes'> | |
* <li> | |
* The {@link Messages} object can also be passed as a parameter on the method. | |
* </ul> | |
* | |
* <ul class='seealso'> | |
* <li class='jf'>{@link org.apache.juneau.rest.RestContext#REST_messages} | |
* <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getMessage(String,Object...)} | |
* <li class='link'>{@doc RestMessages} | |
* </ul> | |
* | |
* @return | |
* The resource bundle. | |
* <br>Never <jk>null</jk>. | |
*/ | |
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. | |
* | |
* <p> | |
* 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 RestOperationContext} of this method. | |
* | |
* @return The {@link RestOperationContext} of this method. May be <jk>null</jk> if method has not yet been found. | |
*/ | |
public RestOperationContext getOpContext() { | |
return opContext; | |
} | |
/** | |
* Returns the {@link BeanSession} associated with this request. | |
* | |
* @return The request bean session. | |
*/ | |
public BeanSession getBeanSession() { | |
return beanSession; | |
} | |
/** | |
* Returns <jk>true</jk> if debug mode is enabled. | |
* | |
* Debug mode is enabled by simply adding <js>"?debug=true"</js> to the query string or adding a <c>Debug: true</c> header on the request. | |
* | |
* @return <jk>true</jk> if debug mode is enabled. | |
*/ | |
public boolean isDebug() { | |
return getAttribute("Debug").asType(Boolean.class).orElse(false); | |
} | |
/** | |
* Sets the <js>"Exception"</js> attribute to the specified throwable. | |
* | |
* <p> | |
* This exception is used by {@link BasicRestLogger} for logging purposes. | |
* | |
* @param t The attribute value. | |
* @return This object (for method chaining). | |
*/ | |
public RestRequest setException(Throwable t) { | |
setAttribute("Exception", t); | |
return this; | |
} | |
/** | |
* Sets the <js>"NoLog"</js> attribute to the specified boolean. | |
* | |
* <p> | |
* This flag is used by {@link BasicRestLogger} and tells it not to log the current request. | |
* | |
* @param b The attribute value. | |
* @return This object (for method chaining). | |
*/ | |
public RestRequest setNoLog(Boolean b) { | |
setAttribute("NoLog", b); | |
return this; | |
} | |
/** | |
* Shortcut for calling <c>setNoLog(<jk>true</jk>)</c>. | |
* | |
* @return This object (for method chaining). | |
*/ | |
public RestRequest setNoLog() { | |
return setNoLog(true); | |
} | |
/** | |
* Sets the <js>"Debug"</js> attribute to the specified boolean. | |
* | |
* <p> | |
* This flag is used by {@link BasicRestLogger} to help determine how a request should be logged. | |
* | |
* @param b The attribute value. | |
* @return This object (for method chaining). | |
* @throws IOException If body 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 <c>setDebug(<jk>true</jk>)</c>. | |
* | |
* @return This object (for method chaining). | |
* @throws IOException If body could not be cached. | |
*/ | |
public RestRequest setDebug() throws IOException { | |
return setDebug(true); | |
} | |
/** | |
* Request-level variable resolver session. | |
* | |
* <p> | |
* Used to resolve SVL variables in text. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <ja>@RestGet</ja> | |
* <jk>public</jk> String hello(RestRequest <jv>req</jv>) { | |
* | |
* <jc>// Get var resolver session.</jc> | |
* VarResolverSession <jv>session</jv> = getVarResolverSession(); | |
* | |
* <jc>// Use it to construct a customized message from a query parameter.</jc> | |
* <jk>return</jk> <jv>session</jv>.resolve(<js>"Hello $RQ{user}!"</js>); | |
* } | |
* </p> | |
* | |
* <ul class='notes'> | |
* <li> | |
* The {@link VarResolverSession} object can also be passed as a parameter on the method. | |
* </ul> | |
* | |
* <ul class='seealso'> | |
* <li class='jm'>{@link org.apache.juneau.rest.RestContext#getVarResolver()} | |
* <li class='link'>{@doc RestSvlVariables} | |
* </ul> | |
* | |
* @return The variable resolver for this request. | |
*/ | |
public VarResolverSession getVarResolverSession() { | |
if (varSession == null) | |
varSession = context | |
.getVarResolver() | |
.createSession(call.getBeanStore()) | |
.bean(RestRequest.class, this) | |
.bean(RestCall.class, call); | |
return varSession; | |
} | |
/** | |
* Returns the file finder registered on the REST resource context object. | |
* | |
* <p> | |
* Used to retrieve localized files from the classpath for a variety of purposes. | |
* | |
* <ul class='seealso'> | |
* <li class='jf'>{@link RestContext#REST_fileFinder} | |
* </ul> | |
* | |
* @return The file finder associated with the REST resource object. | |
*/ | |
public FileFinder getFileFinder() { | |
return context.getFileFinder(); | |
} | |
/** | |
* Returns the static files registered on the REST resource context object. | |
* | |
* <p> | |
* Used to retrieve localized files to be served up as static files through the REST API. | |
* | |
* <ul class='seealso'> | |
* <li class='jf'>{@link RestContext#REST_staticFiles} | |
* </ul> | |
* | |
* @return This object (for method chaining). | |
*/ | |
public StaticFiles getStaticFiles() { | |
return context.getStaticFiles(); | |
} | |
/** | |
* Config file associated with the resource. | |
* | |
* <p> | |
* Returns a config file with session-level variable resolution. | |
* | |
* The config file is identified via one of the following: | |
* <ul class='javatree'> | |
* <li class='ja'>{@link Rest#config()} | |
* <li class='jm'>{@link RestContextBuilder#config(Config)} | |
* </ul> | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <ja>@RestGet</ja>(...) | |
* <jk>public void</jk> doGet(RestRequest <jv>req</jv>) { | |
* | |
* <jc>// Get config file.</jc> | |
* Config <jv>config</jv> = <jv>req</jv>.getConfig(); | |
* | |
* <jc>// Get simple values from config file.</jc> | |
* <jk>int</jk> <jv>timeout</jv> = <jv>config</jv>.getInt(<js>"MyResource/timeout"</js>, 10000); | |
* | |
* <jc>// Get complex values from config file.</jc> | |
* MyBean <jv>bean</jv> = <jv>config</jv>.getObject(<js>"MyResource/myBean"</js>, MyBean.<jk>class</jk>); | |
* } | |
* </p> | |
* | |
* <ul class='notes'> | |
* <li> | |
* The {@link Config} object can also be passed as a parameter on the method. | |
* </ul> | |
* | |
* <ul class='seealso'> | |
* <li class='link'>{@doc RestConfigurationFiles} | |
* </ul> | |
* | |
* @return | |
* The config file associated with the resource, or <jk>null</jk> 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. | |
* | |
* <h5 class='section'>Examples:</h5> | |
* <p class='bcode w800'> | |
* <ja>@RestPost</ja>(<js>"/mypath/{p1}/{p2}/*"</js>) | |
* <jk>public void</jk> myMethod(<ja>@Request</ja> MyRequest <jv>requestBean</jv>) {...} | |
* | |
* <jk>public interface</jk> MyRequest { | |
* | |
* <ja>@Path</ja> <jc>// Path variable name inferred from getter.</jc> | |
* String getP1(); | |
* | |
* <ja>@Path</ja>(<js>"p2"</js>) | |
* String getX(); | |
* | |
* <ja>@Path</ja>(<js>"/*"</js>) | |
* String getRemainder(); | |
* | |
* <ja>@Query</ja> | |
* String getQ1(); | |
* | |
* <jc>// Schema-based query parameter: Pipe-delimited lists of comma-delimited lists of integers.</jc> | |
* <ja>@Query</ja>( | |
* collectionFormat=<js>"pipes"</js> | |
* items=<ja>@Items</ja>( | |
* items=<ja>@SubItems</ja>( | |
* collectionFormat=<js>"csv"</js> | |
* type=<js>"integer"</js> | |
* ) | |
* ) | |
* ) | |
* <jk>int</jk>[][] getQ3(); | |
* | |
* <ja>@Header</ja>(<js>"*"</js>) | |
* Map<String,Object> getHeaders(); | |
* </p> | |
* | |
* @param c The request bean interface to instantiate. | |
* @return A new request bean proxy for this REST request. | |
*/ | |
public <T> T getRequest(Class<T> c) { | |
return getRequest(RequestBeanMeta.create(c, getContext().getContextProperties())); | |
} | |
/** | |
* Same as {@link #getRequest(Class)} but used on pre-instantiated {@link RequestBeanMeta} objects. | |
* | |
* @param rbm The metadata about the request bean interface to create. | |
* @return A new request bean proxy for this REST request. | |
*/ | |
public <T> T getRequest(final RequestBeanMeta rbm) { | |
try { | |
Class<T> c = (Class<T>)rbm.getClassMeta().getInnerClass(); | |
final BeanMeta<T> bm = getBeanSession().getBeanMeta(c); | |
return (T)Proxy.newProxyInstance( | |
c.getClassLoader(), | |
new Class[] { c }, | |
new InvocationHandler() { | |
@Override /* InvocationHandler */ | |
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |
RequestBeanPropertyMeta pm = rbm.getProperty(method.getName()); | |
if (pm != null) { | |
HttpPartParserSession pp = pm.getParser(getPartParserSession()); | |
HttpPartSchema schema = pm.getSchema(); | |
String name = pm.getPartName(); | |
ClassMeta<?> type = getContext().getClassMeta(method.getGenericReturnType()); | |
HttpPartType pt = pm.getPartType(); | |
if (pt == HttpPartType.BODY) | |
return getBody().schema(schema).asType(type); | |
if (pt == QUERY) | |
return getQueryParam(name).parser(pp).schema(schema).asType(type).orElse(null); | |
if (pt == FORMDATA) | |
return getFormParam(name).parser(pp).schema(schema).asType(type).orElse(null); | |
if (pt == HEADER) | |
return getHeader(name).parser(pp).schema(schema).asType(type).orElse(null); | |
if (pt == PATH) | |
return getPathParam(name).parser(pp).schema(schema).asType(type).orElse(null); | |
} | |
return null; | |
} | |
}); | |
} catch (Exception e) { | |
throw runtimeException(e); | |
} | |
} | |
/** | |
* Returns the session arguments to pass to serializers. | |
* | |
* @return The session arguments to pass to serializers. | |
*/ | |
public SerializerSessionArgs getSerializerSessionArgs() { | |
return serializerSessionArgs; | |
} | |
/** | |
* Returns the session arguments to pass to parsers. | |
* | |
* @return The session arguments to pass to parsers. | |
*/ | |
public ParserSessionArgs getParserSessionArgs() { | |
return parserSessionArgs; | |
} | |
/* Called by RestCall.finish() */ | |
void close() { | |
if (config != null) { | |
try { | |
config.close(); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
/** | |
* Returns the current session associated with this request, or if the request does not have a session, creates one. | |
* | |
* @return The current request session. | |
* @see HttpServletRequest#getSession() | |
*/ | |
public HttpSession getSession() { | |
return inner.getSession(); | |
} | |
/** | |
* Returns a boolean indicating whether the authenticated user is included in the specified logical "role". | |
* | |
* <p> | |
* Roles and role membership can be defined using deployment descriptors. | |
* If the user has not been authenticated, the method returns false. | |
* | |
* @param role The role name. | |
* @return <jk>true</jk> if the user holds the specified role. | |
* @see HttpServletRequest#isUserInRole(String) | |
*/ | |
public boolean isUserInRole(String role) { | |
return inner.isUserInRole(role); | |
} | |
/** | |
* Returns the wrapped servlet request. | |
* | |
* @return The wrapped servlet request. | |
*/ | |
public HttpServletRequest getHttpServletRequest() { | |
return inner; | |
} | |
@Override /* Object */ | |
public String toString() { | |
StringBuilder sb = new StringBuilder("\n").append(getRequestLine()).append("\n"); | |
sb.append("---Headers---\n"); | |
for (RequestHeader h : getHeaders().getAll()) { | |
sb.append("\t").append(h).append("\n"); | |
} | |
String m = getMethod(); | |
if (m.equals("PUT") || m.equals("POST")) { | |
try { | |
sb.append("---Body UTF-8---\n"); | |
sb.append(body.asString()).append("\n"); | |
sb.append("---Body Hex---\n"); | |
sb.append(body.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); | |
} | |
} |