Here's a minimal reproducible example of, what I believe is, a bug in Spring Boot 3.4.0.

I have a Spring Boot web app with a controller that looks like this:

@RestController
@RequestMapping("/sftp")
public class SftpController {
    @GetMapping
    public ResponseEntity<Void> find(SftpSearchRequest searchRequest) {
        System.out.println(searchRequest.getHost());
        return ResponseEntity.ok().build();
    }
}

where SftpSearchRequest looks like this:

public class SftpSearchRequest {
    private String id;
    private String host;

    public String getId() { return id; }

    public void setId(String id) { this.id = id; }

    public String getHost() { return host; }

    public void setHost(String host) { this.host = host; }
}

I have the app running on port 8088 and I am sending a request that looks like this: GET http://localhost:8088/sftp?id=123. - When using Spring Boot 3.3.5, "null" is printed to the console - When using Spring Boot 3.4.0, "localhost:8088" is printed to the console

I believe Spring Boot attempts to following SemVer - and this appears to be a breaking change - which is why I believe this may be a bug, especially since I haven't found this behaviour change mentioned in release notes.

Comment From: ngocnhan-tran1996

@ioanclarke

This change mentioned in here

https://github.com/spring-projects/spring-framework/releases/tag/v6.2.0-M4

Support data binding from request headers https://github.com/spring-projects/spring-framework/issues/32676

Comment From: ioanclarke

@ioanclarke

This change mentioned in here

https://github.com/spring-projects/spring-framework/releases/tag/v6.2.0-M4

Support data binding from request headers https://github.com/spring-projects/spring-framework/issues/32676

Thank you for that - great find! Any ideas for how to use the old behaviour here, except by changing the name of thehost field?

Comment From: quaff

I think data binding from request headers should be disabled by default, enabled if field is marked as @RequestHeader.

Comment From: bclozel

@ioanclarke you can try the following: https://github.com/spring-projects/spring-framework/issues/34125#issuecomment-2556616884

This issue is almost a duplicate of #34125, but we could consider it as a request to disable this feature by default. We'll discuss this as a team.

Comment From: bclozel

We have discussed this as a team and we came to the following conclusion:

  • we understand the disruption here and we will extend the list of header names filtered by default; "Priority" was added in #34039 and we should extend that list with more well-known header names
  • in addition to this change, we think that the addHeaderPredicate and setHeaderPredicate should go a long way for uncommon issues. The general binding feature has been out there for a while and we're extending it by popular demand. Let's see if we get more feedback about this after the next release.

I'm repurposing this issue to extend the list of header names filtered by default, adding "Host" and more.

Comment From: bclozel

Right now I'm leaning towards selecting the following headers to be ignored by default because they are: * common in HTTP requests * not likely to be bound on purpose * simple words probably used as class attributes:

"Accept", "Authorization", "Connection", "Cookie", "From", "Host", "Origin", "Priority", "Range", "Referer", "Upgrade"

We can refine this list if we get further feedback from the community.

Applications can still opt back in by replacing the default predicate with your own:

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder;

@ControllerAdvice
public class MyControllerAdvice {

    @InitBinder
    public void initBinder(ExtendedServletRequestDataBinder binder) {
        binder.setHeaderPredicate(header -> ...);
    }
}

Comment From: nastharl

Following up from a related thread: I suspect that 6.2.3 will fix the issue i know about (origin header vs origin param), but i dont know if there are other places in my company where someones custom header happens to collide with some model value, and will start having erroneous information without us knowing it. As a feature its perfectly fine for it to work this way, it just feels like it sprung (heh) up out of nowhere for us. Hopefully nothing breaks?

Comment From: bclozel

@nastharl please give a try to the latest 6.2.3 SNAPSHOTs and let us know if we need to refine things more.

Comment From: nastharl

Will do :) Thanks again for the quick fixes.

Comment From: apoorvam

@bclozel Credentials is another request header that would be good to exclude.

Comment From: bclozel

@apoorvam Unlikely. This doesn't look like a well-known header so you'll have to configure the filtering for your application.