Springboot evaluate api path from Controller method name - spring-boot

#RestController
#RequestMapping(value = "/api/orders/")
public class OrderController {
#PostMapping("create")
public ResponseEntity<OrderResponseV2> create(
#RequestBody OrderRequestV2 orderRequest) {
OrderResponse response = createOrderService.createOrder(orderRequest);
return new ResponseEntity<>(response, HttpStatus.CREATED);
}
}
Is there a way to get the whole API path including root context during runtime using Relfection from class+method name?

Related

How to implement REST API on a RestController using ModelAndView

Im struggling on how to implement a API when the methods in my controller are returning a ModelAndView. Most tutorials I can find are returning ResponseEntities. Should i be making seperate API controllers which strictly handle the API calls under a /api mapping? (which i believe isn't RESTFUL practice). Or is it possible to handle my API calls in the same controller, even when making use of ModelAndView?
My controller looks as following:
#RestController
#RequestMapping("/dish")
public class DishController {
private final DishRepository dishRepository;
public DishController(DishRepository dishRepository) {
this.dishRepository = dishRepository;
}
#GetMapping
public ModelAndView list() {
Iterable<Dish> dishes = this.dishRepository.findAll();
return new ModelAndView("dishes/list", "dishes", dishes);
}
#GetMapping("{id}")
public ModelAndView view(#PathVariable("id") Dish dish) {
return new ModelAndView("dishes/view", "dish", dish);
}
#GetMapping(params = "form")
#PreAuthorize("hasRole('ROLE_ADMIN')")
public String createForm(#ModelAttribute Dish dish) {
return "dishes/form";
}
#ResponseStatus(HttpStatus.CREATED)
#PostMapping
#PreAuthorize("hasRole('ROLE_ADMIN')")
public ModelAndView create(#Valid Dish dish, BindingResult result,
RedirectAttributes redirect) {
if (result.hasErrors()) {
return new ModelAndView("dishes/form", "formErrors", result.getAllErrors());
}
dish = this.dishRepository.save(dish);
redirect.addFlashAttribute("globalMessage", "view.success");
return new ModelAndView("redirect:/d/{dish.id}", "dish.id", dish.getId());
}
#RequestMapping("foo")
public String foo() {
throw new RuntimeException("Expected exception in controller");
}
#ResponseStatus(HttpStatus.OK)
#GetMapping("delete/{id}")
#PreAuthorize("hasRole('ROLE_ADMIN')")
public ModelAndView delete(#PathVariable("id") Long id) {
this.dishRepository.deleteById(id);
Iterable<Dish> dishes = this.dishRepository.findAll();
return new ModelAndView("dishes/list", "dishes", dishes);
}
#ResponseStatus(HttpStatus.OK)
#GetMapping("/modify/{id}")
#PreAuthorize("hasRole('ROLE_ADMIN')")
public ModelAndView modifyForm(#PathVariable("id") Dish dish) {
return new ModelAndView("dishes/form", "dish", dish);
}
You should not use Model and View in RestController. The main goal of RestController is to return data, not views. Take a look here for more details: Returning view from Spring MVC #RestController.
#RestController is a shorthand for writing #Controller and #ResponseBody, which you should only use if all methods are returning an object that should be treated as the response body (eg. JSON).
If you want to combine both REST endpoints and MVC endpoints within the same controller, you can annotate it with #Controller and individually annotate each method with #ResponseBody.
For example:
#Controller // Use #Controller in stead of #RestController
#RequestMapping("/dish")
public class DishController {
#GetMapping("/list")
public ModelAndView list() { /* ... */ }
#GetMapping
#ResponseBody // Use #ResponseBody for REST API methods
public List<Dish> findAll() { /* ... */ }
}
Alternatively, as you've mentioned, you can use multiple controllers:
#Controller
#RequestMapping("/dish")
public class DishViewController { /* ... */ }
#RestController
#RequestMapping("/api/dish")
public class DishAPIController { /* ... */ }

Why Swagger not showing all methods in same class?

i'm trying to use swagger with my code , but not all methods are listing in swagger-ui some methods not show
i am using swagger 2.5.0 version ,and spring boot 2.1.0.RELEASE
my user rest controller
#RestController
#RequestMapping(value = "/rest")
public class UserRestController {
#Autowired
private UserService userService;
#RequestMapping(method = RequestMethod.GET, value = "/users")
public Iterator<User> getUsers() {
return userService.getUsers();
}
#RequestMapping(method = RequestMethod.GET, value = "/user/{id}")
public User getUser(#PathVariable("id") Long id) {
return userService.getUser(id);
}
#RequestMapping(method = RequestMethod.POST, value = "/user")
public User save(#RequestBody User user) {
User userValidation = userService.getUser(user.getId());
if (userValidation != null) {
throw new IllegalAddException("username already used !");
}
return userService.save(user);
}
#RequestMapping(method = RequestMethod.DELETE, value = "/user")
public User delete(#RequestBody User user) {
return userService.save(user);
}
}
and this my config code
#Configuration
#EnableSwagger2
public class SwaggerApi {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.basePackage("com.social.core.rest")).paths(PathSelectors.ant("/rest/*"))
.build().apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfo("Social API", "Soccial api for gamerz zone.", "API TOS", "Terms of service",
new Contact("yali", "www.social.com", "prg#gmail.com"), "License of API",
"API license URL");
}
}
getUser method not showing in swagger ui , and the method worked when i hit url and already getting data
just three method are showing
I solved this issue by adding more star in paths with me config
paths(PathSelectors.ant("/rest/**"))

