spring Ausing double-slashes in URLs - spring

I'm trying migration spring 4.0.7.RELEASE mvc project to webFlux,but old project definition url /getData/api/test,use /getData//api/test can request success,but in webFlux whether use Controller or FunctionType it`s response 404 notFound。How can I make it compatible old spring?
useController
#PostMapping("/getData/api/test")
public Object getData(#RequestBody Person person) {
log.info("received");
return "<html>hello</html>";
}
use function
#Bean
public RouterFunction<ServerResponse> timeRouter() {
return route().POST("/getData/api/test", timeHandler::getCurrentTime)
.build();
}

Related

Spring webflux Multiple Exception handler in functional Endpoint

im working ins Spring web flux project and I used functional endpoints instead of controller annotation but I didn't find a solution to handle multiple exceptions for the same endpoint , this is my code :
#Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.GET("/router/users/{id}"),this::renderException);
}
private Mono<ServerResponse> renderException(ServerRequest request) {
Map<String, Object> error = this.getErrorAttributes(request, ErrorAttributeOptions.defaults());
error.remove("status");
error.remove("requestId");
return ServerResponse.status(HttpStatus.BAD_REQUEST).contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(error));
}
for the endpoint /router/users/{id} i trigger UserNotFoundException and UserException and I want to return 404 for UserNotFoundException and 500 for UserException but I don't know how to do that in the functional endpoint. anyone can guide me on how to do this in the correct way like we did in using #ExceptionHandler in rest controller?
If returning proper code is all you care about then adding #ResponseStatus for your custom exceptions might be the best solution.
#ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException {
// more methods...
public UserNotFoundException(final String message) {
super(message);
}
}
But if you want to build ServerResponse by yourself, make use of project reactor operators, like switchIfEmpty() or onErrorMap(), etc. which could be used as following
Mono<ServerResponse> response() {
return exampleService.getUser()
.flatMap(user -> ServerResponse.ok().body(user, User.class))
.switchIfEmpty(ServerResponse.notFound().build());
}
You might want to take a look at docs Which operator do I need? - handling errors

Keep same URL but contract changes in Spring Boot REST Open API 3?

I am using Spring Boot and REST and Open API 3 implementation. In this example, v1 Group has List implementation - all data will get in List, in v2 Group has pagination implementation - all data will come in the form of pages.
For the consumer, we don't want to change endpoint url for them to be consume.
Endpoint which returns list.
#GetMapping(value = "/contacts", headers = {"Accept-version=v1"})
public ResponseEntity<List<Contact>> findAll() {
List<Contact> contacts = contactService.findContactList();
return new ResponseEntity<>(contacts, HttpStatus.OK);
}
Endpoint with Pagination
#GetMapping(value = "/contacts", headers = {"Accept-version=v2"})
public ResponseEntity<List<Contact>> findAll(Pageable pageable) {
Page<Contact> contactPages = contactService.findContactPageable(pageable);
return new ResponseEntity<>(contactPages, HttpStatus.OK);
}
I want V1 endpoint to be shown in GroupedOpenApi and v2 endpoint to be shown in the GroupedOpenApi2. Any suggestions ?
Lets assume you put the two endpoints in different packaged and then use the Following GroupedOpenApi definition:
#Bean
public GroupedOpenApi groupOpenApiV1() {
return GroupedOpenApi.builder()
.setGroup("v1")
.packagesToScan("test.org.springdoc.api.v1")
.build();
}
#Bean
public GroupedOpenApi groupOpenApiV2() {
return GroupedOpenApi.builder()
.setGroup("v2")
.packagesToScan("test.org.springdoc.api.v2")
.build();
}

Spring 5 reactive exception handling

