2

We have an eCommerce webapp with the front-end server and the API server being in separate EC2 Ubuntus. The API server exposes 168 API RESTFul endpoints using Spring MVC. Each class has the @RestController annotation. We have a problem with one of them.

When this endpoint (which is a PUT) is called by the front-end, we know that it is invoked in Spring, we see its effects in the database and the PDF file it generates, but then (every single time) it returns 404 to the browser which we see in the tomcat access logs, as well, even though it is invoked every time.. There is nothing extraordinary about this endpoint. All 168 endpoints look the same. They have the HTTP method, produces JSON and return ResponseEntity. I moved this endpoint to another new class, I changed its endpoint URI, I changed the method name, I removed everything from this method except for "return ResponseEntity.ok().body(new SomeObject())", but it always returns 404. Does Spring MVC do this or tomcat itself?

We use OpenJDK 11.0.11+9, tomcat 9.0.59, Spring Framework 5.3.16, Spring Security 5.6.2, javaee-api 8.0.1 and servlet 4.0. Here is the endpoint method:

@RestController
@RequestMapping(value = "/api/v1/drivers")
public class DriverAPIService
{
    @PutMapping(value = "/delivered", produces = "application/json")
    public ResponseEntity<OrderStatusResponseBean> markOrderAsDelivered(@RequestBody OrderStatusUpdateRequestBean requestBean, HttpServletRequest request, HttpServletResponse response, Model model)
    {
        try
        {
             return DriverService.markOrderAsDelivered(requestBean, request);
        }
        catch(Throwable e)
        {
            return ResponseEntity.badRequest().build();
        }
    }
}

Our tomcat config in the server.xml for the connectors is:

<Executor name="tomcatThreadPool" namePrefix="tomcat9-thread-" maxIdleTime="60000"
maxThreads="50" minSpareThreads="5"/>

<Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" redirectPort="8443" maxConnections="100" acceptCount="300"/>

<Connector executor="tomcatThreadPool" SSLEnabled="true" maxPostSize="5000000" port="8443" protocol="org.apache.coyote.http11.Http11Nio2Protocol" scheme="https"
    secure="true" compression="on" maxKeepAliveRequests="10" connectionTimeout="60000"
    maxConnections="100" acceptCount="300" enableLookups="false" connectionUploadTimeout="120000" disableUploadTimeout="false">
    <SSLHostConfig>
       <Certificate certificateFile="conf/cert.pem" certificateKeyFile="conf/privkey.pem" certificateChainFile="conf/chain.pem" />
    </SSLHostConfig>

    <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" compression="on" keepAliveTimeout="360000"/>
</Connector>
6
  • In your method, what is the purpose of the HttpServletResponse and Model arguments? Please, could you outline the process performed by DriverService.markOrderAsDelivered? Does Tomcat report any errors in the logs? Commented Mar 21, 2022 at 22:16
  • the HttpServletResponse and Model arguments are not used in this method. They just exist and Spring MVC always injects them. The markOrderAsDelivered() updates the order status in the database to "DELIVERED" and generates the invoice PDF for that order which gets uploaded to AWS S3. No errors in Tomcat. The operation completes from start to finish. This endpoint always gets invoked, but tomcat returns 404 Commented Mar 22, 2022 at 10:09
  • Can you add the DriverService.markOrderAsDelivered(requestBean, request); method to check what is being done there? Also do you have anything configured differently for that endpoint in WebSecurityConfigurerAdapter? Commented Mar 22, 2022 at 15:44
  • the markOrderAsDelivered() just updates the database record for the order and generates the invoice PDF using the com.spire.doc and iText libraries. We use the same libraries on other endpoints. That code is about 450 lines spanning 14 classes. I cannot just copy/paste it here. I even removed all the contents and just returned 200 (OK). I just kept 1 line in there, but it still returns 404 Commented Mar 22, 2022 at 16:55
  • Thank you for the feedback @DimitriosEfthymiou. You mentioned in your comments that even with one line the error is still there. Please, could you indicate how does the object you are returning look like? Does it work if you just return ResponseEntity.ok().build()? My guess is that it should be some error when returning your data, perhaps when dealing with serialization, and it is causing a redirection to some error page/route, which actually does not exists? If you inspect your requests in the browser inspector, is there any valuable information? Commented Mar 22, 2022 at 22:38

1 Answer 1

0
+50

Let's recapitulate what we have:

  • Your service code is wrapped into a catch-all block, that will transform any inner exceptions into an http-400 response. Catch-alls are a code smell, but in this case it helps to exclude the possibility that the problem is deep in your service.
  • The service code is executed (as you say), so the method lookup generally works.

IMHO the problem must be in the response processing. I suspect the HttpServletResponse to be the problem here. There are only rare cases when it's really needed (streaming, cookies, etc.), but generally it causes more problems than it actually solves. So please remove any unneeded parameters from the method signature and try again.

Sign up to request clarification or add additional context in comments.

6 Comments

unfortunately it still returns 404 even though it executes
Is the HttpServletRequest somehow manipulated in your service code? Do you really need the complete object? Can't you just hand in single parameters or headers?
Yes. The HttpServletRequest is manipulated. I have a JWT filter and during that filter I save in the request object the userID (extracted from that JWT) using request.setAttribute("current_user_ID", tokenData.getUserID()); Then every API endpoint just calls getUserID(request) to extract that userID using request.getAttribute("current_user_ID");
Hmm, this sounds unsuspicious. Ok, one last try. Let's further reduce the response processing: Can you please change the method to return a void (=HTTP 200 without body) and try again? And maybe change the signature and set the HttpServletRequest to be the first parameter?
no. Still the same. WOW!!!!!!!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.