Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GraphQL Endpoints shadowed by RequestMappings #404

Closed
dawi opened this issue Jun 3, 2022 · 6 comments
Closed

GraphQL Endpoints shadowed by RequestMappings #404

dawi opened this issue Jun 3, 2022 · 6 comments
Assignees
Labels
status: superseded Issue is superseded by another

Comments

@dawi
Copy link

dawi commented Jun 3, 2022

I want to integrate GraphQL into an application that already provides a rest api.

The application contains a RequestMapping that is used as fallback, if no Controller method exists for handling the request:

@Controller
@RequestMapping
public class FallbackController {

    @RequestMapping("/**")
    public void unmappedRequest(HttpServletRequest request) {
        final String uri = request.getRequestURI();
        throw new ResourceNotFoundException("There is no resource for path " + uri);
    }
}

A corresponding ExceptionHandler exists, that converts the ResourceNotFoundException into a 404 JSON ResponseEntity.

Now having this RequestMapping in place, I get a 404 if I call any of the GraphQL Endpoints.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jun 3, 2022
@dawi
Copy link
Author

dawi commented Jun 3, 2022

Side note: 404 is the perfect issue number for this issue. ;)

@rstoyanchev
Copy link
Contributor

Indeed, the GraphQL endpoint is mapped as a RouterFunction and @RequestMapping methods are considered first. We'll consider an improvement to ensure the GraphQL endpoint is more stable for such scenarios, but such a change may be on the Spring Boot starter side.

In the meantime, one option is to create this catch-all handler as a RouterFunction:

@Bean
public RouterFunction<?> routerFunction() {
	return RouterFunctions.route()
			.route(RequestPredicates.all(), request -> ServerResponse.notFound().build())
			.build();
}

Also, if you didn't have it all, you should still get a 404 by default.

@rstoyanchev rstoyanchev added the for: team-attention An issue we need to discuss as a team to make progress label Jun 8, 2022
@bclozel
Copy link
Member

bclozel commented Jun 8, 2022

In my opinion, the Spring Boot auto-configuration is consistent with the Spring MVC opinion here. We're merely using the functional endpoints as a way to contribute controller handlers; this is probably the best choice when it comes to programmatic contributions like this.

Any @RequestMapping("/**") definition is bound to shadow all other defined mappings, including static resources handling.

@dawi
Copy link
Author

dawi commented Jun 8, 2022

@rstoyanchev Thanks for the suggested RouterFunction workaround.

It works, but I have to explicitly ignore /graphql and /graphiql.

@Bean
public RouterFunction<?> routerFunction() {

    return route()
        .route(

            request -> !request.path().startsWith("/graphql") &&
                       !request.path().startsWith("/graphiql"),

            request -> EntityResponse
                .fromObject(
                    new RestError(
                        HttpStatus.NOT_FOUND,
                        "There is no resource for path " + request.uri()
                    ))
                .status(404)
                .build()
        )
        .build();
}

Regarding the following part of your comment:

Also, if you didn't have it all, you should still get a 404 by default.

You are right, that I get a 404 if I don't have a catch-all RequestMapping or RouterFunction.

However this is still not an ideal solution in our case. Our WebApp consists of a UI which is implemented using Apache Wicket and some REST-Endpoints that are implemented with Spring Web MVC. A user that get's a 404 inside the wicket application should get a nice Error-HTML-Page. A WebService API user should get a JSON Response instead.

Currently we are able to provide nice Error Pages via ErrorPageRegistrar:

@Bean
public ErrorPageRegistrar errorPageRegistrar() {
    return errorPageRegistrar -> {
        final ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error.html");
        final ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/notFound.html");
        final ErrorPage errorPage403 = new ErrorPage(HttpStatus.FORBIDDEN, "/forbidden.html");
        ...
        errorPageRegistrar.addErrorPages(errorPage400, errorPage401, errorPage403, errorPage404, errorPage500);
    };
}

If we don't handle the 404 in the WebService part, then all /api requests that are not handled, return a 404-HTML-Page instead of the 404-JSON.

@rstoyanchev
Copy link
Contributor

It works, but I have to explicitly ignore /graphql and /graphiql.

It worked in my case, but it looks like the order is not deterministic. We've discussed this and created related issues spring-projects/spring-framework#28595 and spring-projects/spring-boot#31314 to ensure RouterFunctions are ordered and the one for GraphQL is at 0. Then you'll be able to do:

@Bean
@Order(1)
public RouterFunction<?> routerFunction() {
    // ...
}

Aside from that, @RequestMapping("/**") is indeed something to be avoided, or otherwise if you really want it mapped that, way you'd have to explicitly exclude anything that might come after it.

@rstoyanchev
Copy link
Contributor

Closing for now in favor of the two related issues.

@rstoyanchev rstoyanchev added status: superseded Issue is superseded by another and removed status: waiting-for-triage An issue we've not yet triaged for: team-attention An issue we need to discuss as a team to make progress labels Jun 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: superseded Issue is superseded by another
Projects
None yet
Development

No branches or pull requests

4 participants