I'am writing a complex application as an experiment with Spring 5 Webflux. I'm planning to use lot's of techniques in this application. I'm familiar with the "old style" #RestController, but now, I'm writing functional endpoints. E.g. the backend of a company registration service.I was after something like the #ControllerAdvice in the "old world". But I wasn't really able to find anything similar as a reactive equivalent. (Or anything what worked out for me.)
I have a very basic setup. A routing function, a reactive Cassandra repository, a handler and a test class. The repository operation can throw IllegalArgumentException, and I'd like to handle it by returning HTTP Status BadRequest to client. Just as an example of I'm capable to do it. :-) The exception is taken care by the handler class. Here is my code.
RouterConfig
#Slf4j
#Configuration
#EnableWebFlux
public class RouterConfig {
#Bean
public RouterFunction<ServerResponse> route(#Autowired CompanyHandler handler) {
return RouterFunctions.route()
.nest(path("/company"), bc -> bc
.GET("/{id}", handler::handleGetCompanyDataRequest)
.before(request -> {
log.info("Request={} has been received.", request.toString());
return request;
})
.after((request, response) -> {
log.info("Response={} has been sent.", response.toString());
return response;
}))
.build();
}
}
CompanyHandler
#Slf4j
#Component
public class CompanyHandler {
#Autowired
private ReactiveCompanyRepository repository;
// Handle get single company data request
public Mono<ServerResponse> handleGetCompanyDataRequest(ServerRequest request) {
//Some validation ges here
return repository.findById(Mono.just(uuid))
.flatMap(this::ok)
.onErrorResume(IllegalArgumentException.class, e -> ServerResponse.badRequest().build())
.switchIfEmpty(ServerResponse.notFound().build());
}
private Mono<ServerResponse> ok (Company c) {
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromPublisher(Mono.just(c), Company.class));
}
}
ReactiveCompanyRepository
#Component
public interface ReactiveCompanyRepository extends ReactiveCassandraRepository<Company, UUID>{
Mono<Company> findByName(String name);
Mono<Company> findByEmail(String email);
}
My problem is that .onErrorResume(IllegalArgumentException.class, e -> ServerResponse.badRequest().build()) is never called and in the test case:
#SuppressWarnings("unchecked")
#Test
public void testGetCompanyExceptionDuringFind() {
Mockito.when(repository.findById(Mockito.any(Mono.class))).thenThrow(new IllegalArgumentException("Hahaha"));
WebTestClient.bindToRouterFunction(routerConfig.route(companyHandler))
.build()
.get().uri("/company/2b851f10-356e-11e9-a847-0f89e1aa5554")
.accept(MediaType.APPLICATION_JSON_UTF8)
.exchange()
.expectStatus().isBadRequest()
.returnResult(Company.class)
.getResponseBody();
}
I always get HttpStatus 500 instead of 400. So it fails.Any help would be appreciated!
Your test does not represent how a reactive API is supposed to behave. If such a repository throws an exception directly, I'd consider that a bug a report that to the maintainers.
When a reactive API returns a reactive type like Mono or Flux, it is expected that all errors are not thrown directly but actually sent as messages in the reactive pipeline.
In this case, your test case should probably be more like:
Mockito.when(repository.findById(Mockito.any(Mono.class)))
.thenReturn(Mono.error(new IllegalArgumentException("Hahaha")));
With that, the
onError... operators in Reactor will handle those error messages.

Rest api filtering in spring boot

I have a project in spring boot, in my controller I have many methods with similar functionality.
Methods for searching post, popular, latest etc and the urls with slight variation like -
url 1 - search/{topicId}
url 2 - search/popular/{topicId}
url 3 - search/latest/{topicId}
What I want, is to have a single method with filter in url like search/{topicId}?filter=popular
How to achieve this in spring boot?
OOPs... it does not depend on SpringBoot. It is simply a URL mapping...You can accept the type as a request param and can process as per business.....
#Controller
public class BookController {
#GetMapping(value = "/search/{topicId}")
#ResponseBody
public List<Object> getBooksByType(#RequestParam String type) {
try{
if("popular".equalsIgnoreCase(type)){
//do your business stuffs
}else if ("latest".equalsIgnoreCase(type)){
//do your business stuffs
}
}catch(Exception e){
e.printStackTrace();
}
return new ArrayList<>();
}
}

How to use WebSession in Spring WebFlux to persist data?

I am trying to develop web application using Spring WebFlux5.0.1 and Spring boot v2.0 M6 version. Requirement is to store objects in session and use it in subsequent pages/controllers.
Controller
#Controller
public class TestController {
#RequestMapping("/")
public Mono<String> testSession(Model model,ServerWebExchange swe){
Mono<WebSession> session = swe.getSession();
System.out.println("In testSession "+session);
model.addAttribute("account", new Account());
return Mono.just("account");
}
}
I was able to get Websession object from ServerWebExchange but i dont see methods to set/get attributes
Need help to understand how to use WebSession object in reactive world
Is it what you want to do ?
swe.getSession().map(
session -> {
session.getAttribute("foo"); // GET
session.getAttributes().put("foo", "bar") // SET
}
);
The accepted solution is incomplete in my opinion since it doesn't show the whole controller method, here it is how it would be done:
#PostMapping("/login")
fun doLogin(#ModelAttribute credentials: Credentials, swe: ServerWebExchange): Mono<String> {
return swe.session.flatMap {
it.attributes["userId"] = credentials.userId
"redirect:/globalPosition".toMono()
}
}

Resources