001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.rest; 014 015import static org.apache.juneau.common.internal.IOUtils.*; 016import static org.apache.juneau.common.internal.ThrowableUtils.*; 017import static org.apache.juneau.httppart.HttpPartType.*; 018import static org.apache.juneau.internal.CollectionUtils.*; 019import static java.lang.Integer.*; 020import static java.util.Optional.*; 021 022import java.io.*; 023import java.lang.reflect.*; 024import java.lang.reflect.Proxy; 025import java.net.*; 026import java.nio.charset.*; 027import java.text.*; 028import java.util.*; 029import jakarta.servlet.*; 030import jakarta.servlet.http.*; 031 032import org.apache.http.*; 033import org.apache.http.message.*; 034import org.apache.juneau.*; 035import org.apache.juneau.assertions.*; 036import org.apache.juneau.common.internal.*; 037import org.apache.juneau.config.*; 038import org.apache.juneau.cp.Messages; 039import org.apache.juneau.dto.swagger.*; 040import org.apache.juneau.dto.swagger.Swagger; 041import org.apache.juneau.http.annotation.Content; 042import org.apache.juneau.http.annotation.FormData; 043import org.apache.juneau.http.annotation.Header; 044import org.apache.juneau.httppart.*; 045import org.apache.juneau.httppart.bean.*; 046import org.apache.juneau.rest.annotation.*; 047import org.apache.juneau.rest.assertions.*; 048import org.apache.juneau.rest.guard.*; 049import org.apache.juneau.rest.httppart.*; 050import org.apache.juneau.rest.logger.*; 051import org.apache.juneau.http.header.*; 052import org.apache.juneau.http.header.Date; 053import org.apache.juneau.http.response.*; 054import org.apache.juneau.http.response.BasicHttpException; 055import org.apache.juneau.rest.staticfile.*; 056import org.apache.juneau.rest.swagger.*; 057import org.apache.juneau.rest.util.*; 058import org.apache.juneau.svl.*; 059import org.apache.juneau.uon.*; 060 061/** 062 * Represents an HTTP request for a REST resource. 063 * 064 * <p> 065 * The {@link RestRequest} object is an extension of the <l>HttpServletRequest</l> class 066 * with various built-in convenience methods for use in building REST interfaces. 067 * It can be accessed by passing it as a parameter on your REST Java method: 068 * </p> 069 * 070 * <p class='bjava'> 071 * <ja>@RestPost</ja>(...) 072 * <jk>public</jk> Object myMethod(RestRequest <jv>req</jv>) {...} 073 * </p> 074 * 075 * <p> 076 * The primary methods on this class are: 077 * </p> 078 * <ul class='javatree'> 079 * <li class='jc'>{@link RestRequest} 080 * <ul class='spaced-list'> 081 * <li>Methods for accessing the request content: 082 * <ul class='javatreec'> 083 * <li class='jm'>{@link RestRequest#getContent() getContent()} 084 * <li class='jm'>{@link RestRequest#getInputStream() getInputStream()} 085 * <li class='jm'>{@link RestRequest#getReader() getReader()} 086 * </ul> 087 * <li>Methods for accessing HTTP parts: 088 * <ul class='javatreec'> 089 * <li class='jm'>{@link RestRequest#containsFormParam(String) containsFormParam(String)} 090 * <li class='jm'>{@link RestRequest#containsHeader(String) containsHeader(String)} 091 * <li class='jm'>{@link RestRequest#containsQueryParam(String) containsQueryParam(String)} 092 * <li class='jm'>{@link RestRequest#getHeader(Class) getHeader(Class)} 093 * <li class='jm'>{@link RestRequest#getHeader(String) getHeader(String)} 094 * <li class='jm'>{@link RestRequest#getHeaders() getHeaders()} 095 * <li class='jm'>{@link RestRequest#getFormParam(Class) getFormParam(Class)} 096 * <li class='jm'>{@link RestRequest#getFormParam(String) getFormParam(String)} 097 * <li class='jm'>{@link RestRequest#getFormParams() getFormParams()} 098 * <li class='jm'>{@link RestRequest#getPathParam(Class) getPathParam(Class)} 099 * <li class='jm'>{@link RestRequest#getPathParam(String) getPathParam(String)} 100 * <li class='jm'>{@link RestRequest#getPathParams() getPathParams()} 101 * <li class='jm'>{@link RestRequest#getPathRemainder() getPathRemainder()} 102 * <li class='jm'>{@link RestRequest#getQueryParam(Class) getQueryParam(Class)} 103 * <li class='jm'>{@link RestRequest#getQueryParam(String) getQueryParam(String)} 104 * <li class='jm'>{@link RestRequest#getQueryParams() getQueryParams()} 105 * <li class='jm'>{@link RestRequest#getQueryString() getQueryString()} 106 * </ul> 107 * <li>Methods for localization: 108 * <ul class='javatreec'> 109 * <li class='jm'>{@link RestRequest#getLocale() getLocale()} 110 * <li class='jm'>{@link RestRequest#getMessage(String,Object...) getMessage(String,Object...)} 111 * <li class='jm'>{@link RestRequest#getMessages() getMessages()} 112 * <li class='jm'>{@link RestRequest#getTimeZone() getTimeZone()} 113 * </ul> 114 * <li>Methods for accessing static files: 115 * <ul class='javatreec'> 116 * <li class='jm'>{@link RestRequest#getStaticFiles() getStaticFiles()} 117 * <li class='jm'>{@link RestRequest#getVarResolverSession() getVarResolverSession()} 118 * </ul> 119 * <li>Methods for assertions: 120 * <ul class='javatreec'> 121 * <li class='jm'>{@link RestRequest#assertContent() assertContent()} 122 * <li class='jm'>{@link RestRequest#assertCharset() assertCharset()} 123 * <li class='jm'>{@link RestRequest#assertFormParam(String) assertFormParam(String)} 124 * <li class='jm'>{@link RestRequest#assertHeader(String) assertHeader(String)} 125 * <li class='jm'>{@link RestRequest#assertQueryParam(String) assertQueryParam(String)} 126 * <li class='jm'>{@link RestRequest#assertRequestLine() assertRequestLine()} 127 * </ul> 128 * <li>Other: 129 * <ul class='javatreec'> 130 * <li class='jm'>{@link RestRequest#getAttribute(String) getAttribute(String)} 131 * <li class='jm'>{@link RestRequest#getAttributes() getAttributes()} 132 * <li class='jm'>{@link RestRequest#getAuthorityPath() getAuthorityPath()} 133 * <li class='jm'>{@link RestRequest#getBeanSession() getBeanSession()} 134 * <li class='jm'>{@link RestRequest#getCharset() getCharset()} 135 * <li class='jm'>{@link RestRequest#getConfig() getConfig()} 136 * <li class='jm'>{@link RestRequest#getContext() getContext()} 137 * <li class='jm'>{@link RestRequest#getContextPath() getContextPath()} 138 * <li class='jm'>{@link RestRequest#getHttpServletRequest() getHttpServletRequest()} 139 * <li class='jm'>{@link RestRequest#getMethod() getMethod()} 140 * <li class='jm'>{@link RestRequest#getOpContext() getOpContext()} 141 * <li class='jm'>{@link RestRequest#getOperationSwagger() getOperationSwagger()} 142 * <li class='jm'>{@link RestRequest#getPartParserSession() getPartParserSession()} 143 * <li class='jm'>{@link RestRequest#getPartSerializerSession() getPartSerializerSession()} 144 * <li class='jm'>{@link RestRequest#getPathInfo() getPathInfo()} 145 * <li class='jm'>{@link RestRequest#getProtocolVersion() getProtocolVersion()} 146 * <li class='jm'>{@link RestRequest#getRequest(Class) getRequest(Class)} 147 * <li class='jm'>{@link RestRequest#getRequestLine() getRequestLine()} 148 * <li class='jm'>{@link RestRequest#getRequestURI() getRequestURI()} 149 * <li class='jm'>{@link RestRequest#getRequestURL() getRequestURL()} 150 * <li class='jm'>{@link RestRequest#getServletPath() getServletPath()} 151 * <li class='jm'>{@link RestRequest#getSession() getSession()} 152 * <li class='jm'>{@link RestRequest#getSwagger() getSwagger()} 153 * <li class='jm'>{@link RestRequest#getUriContext() getUriContext()} 154 * <li class='jm'>{@link RestRequest#getUriResolver() getUriResolver()} 155 * <li class='jm'>{@link RestRequest#isDebug() isDebug()} 156 * <li class='jm'>{@link RestRequest#isPlainText() isPlainText()} 157 * <li class='jm'>{@link RestRequest#isUserInRole(String) isUserInRole(String)} 158 * <li class='jm'>{@link RestRequest#setAttribute(String,Object) setAttribute(String,Object)} 159 * <li class='jm'>{@link RestRequest#setCharset(Charset) setCharset(Charset)} 160 * <li class='jm'>{@link RestRequest#setDebug() setDebug()} 161 * <li class='jm'>{@link RestRequest#setException(Throwable) setException(Throwable)} 162 * <li class='jm'>{@link RestRequest#setNoTrace() setNoTrace()} 163 * </ul> 164 * </ul> 165 * </ul> 166 * 167 * <h5 class='section'>See Also:</h5><ul> 168 169 * </ul> 170 */ 171@SuppressWarnings({ "unchecked", "unused" }) 172public final class RestRequest extends HttpServletRequestWrapper { 173 174 // Constructor initialized. 175 private HttpServletRequest inner; 176 private final RestContext context; 177 private final RestOpContext opContext; 178 private final RequestContent content; 179 private final BeanSession beanSession; 180 private final RequestQueryParams queryParams; 181 private final RequestPathParams pathParams; 182 private final RequestHeaders headers; 183 private final RequestAttributes attrs; 184 private final HttpPartParserSession partParserSession; 185 private final RestSession session; 186 187 // Lazy initialized. 188 private VarResolverSession varSession; 189 private RequestFormParams formParams; 190 private UriContext uriContext; 191 private String authorityPath; 192 private Config config; 193 private Swagger swagger; 194 private Charset charset; 195 196 /** 197 * Constructor. 198 */ 199 RestRequest(RestOpContext opContext, RestSession session) throws Exception { 200 super(session.getRequest()); 201 this.session = session; 202 this.opContext = opContext; 203 204 inner = session.getRequest(); 205 context = session.getContext(); 206 207 attrs = new RequestAttributes(this); 208 209 queryParams = new RequestQueryParams(this, session.getQueryParams(), true); 210 211 headers = new RequestHeaders(this, queryParams, false); 212 213 content = new RequestContent(this); 214 215 if (context.isAllowContentParam()) { 216 String b = queryParams.get("content").asString().orElse(null); 217 if (b != null) { 218 headers.set("Content-Type", UonSerializer.DEFAULT.getResponseContentType()); 219 content.mediaType(MediaType.UON).parser(UonParser.DEFAULT).content(b.getBytes(UTF8)); 220 } 221 } 222 223 pathParams = new RequestPathParams(session, this, true); 224 225 beanSession = opContext.getBeanContext().getSession(); 226 227 partParserSession = opContext.getPartParser().getPartSession(); 228 229 pathParams.parser(partParserSession); 230 231 queryParams 232 .addDefault(opContext.getDefaultRequestQueryData().getAll()) 233 .parser(partParserSession); 234 235 headers 236 .addDefault(opContext.getDefaultRequestHeaders().getAll()) 237 .addDefault(context.getDefaultRequestHeaders().getAll()) 238 .parser(partParserSession); 239 240 content 241 .encoders(opContext.getEncoders()) 242 .parsers(opContext.getParsers()) 243 .maxInput(opContext.getMaxInput()); 244 245 attrs 246 .addDefault(opContext.getDefaultRequestAttributes()) 247 .addDefault(context.getDefaultRequestAttributes()); 248 249 if (isDebug()) 250 inner = CachingHttpServletRequest.wrap(inner); 251 } 252 253 //----------------------------------------------------------------------------------------------------------------- 254 // Request line. 255 //----------------------------------------------------------------------------------------------------------------- 256 257 /** 258 * Returns the request line of this request. 259 * 260 * @return The request line of this request. 261 */ 262 public RequestLine getRequestLine() { 263 String x = inner.getProtocol(); 264 int i = x.indexOf('/'); 265 int j = x.indexOf('.', i); 266 ProtocolVersion pv = new ProtocolVersion(x.substring(0,i), parseInt(x.substring(i+1,j)), parseInt(x.substring(j+1))); 267 return new BasicRequestLine(inner.getMethod(), inner.getRequestURI(), pv); 268 } 269 270 /** 271 * Returns the protocol version from the request line of this request. 272 * 273 * @return The protocol version from the request line of this request. 274 */ 275 public ProtocolVersion getProtocolVersion() { 276 return getRequestLine().getProtocolVersion(); 277 } 278 279 //----------------------------------------------------------------------------------------------------------------- 280 // Assertions 281 //----------------------------------------------------------------------------------------------------------------- 282 283 /** 284 * Returns an assertion on the request line returned by {@link #getRequestLine()}. 285 * 286 * <h5 class='section'>Example:</h5> 287 * <p class='bjava'> 288 * <jc>// Validates the request content contains "foo".</jc> 289 * <jv>request</jv> 290 * .assertRequestLine().protocol().minor().is(1); 291 * </p> 292 * 293 * @return A new assertion object. 294 */ 295 public FluentRequestLineAssertion<RestRequest> assertRequestLine() { 296 return new FluentRequestLineAssertion<>(getRequestLine(), this); 297 } 298 299 /** 300 * Returns a fluent assertion for the request content. 301 * 302 * <h5 class='section'>Example:</h5> 303 * <p class='bjava'> 304 * <jc>// Validates the request content contains "foo".</jc> 305 * <jv>request</jv> 306 * .assertContent().asString().is(<js>"foo"</js>); 307 * </p> 308 * 309 * @return A new fluent assertion on the content, never <jk>null</jk>. 310 */ 311 public FluentRequestContentAssertion<RestRequest> assertContent() { 312 return new FluentRequestContentAssertion<>(getContent(), this); 313 } 314 315 /** 316 * Returns a fluent assertion for the specified header. 317 * 318 * <h5 class='section'>Example:</h5> 319 * <p class='bjava'> 320 * <jc>// Validates the content type is JSON.</jc> 321 * <jv>request</jv> 322 * .assertHeader(<js>"Content-Type"</js>).asString().is(<js>"application/json"</js>); 323 * </p> 324 * 325 * @param name The header name. 326 * @return A new fluent assertion on the parameter, never <jk>null</jk>. 327 */ 328 public FluentRequestHeaderAssertion<RestRequest> assertHeader(String name) { 329 return new FluentRequestHeaderAssertion<>(getHeaderParam(name), this); 330 } 331 332 /** 333 * Returns a fluent assertion for the specified query parameter. 334 * 335 * <h5 class='section'>Example:</h5> 336 * <p class='bjava'> 337 * <jc>// Validates the content type is JSON.</jc> 338 * <jv>request</jv> 339 * .assertQueryParam(<js>"foo"</js>).asString().contains(<js>"bar"</js>); 340 * </p> 341 * 342 * @param name The query parameter name. 343 * @return A new fluent assertion on the parameter, never <jk>null</jk>. 344 */ 345 public FluentRequestQueryParamAssertion<RestRequest> assertQueryParam(String name) { 346 return new FluentRequestQueryParamAssertion<>(getQueryParam(name), this); 347 } 348 349 350 /** 351 * Returns a fluent assertion for the specified form parameter. 352 * 353 * <h5 class='section'>Example:</h5> 354 * <p class='bjava'> 355 * <jc>// Validates the content type is JSON.</jc> 356 * <jv>request</jv> 357 * .assertFormParam(<js>"foo"</js>).asString().contains(<js>"bar"</js>); 358 * </p> 359 * 360 * @param name The query parameter name. 361 * @return A new fluent assertion on the parameter, never <jk>null</jk>. 362 */ 363 public FluentRequestFormParamAssertion<RestRequest> assertFormParam(String name) { 364 return new FluentRequestFormParamAssertion<>(getFormParam(name), this); 365 } 366 367 //----------------------------------------------------------------------------------------------------------------- 368 // Headers 369 //----------------------------------------------------------------------------------------------------------------- 370 371 /** 372 * Request headers. 373 * 374 * <p> 375 * Returns a {@link RequestHeaders} object that encapsulates access to HTTP headers on the request. 376 * 377 * <h5 class='section'>Example:</h5> 378 * <p class='bjava'> 379 * <ja>@RestPost</ja>(...) 380 * <jk>public</jk> Object myMethod(RestRequest <jv>req</jv>) { 381 * 382 * <jc>// Get access to headers.</jc> 383 * RequestHeaders <jv>headers</jv> = <jv>req</jv>.getRequestHeaders(); 384 * 385 * <jc>// Add a default value.</jc> 386 * <jv>headers</jv>.addDefault(<js>"ETag"</js>, <jsf>DEFAULT_UUID</jsf>); 387 * 388 * <jc>// Get a header value as a POJO.</jc> 389 * UUID etag = <jv>headers</jv>.get(<js>"ETag"</js>).as(UUID.<jk>class</jk>).orElse(<jk>null</jk>); 390 * 391 * <jc>// Get a standard header.</jc> 392 * Optional<CacheControl> = <jv>headers</jv>.getCacheControl(); 393 * } 394 * </p> 395 * 396 * <h5 class='section'>Notes:</h5><ul> 397 * <li class='note'> 398 * This object is modifiable. 399 * <li class='note'> 400 * Values are converted from strings using the registered part parser on the resource class. 401 * <li class='note'> 402 * The {@link RequestHeaders} object can also be passed as a parameter on the method. 403 * <li class='note'> 404 * The {@link Header @Header} annotation can be used to access individual header values. 405 * </ul> 406 * 407 * <h5 class='section'>See Also:</h5><ul> 408 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.HttpParts">HTTP Parts</a> 409 * </ul> 410 * 411 * @return 412 * The headers on this request. 413 * <br>Never <jk>null</jk>. 414 */ 415 public RequestHeaders getHeaders() { 416 return headers; 417 } 418 419 /** 420 * Returns the last header with a specified name of this message. 421 * 422 * <p> 423 * If there is more than one matching header in the message the last element of <c>getHeaders(String)</c> is returned. 424 * <br>If there is no matching header in the message, an empty request header object is returned. 425 * 426 * <h5 class='section'>Example:</h5> 427 * <p class='bjava'> 428 * <jc>// Gets a header and throws a BadRequest if it doesn't exist.</jc> 429 * <jv>request</jv> 430 * .getHeader(<js>"Foo"</js>) 431 * .assertValue().exists() 432 * .get(); 433 * </p> 434 * 435 * @param name The header name. 436 * @return The request header object, never <jk>null</jk>. 437 */ 438 public RequestHeader getHeaderParam(String name) { 439 return headers.getLast(name); 440 } 441 442 /** 443 * Returns <jk>true</jk> if this request contains the specified header. 444 * 445 * @param name The header name. 446 * @return <jk>true</jk> if this request contains the specified header. 447 */ 448 public boolean containsHeader(String name) { 449 return headers.contains(name); 450 } 451 452 /** 453 * Provides the ability to perform fluent-style assertions on the response character encoding. 454 * 455 * <h5 class='section'>Examples:</h5> 456 * <p class='bjava'> 457 * <jc>// Validates that the response content charset is UTF-8.</jc> 458 * <jv>request</jv> 459 * .assertCharset().is(<js>"utf-8"</js>); 460 * </p> 461 * 462 * @return A new fluent assertion object. 463 * @throws BasicHttpException If REST call failed. 464 */ 465 public FluentStringAssertion<RestRequest> assertCharset() { 466 return new FluentStringAssertion<>(getCharset().name(), this); 467 } 468 469 /** 470 * Sets the charset to expect on the request content. 471 * 472 * @param value The new value to use for the request content. 473 */ 474 public void setCharset(Charset value) { 475 this.charset = value; 476 } 477 478 /** 479 * Returns the charset specified on the <c>Content-Type</c> header, or <js>"UTF-8"</js> if not specified. 480 * 481 * @return The charset to use to decode the request content. 482 */ 483 public Charset getCharset() { 484 if (charset == null) { 485 // Determine charset 486 // NOTE: Don't use super.getCharacterEncoding() because the spec is implemented inconsistently. 487 // Jetty returns the default charset instead of null if the character is not specified on the request. 488 String h = getHeaderParam("Content-Type").orElse(null); 489 if (h != null) { 490 int i = h.indexOf(";charset="); 491 if (i > 0) 492 charset = Charset.forName(h.substring(i+9).trim()); 493 } 494 if (charset == null) 495 charset = opContext.getDefaultCharset(); 496 if (charset == null) 497 charset = Charset.forName("UTF-8"); 498 } 499 return charset; 500 } 501 502 /** 503 * Returns the preferred Locale that the client will accept content in, based on the Accept-Language header. 504 * 505 * <p> 506 * If the client request doesn't provide an <c>Accept-Language</c> header, this method returns the default locale for the server. 507 * 508 * @return The preferred Locale that the client will accept content in. Never <jk>null</jk>. 509 */ 510 @Override 511 public Locale getLocale() { 512 Locale best = inner.getLocale(); 513 String h = headers.get("Accept-Language").asString().orElse(null); 514 if (h != null) { 515 StringRanges sr = StringRanges.of(h); 516 float qValue = 0; 517 for (StringRange r : sr.toList()) { 518 if (r.getQValue() > qValue) { 519 best = toLocale(r.getName()); 520 qValue = r.getQValue(); 521 } 522 } 523 } 524 return best; 525 } 526 527 //----------------------------------------------------------------------------------------------------------------- 528 // Standard headers. 529 //----------------------------------------------------------------------------------------------------------------- 530 531 /** 532 * Returns the request header of the specified type. 533 * 534 * <p> 535 * Type must have a name specified via the {@link org.apache.juneau.http.annotation.Header} annotation 536 * and a public constructor that takes in either <c>value</c> or <c>name,value</c> as strings. 537 * 538 * <p> 539 * Typically any of the following: 540 * <ul class='javatreec'> 541 * <li class='jc'>{@link Accept} 542 * <li class='jc'>{@link AcceptCharset} 543 * <li class='jc'>{@link AcceptEncoding} 544 * <li class='jc'>{@link AcceptLanguage} 545 * <li class='jc'>{@link AcceptRanges} 546 * <li class='jc'>{@link Authorization} 547 * <li class='jc'>{@link CacheControl} 548 * <li class='jc'>{@link ClientVersion} 549 * <li class='jc'>{@link Connection} 550 * <li class='jc'>{@link ContentDisposition} 551 * <li class='jc'>{@link ContentEncoding} 552 * <li class='jc'>{@link ContentLength} 553 * <li class='jc'>{@link ContentType} 554 * <li class='jc'>{@link Date} 555 * <li class='jc'>{@link Debug} 556 * <li class='jc'>{@link Expect} 557 * <li class='jc'>{@link Forwarded} 558 * <li class='jc'>{@link From} 559 * <li class='jc'>{@link Host} 560 * <li class='jc'>{@link IfMatch} 561 * <li class='jc'>{@link IfModifiedSince} 562 * <li class='jc'>{@link IfNoneMatch} 563 * <li class='jc'>{@link IfRange} 564 * <li class='jc'>{@link IfUnmodifiedSince} 565 * <li class='jc'>{@link MaxForwards} 566 * <li class='jc'>{@link NoTrace} 567 * <li class='jc'>{@link Origin} 568 * <li class='jc'>{@link Pragma} 569 * <li class='jc'>{@link ProxyAuthorization} 570 * <li class='jc'>{@link Range} 571 * <li class='jc'>{@link Referer} 572 * <li class='jc'>{@link TE} 573 * <li class='jc'>{@link Thrown} 574 * <li class='jc'>{@link Upgrade} 575 * <li class='jc'>{@link UserAgent} 576 * <li class='jc'>{@link Warning} 577 * </ul> 578 * 579 * @param <T> The bean type to create. 580 * @param type The bean type to create. 581 * @return The parsed header on the request, never <jk>null</jk>. 582 */ 583 public <T> Optional<T> getHeader(Class<T> type) { 584 return headers.get(type); 585 } 586 587 /** 588 * Returns the <c>Time-Zone</c> header value on the request if there is one. 589 * 590 * <p> 591 * Example: <js>"GMT"</js>. 592 * 593 * @return The parsed header on the request, never <jk>null</jk>. 594 */ 595 public Optional<TimeZone> getTimeZone() { 596 String tz = headers.get("Time-Zone").asString().orElse(null); 597 if (tz != null) 598 return optional(TimeZone.getTimeZone(tz)); 599 return Optional.empty(); 600 } 601 602 //----------------------------------------------------------------------------------------------------------------- 603 // Attributes 604 //----------------------------------------------------------------------------------------------------------------- 605 606 /** 607 * Request attributes. 608 * 609 * <p> 610 * Returns a {@link RequestAttributes} object that encapsulates access to attributes on the request. 611 * 612 * <h5 class='section'>Example:</h5> 613 * <p class='bjava'> 614 * <ja>@RestPost</ja>(...) 615 * <jk>public</jk> Object myMethod(RestRequest <jv>req</jv>) { 616 * 617 * <jc>// Get access to attributes.</jc> 618 * RequestAttributes <jv>attributes</jv> = <jv>req</jv>.getAttributes(); 619 * 620 * <jc>// Get a header value as a POJO.</jc> 621 * UUID <jv>etag</jv> = <jv>attributes</jv>.get(<js>"ETag"</js>, UUID.<jk>class</jk>); 622 * } 623 * </p> 624 * 625 * <h5 class='section'>Notes:</h5><ul> 626 * <li class='note'> 627 * This object is modifiable. 628 * <li class='note'> 629 * Values are converted from strings using the registered part parser on the resource class. 630 * <li class='note'> 631 * The {@link RequestAttributes} object can also be passed as a parameter on the method. 632 * <li class='note'> 633 * The {@link Attr @Attr} annotation can be used to access individual attribute values. 634 * </ul> 635 * 636 * <h5 class='section'>See Also:</h5><ul> 637 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.HttpParts">HTTP Parts</a> 638 * </ul> 639 * 640 * @return 641 * The headers on this request. 642 * <br>Never <jk>null</jk>. 643 */ 644 public RequestAttributes getAttributes() { 645 return attrs; 646 } 647 648 /** 649 * Returns the request attribute with the specified name. 650 * 651 * @param name The attribute name. 652 * @return The attribute value, never <jk>null</jk>. 653 */ 654 @Override 655 public RequestAttribute getAttribute(String name) { 656 return attrs.get(name); 657 } 658 659 /** 660 * Sets a request attribute. 661 * 662 * @param name The attribute name. 663 * @param value The attribute value. 664 */ 665 @Override 666 public void setAttribute(String name, Object value) { 667 attrs.set(name, value); 668 } 669 670 //----------------------------------------------------------------------------------------------------------------- 671 // Query parameters 672 //----------------------------------------------------------------------------------------------------------------- 673 674 /** 675 * Query parameters. 676 * 677 * <p> 678 * Returns a {@link RequestQueryParams} object that encapsulates access to URL GET parameters. 679 * 680 * <p> 681 * Similar to {@link HttpServletRequest#getParameterMap()} but only looks for query parameters in the URL and not form posts. 682 * 683 * <h5 class='section'>Example:</h5> 684 * <p class='bjava'> 685 * <ja>@RestGet</ja>(...) 686 * <jk>public void</jk> doGet(RestRequest <jv>req</jv>) { 687 * 688 * <jc>// Get access to query parameters on the URL.</jc> 689 * RequestQueryParams <jv>query</jv> = <jv>req</jv>.getQuery(); 690 * 691 * <jc>// Get query parameters converted to various types.</jc> 692 * <jk>int</jk> <jv>p1/</jv> = <jv>query</jv>.getInteger(<js>"p1"</js>).orElse(<jk>null</jk>); 693 * String <jv>p2</jv> = <jv>query</jv>.getString(<js>"p2"</js>).orElse(<jk>null</jk>); 694 * UUID <jv>p3</jv> = <jv>query</jv>.get(<js>"p3"</js>).as(UUID.<jk>class</jk>).orElse(<jk>null</jk>); 695 * } 696 * </p> 697 * 698 * <h5 class='section'>Notes:</h5><ul> 699 * <li class='note'> 700 * This object is modifiable. 701 * </ul> 702 * 703 * <h5 class='section'>See Also:</h5><ul> 704 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.HttpParts">HTTP Parts</a> 705 * </ul> 706 * 707 * @return 708 * The query parameters as a modifiable map. 709 * <br>Never <jk>null</jk>. 710 */ 711 public RequestQueryParams getQueryParams() { 712 return queryParams; 713 } 714 715 /** 716 * Shortcut for calling <c>getRequestQuery().getLast(<jv>name</jv>)</c>. 717 * 718 * @param name The query parameter name. 719 * @return The query parameter, never <jk>null</jk>. 720 */ 721 public RequestQueryParam getQueryParam(String name) { 722 return queryParams.get(name); 723 } 724 725 /** 726 * Returns the request query parameter of the specified type. 727 * 728 * <p> 729 * Type must have a name specified via the {@link org.apache.juneau.http.annotation.Query} annotation 730 * and a public constructor that takes in either <c>value</c> or <c>name,value</c> as strings. 731 * 732 * @param <T> The bean type to create. 733 * @param type The bean type to create. 734 * @return The parsed query parameter on the request, never <jk>null</jk>. 735 */ 736 public <T> Optional<T> getQueryParam(Class<T> type) { 737 return queryParams.get(type); 738 } 739 740 /** 741 * Returns <jk>true</jk> if this request contains the specified header. 742 * 743 * @param name The header name. 744 * @return <jk>true</jk> if this request contains the specified header. 745 */ 746 public boolean containsQueryParam(String name) { 747 return queryParams.contains(name); 748 } 749 750 751 //----------------------------------------------------------------------------------------------------------------- 752 // Form data parameters 753 //----------------------------------------------------------------------------------------------------------------- 754 755 /** 756 * Form-data. 757 * 758 * <p> 759 * Returns a {@link RequestFormParams} object that encapsulates access to form post parameters. 760 * 761 * <p> 762 * Similar to {@link HttpServletRequest#getParameterMap()}, but only looks for form data in the HTTP content. 763 * 764 * <h5 class='section'>Example:</h5> 765 * <p class='bjava'> 766 * <ja>@RestPost</ja>(...) 767 * <jk>public void</jk> doPost(RestRequest <jv>req</jv>) { 768 * 769 * <jc>// Get access to parsed form data parameters.</jc> 770 * RequestFormParams <jv>formParams</jv> = <jv>req</jv>.getFormParams(); 771 * 772 * <jc>// Get form data parameters converted to various types.</jc> 773 * <jk>int</jk> <jv>p1</jv> = <jv>formParams</jv>.get(<js>"p1"</js>).asInteger().orElse(0); 774 * String <jv>p2</jv> = <jv>formParams</jv>.get(<js>"p2"</js>).asString().orElse(<jk>null</jk>); 775 * UUID <jv>p3</jv> = <jv>formParams</jv>.get(<js>"p3"</js>).as(UUID.<jk>class</jk>).orElse(<jk>null</jk>); 776 * } 777 * </p> 778 * 779 * <h5 class='section'>Notes:</h5><ul> 780 * <li class='note'> 781 * This object is modifiable. 782 * <li class='note'> 783 * Values are converted from strings using the registered part parser on the resource class. 784 * <li class='note'> 785 * The {@link RequestFormParams} object can also be passed as a parameter on the method. 786 * <li class='note'> 787 * The {@link FormData @FormDAta} annotation can be used to access individual form data parameter values. 788 * </ul> 789 * 790 * <h5 class='section'>See Also:</h5><ul> 791 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.HttpParts">HTTP Parts</a> 792 * </ul> 793 * 794 * @return 795 * The URL-encoded form data from the request. 796 * <br>Never <jk>null</jk>. 797 * @throws InternalServerError If query parameters could not be parsed. 798 * @see org.apache.juneau.http.annotation.FormData 799 */ 800 public RequestFormParams getFormParams() throws InternalServerError { 801 try { 802 if (formParams == null) 803 formParams = new RequestFormParams(this, true).parser(partParserSession); 804 formParams.addDefault(opContext.getDefaultRequestFormData().getAll()); 805 return formParams; 806 } catch (Exception e) { 807 throw new InternalServerError(e); 808 } 809 } 810 811 /** 812 * Shortcut for calling <c>getFormData().getString(name)</c>. 813 * 814 * @param name The form data parameter name. 815 * @return The form data parameter value, or <jk>null</jk> if not found. 816 */ 817 public RequestFormParam getFormParam(String name) { 818 return getFormParams().get(name); 819 } 820 821 /** 822 * Returns the request form-data parameter of the specified type. 823 * 824 * <p> 825 * Type must have a name specified via the {@link org.apache.juneau.http.annotation.FormData} annotation 826 * and a public constructor that takes in either <c>value</c> or <c>name,value</c> as strings. 827 * 828 * @param <T> The bean type to create. 829 * @param type The bean type to create. 830 * @return The parsed form-data parameter on the request, never <jk>null</jk>. 831 */ 832 public <T> Optional<T> getFormParam(Class<T> type) { 833 return getFormParams().get(type); 834 } 835 836 /** 837 * Returns <jk>true</jk> if this request contains the specified header. 838 * 839 * @param name The header name. 840 * @return <jk>true</jk> if this request contains the specified header. 841 */ 842 public boolean containsFormParam(String name) { 843 return getFormParams().contains(name); 844 } 845 846 //----------------------------------------------------------------------------------------------------------------- 847 // Path parameters 848 //----------------------------------------------------------------------------------------------------------------- 849 850 /** 851 * Path parameters. 852 * 853 * <p> 854 * Returns a {@link RequestPathParams} object that encapsulates access to URL path parameters. 855 * 856 * <h5 class='section'>Example:</h5> 857 * <p class='bjava'> 858 * <ja>@RestGet</ja>(<js>"/{foo}/{bar}/{baz}/*"</js>) 859 * <jk>public void</jk> doGet(RestRequest <jv>req</jv>) { 860 * 861 * <jc>// Get access to path data.</jc> 862 * RequestPathParams <jv>pathParams</jv> = <jv>req</jv>.getPathParams(); 863 * 864 * <jc>// Example URL: /123/qux/true/quux</jc> 865 * 866 * <jk>int</jk> <jv>foo</jv> = <jv>pathParams</jv>.get(<js>"foo"</js>).asInteger().orElse(-1); <jc>// =123</jc> 867 * String <jv>bar</jv> = <jv>pathParams</jv>.get(<js>"bar"</js>).orElse(<jk>null</jk>); <jc>// =qux</jc> 868 * <jk>boolean</jk> <jv>baz</jv> = <jv>pathParams</jv>.get(<js>"baz"</js>).asBoolean().orElse(<jk>false</jk>); <jc>// =true</jc> 869 * String <jv>remainder</jv> = <jv>pathParams</jv>.getRemainder().orElse(<jk>null</jk>); <jc>// =quux</jc> 870 * } 871 * </p> 872 * 873 * <h5 class='section'>Notes:</h5><ul> 874 * <li class='note'> 875 * This object is modifiable. 876 * </ul> 877 * 878 * @return 879 * The path parameters. 880 * <br>Never <jk>null</jk>. 881 */ 882 public RequestPathParams getPathParams() { 883 return pathParams; 884 } 885 886 /** 887 * Shortcut for calling <c>getPathParams().get(<jv>name</jv>)</c>. 888 * 889 * @param name The path parameter name. 890 * @return The path parameter, never <jk>null</jk>. 891 */ 892 public RequestPathParam getPathParam(String name) { 893 return pathParams.get(name); 894 } 895 896 /** 897 * Returns the request path parameter of the specified type. 898 * 899 * <p> 900 * Type must have a name specified via the {@link org.apache.juneau.http.annotation.Path} annotation 901 * and a public constructor that takes in either <c>value</c> or <c>name,value</c> as strings. 902 * 903 * @param <T> The bean type to create. 904 * @param type The bean type to create. 905 * @return The parsed form-data parameter on the request, never <jk>null</jk>. 906 */ 907 public <T> Optional<T> getPathParam(Class<T> type) { 908 return pathParams.get(type); 909 } 910 911 /** 912 * Shortcut for calling <c>getPathParams().getRemainder()</c>. 913 * 914 * @return The path remainder value, never <jk>null</jk>. 915 */ 916 public RequestPathParam getPathRemainder() { 917 return pathParams.getRemainder(); 918 } 919 920 //----------------------------------------------------------------------------------------------------------------- 921 // Content methods 922 //----------------------------------------------------------------------------------------------------------------- 923 924 /** 925 * Request content. 926 * 927 * <p> 928 * Returns a {@link RequestContent} object that encapsulates access to the HTTP request content. 929 * 930 * <h5 class='section'>Example:</h5> 931 * <p class='bjava'> 932 * <ja>@RestPost</ja>(...) 933 * <jk>public void</jk> doPost(RestRequest <jv>req</jv>) { 934 * 935 * <jc>// Convert content to a linked list of Person objects.</jc> 936 * List<Person> <jv>list</jv> = <jv>req</jv>.getContent().as(LinkedList.<jk>class</jk>, Person.<jk>class</jk>); 937 * .. 938 * } 939 * </p> 940 * 941 * <h5 class='section'>Notes:</h5><ul> 942 * <li class='note'> 943 * The {@link RequestContent} object can also be passed as a parameter on the method. 944 * <li class='note'> 945 * The {@link Content @Content} annotation can be used to access the content as well. 946 * </ul> 947 * 948 * <h5 class='section'>See Also:</h5><ul> 949 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.HttpParts">HTTP Parts</a> 950 * </ul> 951 * 952 * @return 953 * The content of this HTTP request. 954 * <br>Never <jk>null</jk>. 955 */ 956 public RequestContent getContent() { 957 return content; 958 } 959 960 /** 961 * Returns the HTTP content content as a {@link Reader}. 962 * 963 * <p> 964 * If {@code allowHeaderParams} init parameter is true, then first looks for {@code &content=xxx} in the URL query 965 * string. 966 * 967 * <p> 968 * Automatically handles GZipped input streams. 969 * 970 * <p> 971 * This method is equivalent to calling <c>getContent().getReader()</c>. 972 * 973 * @return The HTTP content content as a {@link Reader}. 974 * @throws IOException If content could not be read. 975 */ 976 @Override 977 public BufferedReader getReader() throws IOException { 978 return getContent().getReader(); 979 } 980 981 /** 982 * Returns the HTTP content content as an {@link InputStream}. 983 * 984 * <p> 985 * Automatically handles GZipped input streams. 986 * 987 * <p> 988 * This method is equivalent to calling <c>getContent().getInputStream()</c>. 989 * 990 * @return The negotiated input stream. 991 * @throws IOException If any error occurred while trying to get the input stream or wrap it in the GZIP wrapper. 992 */ 993 @Override 994 public ServletInputStream getInputStream() throws IOException { 995 return getContent().getInputStream(); 996 } 997 998 //----------------------------------------------------------------------------------------------------------------- 999 // URI-related methods 1000 //----------------------------------------------------------------------------------------------------------------- 1001 1002 /** 1003 * Returns the portion of the request URI that indicates the context of the request. 1004 * 1005 * <p>The context path always comes first in a request URI. 1006 * The path starts with a <js>"/"</js> character but does not end with a <js>"/"</js> character. 1007 * For servlets in the default (root) context, this method returns <js>""</js>. 1008 * The container does not decode this string. 1009 * 1010 * @return The context path, never <jk>null</jk>. 1011 * @see HttpServletRequest#getContextPath() 1012 */ 1013 @Override 1014 public String getContextPath() { 1015 String cp = context.getUriContext(); 1016 return cp == null ? inner.getContextPath() : cp; 1017 } 1018 1019 /** 1020 * Returns the URI authority portion of the request. 1021 * 1022 * @return The URI authority portion of the request. 1023 */ 1024 public String getAuthorityPath() { 1025 if (authorityPath == null) 1026 authorityPath = context.getUriAuthority(); 1027 if (authorityPath == null) { 1028 String scheme = inner.getScheme(); 1029 int port = inner.getServerPort(); 1030 StringBuilder sb = new StringBuilder(inner.getScheme()).append("://").append(inner.getServerName()); 1031 if (! (port == 80 && "http".equals(scheme) || port == 443 && "https".equals(scheme))) 1032 sb.append(':').append(port); 1033 authorityPath = sb.toString(); 1034 } 1035 return authorityPath; 1036 } 1037 1038 /** 1039 * Returns the part of this request's URL that calls the servlet. 1040 * 1041 * <p> 1042 * This path starts with a <js>"/"</js> character and includes either the servlet name or a path to the servlet, 1043 * but does not include any extra path information or a query string. 1044 * 1045 * @return The servlet path, never <jk>null</jk>. 1046 * @see HttpServletRequest#getServletPath() 1047 */ 1048 @Override 1049 public String getServletPath() { 1050 String cp = context.getUriContext(); 1051 String sp = inner.getServletPath(); 1052 return cp == null || ! sp.startsWith(cp) ? sp : sp.substring(cp.length()); 1053 } 1054 1055 /** 1056 * Returns the URI context of the request. 1057 * 1058 * <p> 1059 * The URI context contains all the information about the URI of the request, such as the servlet URI, context 1060 * path, etc... 1061 * 1062 * @return The URI context of the request. 1063 */ 1064 public UriContext getUriContext() { 1065 if (uriContext == null) 1066 uriContext = UriContext.of(getAuthorityPath(), getContextPath(), getServletPath(), StringUtils.urlEncodePath(inner.getPathInfo())); 1067 return uriContext; 1068 } 1069 1070 /** 1071 * Returns a URI resolver that can be used to convert URIs to absolute or root-relative form. 1072 * 1073 * @param resolution The URI resolution rule. 1074 * @param relativity The relative URI relativity rule. 1075 * @return The URI resolver for this request. 1076 */ 1077 public UriResolver getUriResolver(UriResolution resolution, UriRelativity relativity) { 1078 return UriResolver.of(resolution, relativity, getUriContext()); 1079 } 1080 1081 /** 1082 * Shortcut for calling {@link #getUriResolver()} using {@link UriResolution#ROOT_RELATIVE} and 1083 * {@link UriRelativity#RESOURCE} 1084 * 1085 * @return The URI resolver for this request. 1086 */ 1087 public UriResolver getUriResolver() { 1088 return UriResolver.of(context.getUriResolution(), context.getUriRelativity(), getUriContext()); 1089 } 1090 1091 /** 1092 * Returns the URI for this request. 1093 * 1094 * <p> 1095 * Similar to {@link #getRequestURI()} but returns the value as a {@link URI}. 1096 * It also gives you the capability to override the query parameters (e.g. add new query parameters to the existing 1097 * URI). 1098 * 1099 * @param includeQuery If <jk>true</jk> include the query parameters on the request. 1100 * @param addQueryParams Augment the request URI with the specified query parameters. 1101 * @return A new URI. 1102 */ 1103 public URI getUri(boolean includeQuery, Map<String,Object> addQueryParams) { 1104 String uri = inner.getRequestURI(); 1105 if (includeQuery || addQueryParams != null) { 1106 StringBuilder sb = new StringBuilder(uri); 1107 RequestQueryParams rq = this.queryParams.copy(); 1108 if (addQueryParams != null) 1109 for (Map.Entry<String,?> e : addQueryParams.entrySet()) 1110 rq.set(e.getKey(), e.getValue()); 1111 if (! rq.isEmpty()) 1112 sb.append('?').append(rq.asQueryString()); 1113 uri = sb.toString(); 1114 } 1115 try { 1116 return new URI(uri); 1117 } catch (URISyntaxException e) { 1118 // Shouldn't happen. 1119 throw asRuntimeException(e); 1120 } 1121 } 1122 1123 //----------------------------------------------------------------------------------------------------------------- 1124 // Labels 1125 //----------------------------------------------------------------------------------------------------------------- 1126 1127 /** 1128 * Returns the localized swagger associated with the resource. 1129 * 1130 * <p> 1131 * A shortcut for calling <c>getInfoProvider().getSwagger(request);</c> 1132 * 1133 * <h5 class='section'>Example:</h5> 1134 * <p class='bjava'> 1135 * <ja>@RestGet</ja> 1136 * <jk>public</jk> List<Tag> swaggerTags(RestRequest <jv>req</jv>) { 1137 * <jk>return</jk> <jv>req</jv>.getSwagger().getTags(); 1138 * } 1139 * </p> 1140 * 1141 * <h5 class='section'>Notes:</h5><ul> 1142 * <li class='note'> 1143 * The {@link Swagger} object can also be passed as a parameter on the method. 1144 * </ul> 1145 * 1146 * <h5 class='section'>See Also:</h5><ul> 1147 * <li class='jm'>{@link RestContext.Builder#swaggerProvider(Class)} 1148 * <li class='jm'>{@link RestContext.Builder#swaggerProvider(SwaggerProvider)} 1149 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.Swagger">Swagger</a> 1150 * </ul> 1151 * 1152 * @return 1153 * The swagger associated with the resource. 1154 * <br>Never <jk>null</jk>. 1155 */ 1156 public Optional<Swagger> getSwagger() { 1157 return context.getSwagger(getLocale()); 1158 } 1159 1160 /** 1161 * Returns the swagger for the Java method invoked. 1162 * 1163 * @return The swagger for the Java method as an {@link Optional}. Never <jk>null</jk>. 1164 */ 1165 public Optional<Operation> getOperationSwagger() { 1166 1167 Optional<Swagger> swagger = getSwagger(); 1168 if (! swagger.isPresent()) 1169 return Optional.empty(); 1170 1171 return ofNullable(swagger.get().getOperation(opContext.getPathPattern(), getMethod().toLowerCase())); 1172 } 1173 1174 //----------------------------------------------------------------------------------------------------------------- 1175 // Other methods 1176 //----------------------------------------------------------------------------------------------------------------- 1177 1178 /** 1179 * Returns the part serializer associated with this request. 1180 * 1181 * @return The part serializer associated with this request. 1182 */ 1183 public HttpPartParserSession getPartParserSession() { 1184 return partParserSession; 1185 } 1186 1187 /** 1188 * Returns the HTTP method of this request. 1189 * 1190 * <p> 1191 * If <c>allowHeaderParams</c> init parameter is <jk>true</jk>, then first looks for 1192 * <c>&method=xxx</c> in the URL query string. 1193 * 1194 * @return The HTTP method of this request. 1195 */ 1196 @Override 1197 public String getMethod() { 1198 return session.getMethod(); 1199 } 1200 1201 /** 1202 * Returns <jk>true</jk> if <c>&plainText=true</c> was specified as a URL parameter. 1203 * 1204 * <p> 1205 * This indicates that the <c>Content-Type</c> of the output should always be set to <js>"text/plain"</js> 1206 * to make it easy to render in a browser. 1207 * 1208 * <p> 1209 * This feature is useful for debugging. 1210 * 1211 * @return <jk>true</jk> if {@code &plainText=true} was specified as a URL parameter 1212 */ 1213 public boolean isPlainText() { 1214 return "true".equals(queryParams.get("plainText").asString().orElse("false")); 1215 } 1216 1217 /** 1218 * Returns the resource bundle for the request locale. 1219 * 1220 * <h5 class='section'>Example:</h5> 1221 * <p class='bjava'> 1222 * <ja>@RestGet</ja> 1223 * <jk>public</jk> String hello(RestRequest <jv>req</jv>, <ja>@Query</ja>(<js>"user"</js>) String <jv>user</jv>) { 1224 * 1225 * <jc>// Return a localized message.</jc> 1226 * <jk>return</jk> <jv>req</jv>.getMessages().getString(<js>"hello.message"</js>, <jv>user</jv>); 1227 * } 1228 * </p> 1229 * 1230 * <h5 class='section'>Notes:</h5><ul> 1231 * <li class='note'> 1232 * The {@link Messages} object can also be passed as a parameter on the method. 1233 * </ul> 1234 * 1235 * <h5 class='section'>See Also:</h5><ul> 1236 * <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getMessage(String,Object...)} 1237 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.LocalizedMessages">Localized Messages</a> 1238 * </ul> 1239 * 1240 * @return 1241 * The resource bundle. 1242 * <br>Never <jk>null</jk>. 1243 */ 1244 public Messages getMessages() { 1245 return context.getMessages().forLocale(getLocale()); 1246 } 1247 1248 /** 1249 * Shortcut method for calling {@link RestRequest#getMessages()} and {@link Messages#getString(String,Object...)}. 1250 * 1251 * @param key The message key. 1252 * @param args Optional {@link MessageFormat}-style arguments. 1253 * @return The localized message. 1254 */ 1255 public String getMessage(String key, Object...args) { 1256 return getMessages().getString(key, args); 1257 } 1258 1259 /** 1260 * Returns the resource context handling the request. 1261 * 1262 * <p> 1263 * Can be used to access servlet-init parameters or annotations during requests, such as in calls to 1264 * {@link RestGuard#guard(RestRequest, RestResponse)}.. 1265 * 1266 * @return The resource context handling the request. 1267 */ 1268 public RestContext getContext() { 1269 return context; 1270 } 1271 1272 /** 1273 * Returns access to the inner {@link RestOpContext} of this method. 1274 * 1275 * @return The {@link RestOpContext} of this method. May be <jk>null</jk> if method has not yet been found. 1276 */ 1277 public RestOpContext getOpContext() { 1278 return opContext; 1279 } 1280 1281 /** 1282 * Returns the {@link BeanSession} associated with this request. 1283 * 1284 * @return The request bean session. 1285 */ 1286 public BeanSession getBeanSession() { 1287 return beanSession; 1288 } 1289 1290 /** 1291 * Returns <jk>true</jk> if debug mode is enabled. 1292 * 1293 * 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. 1294 * 1295 * @return <jk>true</jk> if debug mode is enabled. 1296 */ 1297 public boolean isDebug() { 1298 return getAttribute("Debug").as(Boolean.class).orElse(false); 1299 } 1300 1301 /** 1302 * Sets the <js>"Exception"</js> attribute to the specified throwable. 1303 * 1304 * <p> 1305 * This exception is used by {@link CallLogger} for logging purposes. 1306 * 1307 * @param t The attribute value. 1308 * @return This object. 1309 */ 1310 public RestRequest setException(Throwable t) { 1311 setAttribute("Exception", t); 1312 return this; 1313 } 1314 1315 /** 1316 * Sets the <js>"NoTrace"</js> attribute to the specified boolean. 1317 * 1318 * <p> 1319 * This flag is used by {@link CallLogger} and tells it not to log the current request. 1320 * 1321 * @param b The attribute value. 1322 * @return This object. 1323 */ 1324 public RestRequest setNoTrace(Boolean b) { 1325 setAttribute("NoTrace", b); 1326 return this; 1327 } 1328 1329 /** 1330 * Shortcut for calling <c>setNoTrace(<jk>true</jk>)</c>. 1331 * 1332 * @return This object. 1333 */ 1334 public RestRequest setNoTrace() { 1335 return setNoTrace(true); 1336 } 1337 1338 /** 1339 * Sets the <js>"Debug"</js> attribute to the specified boolean. 1340 * 1341 * <p> 1342 * This flag is used by {@link CallLogger} to help determine how a request should be logged. 1343 * 1344 * @param b The attribute value. 1345 * @return This object. 1346 * @throws IOException If content could not be cached. 1347 */ 1348 public RestRequest setDebug(Boolean b) throws IOException { 1349 setAttribute("Debug", b); 1350 if (b) 1351 inner = CachingHttpServletRequest.wrap(inner); 1352 return this; 1353 } 1354 1355 /** 1356 * Shortcut for calling <c>setDebug(<jk>true</jk>)</c>. 1357 * 1358 * @return This object. 1359 * @throws IOException If content could not be cached. 1360 */ 1361 public RestRequest setDebug() throws IOException { 1362 return setDebug(true); 1363 } 1364 1365 /** 1366 * Request-level variable resolver session. 1367 * 1368 * <p> 1369 * Used to resolve SVL variables in text. 1370 * 1371 * <h5 class='section'>Example:</h5> 1372 * <p class='bjava'> 1373 * <ja>@RestGet</ja> 1374 * <jk>public</jk> String hello(RestRequest <jv>req</jv>) { 1375 * 1376 * <jc>// Get var resolver session.</jc> 1377 * VarResolverSession <jv>session</jv> = getVarResolverSession(); 1378 * 1379 * <jc>// Use it to construct a customized message from a query parameter.</jc> 1380 * <jk>return</jk> <jv>session</jv>.resolve(<js>"Hello $RQ{user}!"</js>); 1381 * } 1382 * </p> 1383 * 1384 * <h5 class='section'>Notes:</h5><ul> 1385 * <li class='note'> 1386 * The {@link VarResolverSession} object can also be passed as a parameter on the method. 1387 * </ul> 1388 * 1389 * <h5 class='section'>See Also:</h5><ul> 1390 * <li class='jm'>{@link org.apache.juneau.rest.RestContext#getVarResolver()} 1391 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.SvlVariables">SVL Variables</a> 1392 * </ul> 1393 * 1394 * @return The variable resolver for this request. 1395 */ 1396 public VarResolverSession getVarResolverSession() { 1397 if (varSession == null) 1398 varSession = context 1399 .getVarResolver() 1400 .createSession(session.getBeanStore()) 1401 .bean(RestRequest.class, this) 1402 .bean(RestSession.class, session); 1403 return varSession; 1404 } 1405 1406 /** 1407 * Returns the static files registered on the REST resource context object. 1408 * 1409 * <p> 1410 * Used to retrieve localized files to be served up as static files through the REST API. 1411 * 1412 * @return This object. 1413 */ 1414 public StaticFiles getStaticFiles() { 1415 return context.getStaticFiles(); 1416 } 1417 1418 /** 1419 * Config file associated with the resource. 1420 * 1421 * <p> 1422 * Returns a config file with session-level variable resolution. 1423 * 1424 * The config file is identified via one of the following: 1425 * <ul class='javatree'> 1426 * <li class='ja'>{@link Rest#config()} 1427 * <li class='jm'>{@link RestContext.Builder#config(Config)} 1428 * </ul> 1429 * 1430 * <h5 class='section'>Example:</h5> 1431 * <p class='bjava'> 1432 * <ja>@RestGet</ja>(...) 1433 * <jk>public void</jk> doGet(RestRequest <jv>req</jv>) { 1434 * 1435 * <jc>// Get config file.</jc> 1436 * Config <jv>config</jv> = <jv>req</jv>.getConfig(); 1437 * 1438 * <jc>// Get simple values from config file.</jc> 1439 * <jk>int</jk> <jv>timeout</jv> = <jv>config</jv>.get(<js>"MyResource/timeout"</js>).asInteger().orElse(=10000); 1440 * 1441 * <jc>// Get complex values from config file.</jc> 1442 * MyBean <jv>bean</jv> = <jv>config</jv>.get(<js>"MyResource/myBean"</js>).as(MyBean.<jk>class</jk>).orElse(<jk>null</jk>); 1443 * } 1444 * </p> 1445 * 1446 * <h5 class='section'>Notes:</h5><ul> 1447 * <li class='note'> 1448 * The {@link Config} object can also be passed as a parameter on the method. 1449 * </ul> 1450 * 1451 * <h5 class='section'>See Also:</h5><ul> 1452 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.ConfigurationFiles">Configuration Files</a> 1453 * </ul> 1454 * 1455 * @return 1456 * The config file associated with the resource, or <jk>null</jk> if resource does not have a config file 1457 * associated with it. 1458 */ 1459 public Config getConfig() { 1460 if (config == null) 1461 config = context.getConfig().resolving(getVarResolverSession()); 1462 return config; 1463 } 1464 1465 /** 1466 * Creates a proxy interface to retrieve HTTP parts of this request as a proxy bean. 1467 * 1468 * <h5 class='section'>Examples:</h5> 1469 * <p class='bjava'> 1470 * <ja>@RestPost</ja>(<js>"/mypath/{p1}/{p2}/*"</js>) 1471 * <jk>public void</jk> myMethod(<ja>@Request</ja> MyRequest <jv>requestBean</jv>) {...} 1472 * 1473 * <jk>public interface</jk> MyRequest { 1474 * 1475 * <ja>@Path</ja> <jc>// Path variable name inferred from getter.</jc> 1476 * String getP1(); 1477 * 1478 * <ja>@Path</ja>(<js>"p2"</js>) 1479 * String getX(); 1480 * 1481 * <ja>@Path</ja>(<js>"/*"</js>) 1482 * String getRemainder(); 1483 * 1484 * <ja>@Query</ja> 1485 * String getQ1(); 1486 * 1487 * <jc>// Schema-based query parameter: Pipe-delimited lists of comma-delimited lists of integers.</jc> 1488 * <ja>@Query</ja>( 1489 * collectionFormat=<js>"pipes"</js> 1490 * items=<ja>@Items</ja>( 1491 * items=<ja>@SubItems</ja>( 1492 * collectionFormat=<js>"csv"</js> 1493 * type=<js>"integer"</js> 1494 * ) 1495 * ) 1496 * ) 1497 * <jk>int</jk>[][] getQ3(); 1498 * 1499 * <ja>@Header</ja>(<js>"*"</js>) 1500 * Map<String,Object> getHeaders(); 1501 * </p> 1502 * 1503 * @param <T> The request bean interface to instantiate. 1504 * @param c The request bean interface to instantiate. 1505 * @return A new request bean proxy for this REST request. 1506 */ 1507 public <T> T getRequest(Class<T> c) { 1508 return getRequest(RequestBeanMeta.create(c, getContext().getAnnotations())); 1509 } 1510 1511 /** 1512 * Same as {@link #getRequest(Class)} but used on pre-instantiated {@link RequestBeanMeta} objects. 1513 * 1514 * @param <T> The request bean interface to instantiate. 1515 * @param rbm The metadata about the request bean interface to create. 1516 * @return A new request bean proxy for this REST request. 1517 */ 1518 public <T> T getRequest(final RequestBeanMeta rbm) { 1519 try { 1520 Class<T> c = (Class<T>)rbm.getClassMeta().getInnerClass(); 1521 final BeanSession bs = getBeanSession(); 1522 final BeanMeta<T> bm = bs.getBeanMeta(c); 1523 return (T)Proxy.newProxyInstance( 1524 c.getClassLoader(), 1525 new Class[] { c }, 1526 (InvocationHandler) (proxy, method, args) -> { 1527 RequestBeanPropertyMeta pm = rbm.getProperty(method.getName()); 1528 if (pm != null) { 1529 HttpPartParserSession pp = pm.getParser(getPartParserSession()); 1530 HttpPartSchema schema = pm.getSchema(); 1531 String name = pm.getPartName(); 1532 ClassMeta<?> type = bs.getClassMeta(method.getGenericReturnType()); 1533 HttpPartType pt = pm.getPartType(); 1534 if (pt == HttpPartType.BODY) 1535 return getContent().setSchema(schema).as(type); 1536 if (pt == QUERY) 1537 return getQueryParam(name).parser(pp).schema(schema).as(type).orElse(null); 1538 if (pt == FORMDATA) 1539 return getFormParam(name).parser(pp).schema(schema).as(type).orElse(null); 1540 if (pt == HEADER) 1541 return getHeaderParam(name).parser(pp).schema(schema).as(type).orElse(null); 1542 if (pt == PATH) 1543 return getPathParam(name).parser(pp).schema(schema).as(type).orElse(null); 1544 } 1545 return null; 1546 }); 1547 } catch (Exception e) { 1548 throw asRuntimeException(e); 1549 } 1550 } 1551 1552 /* Called by RestSession.finish() */ 1553 void close() { 1554 if (config != null) { 1555 try { 1556 config.close(); 1557 } catch (IOException e) { 1558 e.printStackTrace(); 1559 } 1560 } 1561 } 1562 1563 /** 1564 * Returns the wrapped servlet request. 1565 * 1566 * @return The wrapped servlet request. 1567 */ 1568 public HttpServletRequest getHttpServletRequest() { 1569 return inner; 1570 } 1571 1572 /** 1573 * Returns the part serializer session for this request. 1574 * 1575 * @return The part serializer session for this request. 1576 */ 1577 public HttpPartSerializerSession getPartSerializerSession() { 1578 return opContext.getPartSerializer().getPartSession(); 1579 } 1580 1581 @Override /* Object */ 1582 public String toString() { 1583 StringBuilder sb = new StringBuilder("\n").append(getRequestLine()).append("\n"); 1584 sb.append("---Headers---\n"); 1585 getHeaders().forEach(x -> sb.append("\t").append(x).append("\n")); 1586 String m = getMethod(); 1587 if (m.equals("PUT") || m.equals("POST")) { 1588 try { 1589 sb.append("---Content UTF-8---\n"); 1590 sb.append(content.asString()).append("\n"); 1591 sb.append("---Content Hex---\n"); 1592 sb.append(content.asSpacedHex()).append("\n"); 1593 } catch (Exception e1) { 1594 sb.append(e1.getLocalizedMessage()); 1595 } 1596 } 1597 return sb.toString(); 1598 } 1599 1600 //----------------------------------------------------------------------------------------------------------------- 1601 // Utility methods 1602 //----------------------------------------------------------------------------------------------------------------- 1603 1604 /* 1605 * Converts an Accept-Language value entry to a Locale. 1606 */ 1607 private static Locale toLocale(String lang) { 1608 String country = ""; 1609 int i = lang.indexOf('-'); 1610 if (i > -1) { 1611 country = lang.substring(i+1).trim(); 1612 lang = lang.substring(0,i).trim(); 1613 } 1614 return new Locale(lang, country); 1615 } 1616}