简体   繁体   中英

Why Optional and @PathVariable combination is not supported by Spring/java?

I would like to make an api that could accept two Path variables and one of them could be optional. In this article they said that we can achieve this by using Optional, but this is not working.

here is my Controller

@GetMapping("/users/{dateDu}/{dateAu}")
    public ResponseEntity<List<User>> getAllUsersByDate(
        @PathVariable LocalDate dateDu,
        @PathVariable(required = false) Optional<LocalDate> dateAu,
        @org.springdoc.api.annotations.ParameterObject Pageable pageable
    ) {
        if(dateAu.isEmpty()){
            dateAu = Optional.of(LocalDate.now()); 
        }
        Page<User> page = userRepository.getUsersByDate(dateDu, dateAu.get(), pageable);
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
        return ResponseEntity.ok().headers(headers).body(page.getContent());
    }

in this image of swagger the two parameters are required even if the second one has Optional

在此处输入图像描述

Since I generated my spring boot app with JHipster, on postman when I don't give a the value of the Optional parameter. I got this error

<!DOCTYPE html>
<html class="no-js" lang="fr" dir="ltr">

<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title></title>
    <meta name="description" content="Description for " />
    <meta name="google" content="notranslate" />
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
    <meta name="theme-color" content="#000000" />
    <link rel="icon" href="favicon.ico" />
    <link rel="manifest" href="manifest.webapp" />
    <link rel="stylesheet" href="content/css/loading.css" />
    <!-- jhipster-needle-add-resources-to-root - JHipster will add new resources here -->
    <base href="/">
</head>

<body>
    <!--[if lt IE 9]>
      <p class="browserupgrade">
        You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve
        your experience.
      </p>
    <![endif]-->
    <div id="root">
        <div class="app-loading">
            <div class="lds-pacman">
                <div>
                    <div></div>
                    <div></div>
                    <div></div>
                </div>
                <div>
                    <div></div>
                    <div></div>
                    <div></div>
                </div>
            </div>
        </div>
        <div class="app-loading">
            <div id="jhipster-error" style="display: none">
                <!-- This content is for troubleshooting purpose and will be removed when app renders -->
                <h1>An error has occurred :-(</h1>
                <h2>Usual error causes</h2>
                <ol>
                    <li>
                        You started the application from an IDE and you didn't run
                        <code style="color: red">npm start</code> or
                        <code style="color: red">npm run webapp:build</code>.
                    </li>
                    <li>
                        You had a network error while running <code style="color: red">npm install</code>. If you are
                        behind a corporate proxy, it is
                        likely that this error was caused by your proxy. Have a look at the JHipster error logs, you
                        will probably have the cause of
                        the error.
                    </li>
                    <li>
                        You installed a Node.js version that doesn't work with JHipster: please use an LTS (long-term
                        support) version, as it's the
                        only version we support.
                    </li>
                </ol>
                <h2>Building the client side code again</h2>
                <p>If you want to go fast, run <code style="color: red">./mvnw</code> to build and run everything.</p>
                <p>If you want to have more control, so you can debug your issue more easily, you should follow the
                    following steps:</p>
                <ol>
                    <li>Install npm dependencies with the command <code style="color: red">npm install</code></li>
                    <li>
                        Build the client with the command <code style="color: red">npm run webapp:build</code> or
                        <code style="color: red">npm start</code>
                    </li>
                    <li>Start the server with <code style="color: red">./mvnw</code> or using your IDE</li>
                </ol>

                <h2>Getting more help</h2>

                <h3>If you have a question on how to use JHipster</h3>
                <p>
                    Go to Stack Overflow with the
                    <a href="http://stackoverflow.com/tags/jhipster" target="_blank"
                        rel="noopener noreferrer">"jhipster"</a> tag.
                </p>

                <h3>If you have a bug or a feature request</h3>
                <p>
                    First read our
                    <a href="https://github.com/jhipster/generator-jhipster/blob/main/CONTRIBUTING.md" target="_blank"
                        rel="noopener noreferrer">contributing guidelines</a>.
                </p>
                <p>
                    Then, fill a ticket on our
                    <a href="https://github.com/jhipster/generator-jhipster/issues/new/choose" target="_blank"
                        rel="noopener noreferrer">bug tracker</a>, we'll be happy to resolve your issue!
                </p>

                <h3>If you want to chat with contributors and other users</h3>
                <p>
                    Join our chat room on
                    <a href="https://gitter.im/jhipster/generator-jhipster" target="_blank"
                        rel="noopener noreferrer">Gitter.im</a>. Please note
                    that this is a public chat room, and that we expect you to respect other people and write in a
                    correct English language!
                </p>
                <!-- end of troubleshooting content -->
            </div>
        </div>
    </div>
    <noscript>
        <h1>You must enable JavaScript to view this page.</h1>
    </noscript>
    <script type="text/javascript">
        // show an error message if the app loading takes more than 4 sec
      window.onload = function () {
        setTimeout(showError, 4000);
      };
      function showError() {
        var errorElm = document.getElementById('jhipster-error');
        if (errorElm && errorElm.style) {
          errorElm.style.display = 'block';
        }
      }
    </script>
    <!-- uncomment this for adding service worker
    <script>
      if ('serviceWorker' in navigator) {
        window.addEventListener('load', function() {
          navigator.serviceWorker.register('/service-worker.js')
            .then(function () {
              console.log('Service Worker Registered');
            });
        });
      }
    </script>
    -->
    <!-- Google Analytics: uncomment and change UA-XXXXX-X to be your site's ID.
    <script>
      (function(b,o,i,l,e,r){b.GoogleAnalyticsObject=l;b[l]||(b[l]=
      function(){(b[l].q=b[l].q||[]).push(arguments)});b[l].l=+new Date;
      e=o.createElement(i);r=o.getElementsByTagName(i)[0];
      e.src='//www.google-analytics.com/analytics.js';
      r.parentNode.insertBefore(e,r)}(window,document,'script','ga'));
      ga('create','UA-XXXXX-X');ga('send','pageview');
    </script>-->
    <script defer src="app/vendors.bundle.js"></script>
    <script defer src="app/main.bundle.js"></script>
