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