How to get a specific spring boot controller endpoint full url without concat/hardcode strings? My case is need send a request to external url and get async notification back from it, so I need pass my notification endpoint full url to it. Here is sample code:
#RestController
#RequestMapping("/api/v1")
public class MyController {
#PostMapping("/sendRequest")
public void sendRequest() {
...
// 1. get /notifyStatus endpoint full url to be send to external service
String myNotificationFullUrl = xxx ?
// 2. call external service
}
#GetMapping("/notifyStatus")
public void notifyStatus() {
...
}
}
Allows getting any URL on your system, not just a current one.
import org.springframework.hateoas.mvc.ControllerLinkBuilder
...
ControllerLinkBuilder linkBuilder = ControllerLinkBuilder.linkTo(methodOn(YourController.class).getSomeEntityMethod(parameterId, parameterTwoId))
URI methodUri = linkBuilder.Uri()
String methodUrl = methodUri.getPath()
There are more methods also to change a host name, etc.
if you don't want hardcode, maybe a possible solution is add a messages.properties with all your messages and urls. Then you can configure Spring MessageSource and get your url from that file.
Supose that you have a message.properties file with the following property:
url=full_url_to_your_service
In your #Controller, inject your configured MessageSource to allow Spring resolve the messages:
#Autowired
private MessageSource messageSource;
Then you can get your url as follow:
String url= messageSource.getMessage("url", put_here_your_locale);
If you need more information, check Spring doc for MessageSource.
Related
Is there an option to specify a request header once in spring web RestController instead of doing it on every request?
e.q.
#RestController("workflowController")
public class MyClass{
public Value list(#RequestHeader(USER_ID_HEADER_PARAM) String user) {
...some code
}
public Workflow create(#RequestBody Workflow workflow, #RequestHeader(USER_ID_HEADER_PARAM) String user) {
... some code
}
}
the #RequestHeader(USER_ID_HEADER_PARAM) will be repeated in every request.
is there a way to specity it in the #RestCotroller level or the class level?
Thanks
Use some kind of filter class that can be configured to wrap around your requests in your servlets based on the URL path.
Here is info about the generic Servlet API filter API:
https://www.oracle.com/technetwork/java/filters-137243.html
If you're using Spring, there's another way to do it:
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#filters
https://www.baeldung.com/intercepting-filter-pattern-in-java
I have a Spring Boot app that has one controller that serves mostly RESTful endpoints, but it has 1 endpoint that actually needs to return HTML.
#RestController
#RequestMapping("v1/data/accounts")
public class AccountResource {
// Half a dozen endpoints that are all pure data, RESTful APIs
#GetMapping("/confirmRegistration")
public void confirmRegistration(#RequestParam(value = "vt") String token) {
// Some logic goes here
System.out.println("This should work!");
return ResponseEntity.ok('<HTML><body>Hey you did a good job!.</body></HTML>')
}
}
When this runs, no errors/exceptions get thrown at all, and in fact I see the "This should work!" log message in my app logs. However from both a browser and a curl command, the HTTP response is empty. Any idea what I need to change in the ResponEntity builder to get the server returning a hand-crafted HTML string?
Add this to your #RequestMapping or #GetMapping
produces = MediaType.TEXT_HTML_VALUE
Spring defaults to application\json. If you need any other type, you need to specify it.
I'm using Spring boot 1.5.3, Spring Data REST, Spring HATEOAS. Spring Data REST is amazing and does a perfect job, but sometimes it's needed custom business logic and therfore I need to create a custom controller.
I'm going to use a #RepositoryRestController to benefit of Spring Data REST functionality (http://docs.spring.io/spring-data/rest/docs/current/reference/html/#customizing-sdr.overriding-sdr-response-handlers).
Because Spring Data REST use HATEOAS by default, I'm using that. I need a controller like this:
#RepositoryRestController
#RequestMapping(path = "/api/v1/workSessions")
public class WorkSessionController {
#Autowired
private EntityLinks entityLinks;
#Autowired
private WorkSessionRepository workSessionRepository;
#Autowired
private UserRepository userRepository;
#PreAuthorize("isAuthenticated()")
#RequestMapping(method = RequestMethod.POST, path = "/start")
public ResponseEntity<?> start(#RequestBody(required = true) CheckPoint checkPoint) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (checkPoint == null) {
throw new RuntimeException("Checkpoint cannot be empty");
}
if (workSessionRepository.findByAgentUsernameAndEndDateIsNull(auth.getName()).size() > 0) {
// TODO return exception
throw new RuntimeException("Exist a open work session for the user {0}");
}
// ...otherwise it's opened a new work session
WorkSession workSession = new WorkSession();
workSession.setAgent(userRepository.findByUsername(auth.getName()));
workSession.setCheckPoint(checkPoint);
workSession = workSessionRepository.save(workSession);
Resource<WorkSession> resource = new Resource<>(workSession);
resource.add(entityLinks.linkFor(WorkSession.class).slash(workSession.getId()).withSelfRel());
return ResponseEntity.ok(resource);
}
}
Because the parameter CheckPoint must be an existant resource, I want the client send the link of the resourse (like you can do in Spring Data REST POST methods).
Unfortunately when I try to do that, server side I receive an empty CheckPoint object.
I already read Resolving entity URI in custom controller (Spring HATEOAS) and converting URI to entity with custom controller in spring data rest? and expecially Accepting a Spring Data REST URI in custom controller.
I'm wondering if there is a best practice to do this avoiding to expose id to the client, followind so the HATEOAS principles.
Try to change you controller like this:
#RepositoryRestController
#RequestMapping(path = "/checkPoints")
public class CheckPointController {
//...
#Transactional
#PostMapping("/{id}/start")
public ResponseEntity<?> startWorkSession(#PathVariable("id") CheckPoint checkPoint) {
//...
}
}
That will mean: "For CheckPoint with given ID start new WorkSession".
Your POST request will be like: localhost:8080/api/v1/checkPoints/1/start.
Using spring-web, I am mapping a method to receive a request containing dots "." on the path:
#RequestMapping(value = "download/{id:.+}", method = RequestMethod.GET, produces = "application/xls")
public String download(#PathVariable(value = "id") String id) { ... }
For example, /download/file.xls should be a valid address. But when I try to access that address, Spring returns Could not find acceptable representation as if it was trying to find a resource named file.xls.
Spring shouldn't execute download method rather than try to find a resource named as the path variable?
Obs.: my application is a spring-boot application.
Your #RequestMapping says it produces "application/xls", but your return type is a String and you haven't annotated the return type with #ResponseBody.
If you want to return an Excel spreadsheet, you need to produce that spreadsheet on the server and return it as a byte[] from your request mapping. I'm not sure how or why you'd return a String, unless you're controller is a simple #Controller and you're returning the view name.
Have you tried configuring your RequestMappingHandlerMapping
handler.setUseSuffixPatternMatch( false )
(I was configuring my RequestMappingHandlerMapping anyway, so for me I just needed to add that line - chances are you may be letting Spring Boot autoconfig that class).
See https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.html#setUseRegisteredSuffixPatternMatch-boolean-
Possibly you may need to turn off content negotiation as well - I can't remember exactly what Spring Boot default content negotiation is, but it might be affecting your case.
#Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false)
}
Worth noting that if you are working on a wider/existing application then both these configurations have possible implications more widely, so if that is the case then tread carefully!
Using Spring Boot 1.0, I was able to customize the actuator endpoints as follows...
endpoints.beans.id=foo/springbeans
This would expose the spring beans endpoint at /foo/springbeans. However, in the latest Spring Boot this is not possible due to the following code in the AbstractEndpoint...
#NotNull
#Pattern(regexp = "\\w+", message = "ID must only contains letters, numbers and '_'")
private String id;
I tried using the underscore, but that just exposes the endpoint at /foo_springbeans. This lead me to try to add a view controller so I could at least redirect or forward to the default endpoint, but I couldn't find an easy way to do that either. How can I configure the endpoint or a redirect?
After opening an issue with Spring Boot and being told to simply move the entire management context as suggested by Rafal, I was able to achieve what I was looking for, albeit with more code than I'd like. I created a custom MvcEndpoint as follows...
#Named
public class MyCustomHealthCheck extends EndpointMvcAdapter {
private HealthEndpoint delegate;
#Inject
public MyCustomHealthCheck(HealthEndpoint delegate) {
super(delegate);
this.delegate = delegate;
}
#ResponseBody
#RequestMapping(value = "/springbeans", method = GET)
public Health foo() {
return delegate.invoke();
}
}
The code above creates the /springbeans path underwhatever path the HealthEndpoint is mapped to, which is fine enough for my usecase. If I wanted it mapped to an entirely separate path, I would have needed to create a dummy endpoint and stick this MvcEndpoint under that.
For Spring 1.x Following property should help you:
endpoints.beans.path: /foo/springbeans
You can use it with any standard endpoint and if you want to use it with custom endpoint that extends AbstractEndpoint then you need additional annotation:
#ConfigurationProperties(prefix = "endpoints.customEndpoint")
and then use property:
endpoints.customEndpoint.path: /custom/endpoint