I have successfully created a springboot app that returns all the basic endpoints. Now I want to return just few fields from that endpoint in my request. For instance, return status from /health page to my rest call. How do I filter this or make my rest call more specific?
The actual requirement is two return few fields from /env, /health of different apps in one call. For which I am able to do it by returning all fields for both env and health. I just need to return specific fields from them. Also can I use in memory json objects, if so how should I do it?
Finally I figured out as how to create it. So the incoming json object consists of fields in LinkedHashMap type. So I consumed its field values using key
LinkedHashMap response = (LinkedHashMap)restTemplate.getForObject("http://localhost:8080/env",Object.class);
EnvProperties variables = new EnvProperties (response);
Wrapper POJO for all fields,
public EnvProperties (LinkedHashMap body) {
this.sysProperties = new SysEnvProperties((LinkedHashMap) body.get("systemProperties"));
}
POJO for this field,
public SysEnvProperties(LinkedHashMap body) {
this.javaVersion = body.get("java.version").toString();
}
later creating a new json string
#Override
public String toString() {
String s = null;
try {
s = mapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return s;
}
I repeated the same for fields of interest, creating a POJO for each. Finally called these fields using similar wrapper class whose toString method returned the expected json object of desired fields only.
You can create Custom health endpoint or custom heath checker too.
For e.g.
#Component
public class CustomHealthCheck extends AbstractHealthIndicator {
#Override
protected void doHealthCheck(Health.Builder bldr) throws Exception {
// TODO implement some check
boolean running = true;
if (running) {
bldr.up();
} else {
bldr.down();
}
}
}
For further reading:
http://www.christianmenz.ch/programmieren/spring-boot-health-checks/
http://briansjavablog.blogspot.be/2017/09/health-checks-metric-s-more-with-spring.html
http://www.baeldung.com/spring-boot-actuators
You can find a tutorial here. However, the interfaces you want to look into implementing are:
org.springframework.boot.actuate.endpoint.Endpoint
Similar to creating a Controller. This is your /custom-health endpoint.
org.springframework.boot.actuate.metrics.CounterService
You can count integer-value metrics which will be available at /metrics.
org.springframework.boot.actuate.metrics.GaugeService
Or, you can measure double-value metrics which will be available at /metrics.
org.springframework.boot.actuate.health.HealthIndicator
Add metrics to the /health endpoint.
Related
I'm trying to migrate my project to Quarkus Reactive with Hibernate Reactive Panache and I'm not sure how to deal with caching.
My original method looked like this
#Transactional
#CacheResult(cacheName = "subject-cache")
public Subject getSubject(#CacheKey String subjectId) throws Exception {
return subjectRepository.findByIdentifier(subjectId);
}
The Subject is loaded from the cache, if available, by the cache key "subjectId".
Migrating to Mutiny would look like this
#CacheResult(cacheName = "subject-cache")
public Uni<Subject> getSubject(#CacheKey String subjectId) {
return subjectRepository.findByIdentifier(subjectId);
}
However, it can't be right to store the Uni object in the cache.
There is also the option to inject the cache as a bean, however, the fallback function does not support to return an Uni:
#Inject
#CacheName("subject-cache")
Cache cache;
//does not work, cache.get function requires return type Subject, not Uni<Subject>
public Uni<Subject> getSubject(String subjectId) {
return cache.get(subjectId, s -> subjectRepository.findByIdentifier(subjectId));
}
//This works, needs blocking call to repo, to return response wrapped in new Uni
public Uni<Subject> getSubject(String subjectId) {
return cache.get(subjectId, s -> subjectRepository.findByIdentifier(subjectId).await().indefinitely());
}
Can the #CacheResult annotations be used with Uni / Multi and everything is handled under the hood correctly?
Your example with a #CacheResult on a method that returns Uni should actually work. The implementation will automatically "strip" the Uni type and only store the Subject in the cache.
The problem with caching Unis is that depending on how this Uni is created, multiple subscriptions can trigger some code multiple times. To avoid this you have to memoize the Uni like this:
#CacheResult(cacheName = "subject-cache")
public Uni<Subject> getSubject(#CacheKey String subjectId) {
return subjectRepository.findByIdentifier(subjectId)
.memoize().indefinitely();
}
This will ensure that every subscription to the cached Uni will always return the same value (item or failure) without re-executing anything of the original Uni flow.
Using Webflux and Reactive Spring Security, how do you do post processing via annotations to control access to methods?
Trying a very basic sample, I'm not able to get the value from the PostAuthorize annotation. For example
#GetMapping
#PostAuthorize("#email == authentication.principal.email")
public Flux<Project> sampleTest(final String email) {
log.info("email: {}", email);
return Flux.empty();
}
The email will always be null. I have the basic wiring working to the fact if I set something like #PreAuthorize("hasRole('ADMIN')") I'll get back a 403.
I can extract the Authentication out with a helper like:
public Mono<Authentication> getAuthentication() {
return ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.flatMap(Mono::just);
}
I may not be understanding your question correctly, but the PostAuthorize uses the return object - the body of the method doesn't have access to anything in the SPEL expression.
Something like this might work -
#GetMapping
#PostAuthorize("returnObject == someCondition")
public Flux<Project> sampleTest(final String email) {
// get some data and return it
}
But maybe you want to filter the items in the Flux?
You might look at the #PostFilter annotation -
// assuming there's an email property on your Project object.
#GetMapping
#PostFilter("filterObject.getEmail() == authentication.principal.email")
public Flux<Project> sampleTest() {
// get some data and return it
}
I have the bellow service, its return type is either bytes or AmazonServiceException. The ResponseEntity in controller is formed based on these return types from service.
How can I:
1. design the service to return different types
2. make the controller to form ResponseEntiry based on its receives
#Service
Myservice
{
public x example(String a) {
try {
...
return bytes;
}catch(AmazonServiceException | IOException awsEx) {
return awsEx;
}
}
}
Here is the controller
#RequestMapping(method = GET, value = "/getData")
public ResponseEntity<?> getData(#RequestParam("a") String a) throws IOException {
return this.Myservice.example(a)condition ? returnA : returnB;
}
Generally speaking, returning multiple possible types is not a recommended thing to do. It is considered as a bad smell (like Object return type). Method should be specific about the return type it guarantees.
In this particular case, you may want to return String containing either raw bytes or the mentioned Exception message.
But what would be the best practice? Answer is: #ExceptionHandler and json response.
I posted a similar answer some time ago, I suggest you to read it: How can I modify default Json error response Spring?
Also, making a controller/service to return Exception is a really bad smell. I assume you are writing some kind of REST API, in this case your protocol of data exchange should be JSON. So, if anything bad has happened with your AWS service, then throw an exception and return an appropriate message.
Is there a way to add validation to feign clients on the request parameters.
For example:
#FeignClient
public interface ZipCodeClient {
#GetMapping("/zipcodes/{zipCode}")
Optional<ZipCodeView> findByZipCode(#PathVariable("zipCode") String zipCode);
}
It would be nice to verify that zipcode is not empty and is of certain length etc, before sending the HTTP call to the server.
If your validations are simple, apply to only headers and query string parameters, you can use a RequestInterceptor for this, as it provides you the opportunity to review the RequestTemplate before it is sent to the Client.
public class ValidatingRequestInterceptor implements RequestInterceptor {
public void apply(RequestTemplate requestTemplate) {
// use the methods on the request template to check the query and values.
// throw an exception if the request is not valid.
}
}
If you need to validate the request body, you can use a custom Encoder
public class ValidatingEncoder implements Encoder {
public void encode(Object object, Type type, RequestTemplate template) {
// validate the object
// throw an exception if the request is not valid.
}
}
Lastly, if you want to validate individual parameters, you can provide a custom Expander for the parameter and validate it there. You can look at this answer for a complete explanation on how to create a custom expander that can work with Spring Cloud.
How to custom #FeignClient Expander to convert param?
For completeness, I've included an example for how to do this with vanilla Feign.
public class ZipCodeExpander implements Expander {
public String expand(Object value) {
// validate the object
// throw an exception if the request is not valid.
}
}
public interface ZipCodeClient {
#RequestLine("GET /zipcodes/{zipCode}")
Optional<ZipCodeView> findByZipCode(#Param(expander = ZipCodeExpander.class) ("zipCode") String zipCode);
}
As pointed out in this comment, a solution using the Bean Validation API would be nice. And indeed, I found in a Spring Boot project that merely placing #org.springframework.validation.annotation.Validated on the interface is sufficient for enabling Bean Validation.
So for example:
#FeignClient
#Validated
public interface ZipCodeClient {
#GetMapping("/zipcodes/{zipCode}")
Optional<ZipCodeView> findByZipCode(#PathVariable("zipCode") #NotEmpty String zipCode);
}
triggering a ConstraintViolationException in the case of violations.
Any standard Bean Validation feature should work here.
UDPATE Note that there seems to be a potential issue with this solution that might require setting a Hibernate Validator configuration property like this: hibernate.validator.allow_parallel_method_parameter_constraint=true
Currently i have one requirment our backend spring rest api will receive the data in encrypted json format (few fields are encrypted and few fields are plain text)
and then applies the decryption ,then applies some business logic on the data ,finally stores data into database.
This decryption logic is repeatining muliple service implementations methods .
So we desided to isolate the decryption logic from actual business logic.
i am using spring aop to decrypting the data and after decrypting same
object i am passing to service layer methods.
But my service layer methods contains the different types of objects as arguments
Ex :
processEmployee(EmployeeRequest request)
procesStudent(StudentRequest request)
I was looking for a way how can i dynamically change
the data on the same object fields it self ( Ex: EmployeeRequest,StudentRequest )
The following approach i have tried and stuck with 4th step.
1.Introduced a new annotation.
2.Annotate those fields which are having the encrypted data.
3.Retrieve all the annotated fields.
4.Each field data we will apply the decryption logic and
decrypted data will be injected on the same field again
i was looking for the api to achieve 4th step ?
Is there any api available to dynamically execute
the methods on the same object
or any reference please point me .
My suggestion is to not use the same POJO for both encrypted / decrypted class. It make confusing for future usage (as if I received an EmployeeRequest instance, is it decrypted or not?), and also limit the type (as your encrypted/decrypted data must be the same type).
Now, for the implementation, you have two choices:
Using explicit ConversionService
Register a converter:
#Component
public class EmployeeRequestConverter implements Converter<EmployeeRequest, EmployeeRequest> {
#Override
public EmployeeRequest convert(EmployeeRequest source) {
// Apply your decryption logic
}
}
Make the similar converters for other request objects.
Now in your controller:
public class MyController {
private ConversionService conversionService;
private MyService myService;
#RequestMapping(...)
public void aRequest(#RequestBody EmployeeRequest request) {
myService.execute(conversionService.convert(request, EmployeeRequest.class));
}
}
Using reflection.
Precondition: You have #Encrypted annotation on encrypted fields.
Unlike the first solution, you do not create explicit converter for each type of request.
#Service
public class DecryptionService {
public void <T> T decrypt(T input) {
Field[] fields = input.getClass().getDeclaredFields();
for (Field field : fields) {
Encrypted encrypted = field.getAnnotation(Encrypted.class);
if (encrypted != null) {
try{
field.setAccessible(true);
Object val = field.get(input);
// Base on #Encrypted annotation in your val, do the decryption
Object decryptedVal = ...;
field.set(input, decryptedVal);
} catch (Exception ex) {
}
}
}
}
}
Now you can apply this service for your controllers.
You might want to cache the Class.getDeclaredFields() and the mapping between Class<?> -> #Encrypted fields for performance.