spring rest web service #requestparam to method custom class (NOT string) - spring

I'm using this tutorial https://spring.io/guides/gs/rest-service/
I want to pass to web service API method parameter other then String:
#RestController
public class ApiClass {
#RequestMapping("/service")
public int service(#RequestParam(value="paramIn") CustomClass paramIn) {
if (paramIn.value != 0) return 1;
else return 0;
}
}
But when I try it I get this error:
HTTP Status 500 - Failed to convert value of type 'java.lang.String' to required type 'CustomClass'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [CustomClass]: no matching editors or conversion strategy found`
Thnaks,

Usual way to do this is to use POST or PUT method and annotate the the custom object with #RequestBody. For example:
#RequestMapping(value = "/service",
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_JSON_VALUE)
public int service(#RequestBody CustomClass paramIn) {
// do something with the paramIn
}
If you POST a JSON representation of your CustomClass instance to endpoint /service Spring will deserialize it and pass it as an argument to your controller.

Related

resilience4j circuit breaker change fallback method return type than actual called method return type

I am trying to learn Spring Boot microservices. Now I am trying to implement circuit breaker with resilience4j if any of my called service is off.
If I set the fallback method return type as like the actual method return type than it works fine but I can't show the information that my service is off. Because it then send the response null in object's fields. But if I change the return type to String on actual method and also in fallback then I will not be able to get the object value as JSON.
Is it possible to return as string something like Branch service is down!.. with my fallback method and if OK then get the object value as JSON from actual called method? My attempts are below:
My controller method:
#GetMapping("/getById/{id}")
#CircuitBreaker(name = "default", fallbackMethod = "employeeFallback")
public ResponseModelEmployee getEmployee(#PathVariable("id") Long id) {
return employeeService.findByEmployeeId(id);
}
My fallback method in controller:
public ResponseModelEmployee employeeFallback(Long id, Exception ex) {
return new ResponseModelEmployee();
}
My service method called from controller:
public ResponseModelEmployee findByEmployeeId(Long id) {
ResponseModelEmployee empDetails = new ResponseModelEmployee();
...
Branch branch = restTemplate.getForObject("http://BRANCH-SERVICE/branch/getById/" +
employee.get().getBranchId(),
Branch.class);
...
return empDetails;
}
My desire method as fallback:
public String employeeFallback(Long id, Exception ex) {
return "Branch Service is down";
}
If I set my desire method for fallback then it gives the following error:
java.lang.NoSuchMethodException: class com.example.employee.VO.ResponseModelEmployee class com.example.employee.controller.EmployeeController.employeeFallback(class java.lang.Long,class java.lang.Throwable) at io.github.resilience4j.fallback.FallbackMethod.create(FallbackMethod.java:92) ~[resilience4j-spring-1.7.0.jar:1.7.0] ....
Resilince4j expects the fallback method to have the same return type as of the actual method.
Documentation says:
It's important to remember that a fallback method should be placed in
the same class and must have the same method signature with just ONE
extra target exception parameter).
If there are multiple fallbackMethod methods, the method that has the
most closest match will be invoked, for example:
If you try to recover from NumberFormatException, the method with
signature String fallback(String parameter, IllegalArgumentException
exception)} will be invoked.
You can define one global fallback method with an exception parameter
only if multiple methods has the same return type and you want to
define the same fallback method for them once and for all.
So, you cannot directly change the return type to a different one.
You can try few suggestions:
Add #CircuitBreaker and fallback to the service method.
Change return type of service method and fallback method to Object.
One more way could be , you can keep the return type as it is but add a String type message object to response model ResponseModelEmployee. Then set message string to it during fallback.
Another solution could be to return ResponseEntity from the from the method where rest call is made and in the fallback method use ResponseEntity<?> as response object.
you can use Object as a return type
in my case for example:
#GetMapping("/getUser/{id}")
#CircuitBreaker(name= something , fallbackMethod = "ContactsServiceDown")
public ResponseEntity<User> getDetailsById(#PathVariable(id)){
//some logic
return new ResponseEntity<User>(user , HttpStatus.OK);
}
public ResponseEntity<Object> ContactsServiceDown(int id , Exception e){
//some logic
return new ResponseEntity<Object>("ContactsServersDown", HttpStatus.Forbidden)
}
or in returnType ResponseEntity<> leave the type Field empty, hopefully it may work!

Spring - Provide parsed JWT as Resource method parameter

In Spring framework, is there a way to parse JWT and provide it's data as one of the Resource method parameters?
Something like this:
class MyJwtToken {
String fieldA;
Integer fieldB;
}
public ResponseEntity<> create(
#JwtToken MyJwtToken token,
#RequestBody Item item
) {
item.setFieldA(token.getFieldA());
persist(item);
return ...;
}

Spring Data Rest ava.lang.IllegalArgumentException

I am getting
java.lang.IllegalArgumentException: Cannot get property 'objects' on null object
error when I intentionally test spring data rest api simulating a user passing bad url as in
http://myurl/findByDate?year=&month="
The year and month are Integers , but in this case I am simulating the user not passing any value.
The app is using the:
public interface myRepo extends PagingAndSortingRepository<myClass, Long> {}
interface and there is no explicit controller provided in a controller class. The data rest interface provides the following:
#Query("Select c from myPOJO c where (YEAR(c.myDate) = :year AND MONTH(c.myDate) = :month)")
Page findByDate(#Param("year") int year,
#Param("month") int month,
Pageable pageable);
I can see why the exception is being thrown ... but providing a generic exception handler to handle it does not seem to resolve the issue.
#ControllerAdvice
public class ExceptionControllerAdvice {
#ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> exceptionHandler(Exception ex) {
ErrorResponse error = new ErrorResponse();
error.setErrorCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
error.setMessage("Please contact your administrator");
//return new ResponseEntity<ErrorResponse>(error, HttpStatus.OK);
return new ResponseEntity<ErrorResponse>(error, HttpStatus.METHOD_NOT_ALLOWED);
}
}
Any advise on how to trap this data rest exception would be appreciate it.
java.lang.IllegalArgumentException: Cannot get property 'objects' on null object
Thank you
This is resolved by using the object Integer instead of the primitive Int for the param. This appears to handle the conversion to default value and avoid nulls.
Thanks

Customization of Spring ConversionFailedException error HTTP status

I have an enum class:
class enum Type {
LOCAL, REMOTE
}
I have an API that accepts the enum as a GET parameter
#RequestMapping(method = RequestMethod.GET, location="item", params = "type")
public Item[] get(Type type) {
...
When a client calls the API with valid values, like GET /item?type=LOCAL or GET /item?type=REMOTE it works fine. If the client supplies invalid value for type, e.g. GET /item?type=INVALID_TYPE, then Spring generates 500 Internal Server Error. I would like to turn it into 400 Bad Request validation error, potentially adding useful information for the client. I prefer to reuse the built type converter since in works just fine, just want to change a type of error HTTP thrown with minimum changes.
I believe if you add the right exception to #ControllerAdvice, you can customize the response. In this case, I found that MethodArgumentTypeMismatchException was the one in question.
#ExceptionHandler(MethodArgumentTypeMismatchException.class)
public void methodArgumentTypeMismatchException(final HttpServletResponse response) throws IOException {
response.sendError(BAD_REQUEST.value());
}
Why is this happening?
I would consider having a look at the example here about the #ControllerAdvice and/or #ExceptionHandler annotations. The error you're experiencing is occurring because, I believe, Spring tries to construct a Type from the "INVALID_TYPE" string and gets an error when it cannot create a Type from it--because "INVALID_TYPE" is not one of the available values.
What can I do about it?
What you'll want to do is add a string constructor to your enum so it knows, more correctly how to create one of the enum objects, and then check the input to see if its valid. If it is invalid, throw a custom exception. Then, in your #ControllerAdvice, you can customize the HTTP status code of the response.
The exception will then be able to be handled with something like the following:
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.BAD_REQUEST) // 409
#ExceptionHandler(MyCustomException.class)
public void handleConflict() {
// handle the exception response, if you need information about the
// request, it should be able to be attached to the custom exception
}
}
The enum would look something like this:
public enum Type{
LOCAL("LOCAL"),
REMOTE("REMOTE");
private String type;
private Type(String type) {
if(type.equals("LOCAL") || type.equals("REMOTE")) {
this.type = type;
} else {
throw new MyCustomException();
}
}
public String getType() {
return url;
}
}

Using MvcUriComponentsBuilder::fromMethodCall with String as the return type

I'd like to use the MvcUriComponentsBuilder::fromMethodCall method to build URLs from my controllers. I normally have a String return type (which returns the view name) and a Model instance as method parameter in my controller methods like:
#Controller
public class MyController {
#RequestMapping("/foo")
public String foo(Model uiModel) {
uiModel.addAttribute("pi", 3.1415);
return "fooView";
}
}
I try to generate a URL e.g. like:
String url = MvcUriComponentsBuilder.fromMethodCall(on(MyController.class).foo(null)).build().toUriString();
This leads to this exception:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class java.lang.String
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978) ~[spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
This happens because the String return type wants to get proxied, but can't as a final class.
What's a way to overcome this? I'd like to keep the String as a return type and get the Model as input from a parameter in my controller methods because IMHO it's way easier than handling a ModelAndView instance in every controller method.
fromMethodCall uses CGLIB proxy in the process which is why you run into the issue. This article details why.https://github.com/spring-projects/spring-hateoas/issues/155. Try using fromMethodName if you want to maintain the String return types.
MvcUriComponentsBuilder.fromMethodName(MyController.class, "foo", new Object()).build();
Consider changing the signature of the method to return Spring's ModelAndView vs. returning String. For example:
#Controller
public class MyController {
#RequestMapping("/foo")
public ModelAndView foo() {
return new ModelAndView("fooView", "pi", 3.1415);
}
}
With this refactored signature, the corresponding fromMethodCall invocation would look like this:
UriComponents uri = fromMethodCall(on(MyController.class).foo()).build();

Resources