In the Spring3,How to call a another server's controller in my controller

I have 3 servers,serverA,serverB,serverC,Now in the serverC,some request from serverB is by processed,and then,I don't know what is the result(response),if it's resultA,I want give the resultA to the serverA as a request,else give the serverB.
so what I can do something in the serverC's controller,or there is something wrong in the desgin.
Please tell me what I should to do,Thanks.
This is my code.
serverA
#RestController
public class ControllerA {
#RequestMapping(value = "/methodA", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<String> methodA(#RequestBody String something) {
// some process
return null;
}
serverB
#RestController
public class ControllerB {
#RequestMapping(value = "/methodB", consumes =MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> methodB(#RequestBody String something) {
// some process
return null;
}
serverC
#RestController
public class ControllerC {
public ResponseEntity<String> methodC(#RequestBody String someReq) {
if (checkPam(someReq)) {
**// I want to call the ControllerA in serverA.**
}else {
**// I want to call the ControllerB in serverB.**
}
return null;
}
You can simply Use RestTemplate:
#RestController
public class ControllerC {
public ResponseEntity<String> methodC(#RequestBody String someReq) {
RestTemplate restTemplate = new RestTemplate();
if (checkPam(someReq)) {
String fooResourceUrl
= "http://path-to-server-a/path-to-service-a";
ResponseEntity<String> response
= restTemplate.getForEntity(fooResourceUrl , String.class);
}else {
String fooResourceUrl
= "http://path-to-server-b/path-to-service-b";
ResponseEntity<String> response
= restTemplate.getForEntity(fooResourceUrl , String.class);
}
return null;
}
As you can see, I instantiate RestTemplate object by new operator, you can also declare RestTemplate bean in your context and then autowire it in your controller class.

Rest template giving null body and status 302

I am trying to consume a rest call in my mvc controller, however every time I do it returns a null body with http status as 302.Also I am using spring boot with spring security to get https.
I've followed code samples from here: http://websystique.com/springmvc/spring-mvc-4-restful-web-services-crud-example-resttemplate/
and Get list of JSON objects with Spring RestTemplate however none of these work
Can someone please point me in the right direction
Thank you,
REST
#RequestMapping(value = "/api/*")
#RestController
public class PostApiController {
static final Logger logger = LogManager.getLogger(PostApiController.class.getName());
private final PostService postService;
#Inject
public PostApiController(final PostService postService) {
this.postService = postService;
}
//-------------------Retrieve All Posts--------------------------------------------------------
#RequestMapping(value = "post", method = RequestMethod.GET)
public ResponseEntity<List<Post>> getAllPosts() {
List<Post> posts = postService.findAllPosts();
if(posts.isEmpty()){
return new ResponseEntity<List<Post>>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
}
return new ResponseEntity<List<Post>>(posts, HttpStatus.OK);
}
}
Controller
#Controller
public class PostController {
static final Logger logger = LogManager.getLogger(PostController.class.getName());
public static final String REST_SERVICE_URI = "http://localhost:8080/api"; //"http://localhost:8080/api";
private final PostService postService;
#Inject
public PostController(final PostService postService) {
this.postService = postService;
}
#SuppressWarnings("unchecked")
#RequestMapping(value = "/getAll")
// public String create(#Valid Post post, BindingResult bindingResult, Model
// model) {
public ModelAndView getAll() {
// if (bindingResult.hasErrors()) {
// return "mvchome";
// }
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<List<Post>> responseEntity = restTemplate.exchange(REST_SERVICE_URI+"/post",HttpMethod.GET, null, new ParameterizedTypeReference<List<Post>>() {});
// ResponseEntity<Post[]> responseEntity = restTemplate.getForEntity(REST_SERVICE_URI+"/post", Post[].class);
List<Post> postsMap = responseEntity.getBody();
MediaType contentType = responseEntity.getHeaders().getContentType();
HttpStatus statusCode = responseEntity.getStatusCode();
// List<LinkedHashMap<String, Object>> postsMap = restTemplate.getForObject(REST_SERVICE_URI+"/post", List.class);
// String s= REST_SERVICE_URI+"/post";
// logger.info(s);
if(postsMap!=null){
for(Post map : postsMap){
logger.info("User : id="+map.getUid());
}
}else{
logger.info("No user exist----------");
}
//List<Post> postList = postService.findAllPosts();
ModelAndView mav = new ModelAndView("mvchome");
mav.addObject("postsList", postsMap);
Post newpost = new Post();
mav.addObject("post", newpost);
return mav;
}
}
***** to fix my issue I modified my code to just do a redirect on select url paths instead of "/*"
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat =
new TomcatEmbeddedServletContainerFactory() {
#Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
//used to be just collection.addPattern("/*"); now I changed it to specify which path I want it to redirect
collection.addPattern("/mvchome/*");
collection.addPattern("/home/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(createHttpConnector());
return tomcat;
}
The http status 302 is usually caused by wrong url setting.
First, make sure that public ResponseEntity<List<Post>> getAllPosts() {} method is called (just print List<Post> result inside it).
If it's called properly and you can get the return value inside public ModelAndView getAll() {}.
The problem should be the directing setting of the public ModelAndView getAll() {} method.
Check if you make something wrong in your web.xml or spring configuration. Pay attention to the configuration which redirects to views and the url mapping of your dispatcher servlet.
If public ResponseEntity<List<Post>> getAllPosts() {} is called but you can't get the return value, then it should be the issues of directing setting of the public ResponseEntity<List<Post>> getAllPosts() {} method.
Check your spring configuration and web.xml for that. The possible cause usually will be the misuse of wildcard in the configuration and web.xml, or just unnoticed wrong mapping.

Spring HATEOAS: How to advertise resource services?

I've got Spring HATEOAS working for accessing a specific resource, such as
http://localhost:8080/user/1
But I want to also be able to advertise a service url:
http://localhost:8080/user
For instance, if you do a GET / , I want to return the service resources I advertise. Right now the only one is /auth.
#RequestMapping(value = "/", method = RequestMethod.GET)
#ResponseBody
public HttpEntity<AuthenticationResource> post() {
AuthenticationResource resource = new AuthenticationResource();
resource.add(linkTo(methodOn(AuthenticationController.class).authenticate()).withSelfRel());
return new ResponseEntity<AuthenticationResource>(resource, HttpStatus.OK);
}
#RequestMapping(value = "/auth", method = RequestMethod.POST, consumes = "application/json")
#ResponseBody
public void authenticate() {
//users.save(user);
}
Currently this is not compiling because linkTo doesn't take a void argument, which I presume is the return type of my authenticate method
What I WANT is this:
{"links":[{"rel":"someString","href":"http://localhost/auth"}]}
How do I accomplish this while staying within HATEOAS best practice?
This.
#RequestMapping(value = "/", method = RequestMethod.GET)
#ResponseBody
public HttpEntity<ResourceSupport> post() {
ResourceSupport resource = new ResourceSupport();
resource.add(linkTo(methodOn(AuthenticationController.class).authenticate()).withRel("authenticate"));
return new ResponseEntity<ResourceSupport>(resource, HttpStatus.OK);
}
#RequestMapping(value = "/auth", method = RequestMethod.POST, consumes = "application/json")
#ResponseBody
public HttpEntity<AuthenticationResource> authenticate() {
AuthenticationResourceAssembler assembler = new AuthenticationResourceAssembler();
AuthenticationResource resource = assembler.toResource(new Authentication());
return new ResponseEntity<AuthenticationResource>(resource, HttpStatus.OK);
}

Resources