</body>

</html>

I would appreciate if any one could help me to understand why this is happening and/or how I could solve it. Thank you in advance.

This is a composite question. I found some issues in your question.

First, you should change your path to

@GetMapping(value = {"/users/{dateDu}", "/users/{dateDu}/{dateAu}"})

Otherwise you will get 404 error if you don't provide dateAu in your request.


Second, you should add @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) to your parameters to tell Spring how to convert String to LocalDate. Otherwise you will get a Bad Request with error message.

Failed to convert value of type 'java.lang.String' to required type 'java.time.LocalDate'

You didn't notice this issue because Swagger have already handled it for you, I guess.

I try to fix your code:

@GetMapping(value = {"/users/{dateDu}", "/users/{dateDu}/{dateAu}"})
public ResponseEntity<List<User>> getAllUsersByDate(
    @PathVariable @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate dateDu,
    @PathVariable(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Optional<LocalDate> dateAu,
    @org.springdoc.api.annotations.ParameterObject Pageable pageable) {
    if (dateAu.isEmpty()) {
        dateAu = Optional.of(LocalDate.now()); 
    }
    Page<User> page = userRepository.getUsersByDate(dateDu, dateAu.get(), pageable);
    HttpHeaders headers = PaginationUtil
        .generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
    return ResponseEntity.ok().headers(headers).body(page.getContent());
}

I think it should be work if you use postman to test.


Third, according to this post , all of @PathVariable MUST be required in Swagger. It means your question is nothing to do with combination of @PathVariable and Optional in Java. It's a spec/limitation of Swagger. Thus, if you really want to use Swagger to test your API, you have 2 choices:

  1. Split it to 2 endpoints, as @tucuxi said.
  2. Use QueryString style instead.

Last, it seems like you probably don't need an Optional . Optional is designed to be used as a return type in most case. But this is a controversial design issue. So, if you really want to use Optional , you can follow the best practice described by this excellent article

Maybe try adding the 2 paths to the @GetMapping?

@GetMapping("/users/{dateDu}", "/users/{dateDu}/{dateAu}")

不确定这是否能解决您的问题,但您可能应该删除参数注释中的错误标志: @PathVariable Optional<LocalDate> dateAu并添加两个路径: @GetMapping(value = {"/users/{dateDu}", "/users/{dateDu}/{dateAu}"})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM