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