Exception full class name is coming in Rest API json response - java

I have written a code which will throw the following message if the list id specify in the URL didn't find in the db. It should send a json response with error message but i am getting exception class name also with message:
Expected Output from rest API:
{
"code": 404,
"message": "Watchlist dnd was not found"
}
code:
#RolesAllowed({ "admin" })
#Path("/{listId}")
#GET
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ApiOperation(value = "Returns a watchlist.", notes = "")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "The watchlist was returned.", response = Watchlist.class),
#ApiResponse(code = 404, message = "The watchlist was not found.", response = ErrorMessage.class),
#ApiResponse(code = 500, message = "Internal server error.", response = ErrorMessage.class) })
public Watchlist getList(#PathParam("listId") String listId, #HeaderParam("x-access-token") String jwtToken,
#Context SecurityContext sec, #Context final HttpServletResponse response) throws Exception {
final String sourceMethod = "getList";
if (logger.isLoggable(Level.FINER)) {
logger.entering(CLASSNAME, sourceMethod);
}
WatchlistService service = new WatchlistService(cedmPersitence);
Watchlist list = service.getWatchList(listId);
if (logger.isLoggable(Level.FINER)) {
logger.exiting(CLASSNAME, sourceMethod);
}
return list;
}
public Watchlist getWatchList(String listId) throws IOException,NotFoundException{
Watchlist list = new Watchlist();
list.setListId(listId);
if(listId !=null) {
HBasePersistence persistence = new HBasePersistence();
persistence.init("watchlist");
List<WatchlistEntry> watchListEntries = persistence.getWatchlistByListId(listId);
if (watchListEntries == null || watchListEntries.isEmpty()) {
throw new NotFoundException("Watchlist " + listId + " was not found");
}
list.setEntries(watchListEntries);
}
return list;
}
But I am getting this response:
{
"code": 404,
"message": "class com.ibm.cedm.exception.NotFoundException:Watchlist dnd was not found"
}
anybody know why is it so ?

As you can see in the reference:
https://docs.oracle.com/javaee/7/api/javax/ws/rs/NotFoundException.html#NotFoundException-java.lang.String- , when you pass the error message to a NotFoundException, surely the Throwable.getMessage() enrich that message with the class name.
NotFoundException also accepts a Response as parameter, so instead of passing a message you can build the response by passing the Response.
final Response.ResponseBuilder response = Response.status(Response.Status.NOT_FOUND);
response.entity("Watchlist " + listId + " was not found").type("text/plain");
throw new NotFoundException(response.build());

Related

How do I get the authentication key on a Rest Client

I have a microservice that need to consume from another microservice. The thing is that the controller has an authentication header. So how can I get that token from it to make a call from this new rest client?
I have used swagger codegen to get the different endpoints that I need to check.
The interface is as follows:
#javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.SpringCodegen", date = "2022-06-20T09:40:39.340424400+02:00[Europe/Berlin]")
#Api(value = "getRelationByCucoId", description = "the getRelationByCucoId API")
public interface GetRelationByCucoIdApi {
#ApiOperation(value = "Retrieve a list of CustomerIDs for a certain CucoId", nickname = "getRelationByCucoIdUsingGET", notes = "This is a description", response = CucoPerson.class, responseContainer = "List", tags={ "Customers", })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successfully retrieved list", response = CucoPerson.class, responseContainer = "List"),
#ApiResponse(code = 400, message = "The provided CucoID format or length is incorrect (Integer up to 10 digit)"),
#ApiResponse(code = 401, message = "You are not authorized to view the resource, check if the JWT was provided in the header"),
#ApiResponse(code = 403, message = "Accessing the resource you were trying to reach is forbidden"),
#ApiResponse(code = 404, message = "The resource you were trying to reach is not found") })
#RequestMapping(value = "/getRelationByCucoId/{cucoId}",
produces = { "application/json" },
method = RequestMethod.GET)
ResponseEntity<List<CucoPerson>> getRelationByCucoIdUsingGET(#ApiParam(value = "cucoId",required=true) #PathVariable("cucoId") Integer cucoId
,#ApiParam(value = "Authentication Header" ) #RequestHeader(value="GS-AUTH-TOKEN", required=false) String GS_AUTH_TOKEN, #AuthenticationPrincipal Principal principal
);
}
And the implementation:
#Override
public ResponseEntity<List<CucoPerson>> getRelationByCucoIdUsingGET(Integer cucoId, String GS_AUTH_TOKEN, Principal principal) {
return new ResponseEntity<>(new ArrayList<>(), HttpStatus.OK);
}
So how can I get it authenticated?
Thanks!

Java Swagger not generating service where endpoint response type is List<T>

I am trying to use swagger with java.
Using NSwag studio I am able to generate all my endpoints except one that returns a list of objects.
Here is my action in controller:
#ApiOperation(value = "getAll", nickname = "getAll", responseContainer = "List", response = DiakEntity.class)
#GetMapping("/api/diakok")
#ResponseBody
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')")
public List<DiakEntity> GetDiakok() throws Exception
{
ServiceObjectResponse<List<DiakEntity>> request = _diakService.getAll();
if(!request.getIsSuccess())
{
throw new Exception(request.getMessage());
}
return request.getObject();
}
I am using swagger-annotations 1.5.23, springfox-swagger-ui 2.9.2, springfox-swagger2 2.9.2.
If I test from Postman it works.
Also tried like this:
#ApiOperation(value = "getAll", nickname = "getAll")
#ApiResponse(code = 200, responseContainer="List", response=DiakEntity.class, message = "Gets all diak objects")
#GetMapping("/api/diakok")
#ResponseBody
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')")
public ResponseEntity<List<DiakEntity>> GetDiakok() throws Exception
{
ServiceObjectResponse<List<DiakEntity>> request = _diakService.getAll();
if(!request.getIsSuccess())
{
throw new Exception(request.getMessage());
}
return new ResponseEntity<>(request.getObject(), HttpStatus.OK);
}
thnx
Please try with the following annotation for swagger.
#ApiOperation(value = "getAll", nickname = "getAll")
#ApiResponse(code = 200, responseContainer="List", response=DiakEntity.class)
At the end I changed my action as below, and it started to work
#ApiOperation(value = "all", nickname = "all")
#PostMapping("/api/diak/all")
#ResponseBody
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')")
public List<DiakEntity> GetAll(#RequestBody #Valid RequestDiakByName data) throws Exception
{
ServiceObjectResponse<List<DiakEntity>> request = _diakService.getAll();
if(!request.getIsSuccess())
{
throw new Exception(request.getMessage());
}
return request.getObject();
}

How to change null value from default-reply-timeout property in gateway element for Spring Integration?

I have a gateway defined like this:
<int:gateway
service-interface="com.charter.egateway.integration.accountbase.AccountBaseSearchServiceInterface"
default-reply-timeout="13000" >
<int:method name="search" request-channel="accountBaseRequestChannel" reply-channel="accountBaseResponseChannel" />
</int:gateway>
When it timeouts after 13s, my code below receives a 'null' - but I'm using 'null' for 'null responses', i.e, responses without content:
#Secured(SecurityRoles.ROLE_READ)
#RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE, value = "search", method = RequestMethod.GET)
#ApiOperation(
value = "This service works like a single entry to execute searches on Salesforce",
notes = "Returns a list of accounts."
)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success", response = AccountSearchResponse.class),
#ApiResponse(code = 400, message = "Invalid request.", response = ErrorResponse.class, responseContainer="List"),
#ApiResponse(code = 500, message = "Internal Server Error.", response = ErrorResponse.class, responseContainer="List"),
#ApiResponse(code = 204, message = "No content.", response = AccountSearchResponse.class)
})
public ResponseEntity<?> search(#Valid AccountSearchRequest objectSearch) {
logger.debug("Returning request: {}.", objectSearch.toString());
ResponseEntity<ErrorResponse>response = RequestMessageValidator.validate(objectSearch);
if (null != response) {
logger.debug("Returning ErrorResponse: {}.", response.toString());
return response;
}
Message<AccountSearchRequest> requestMsg = MessageBuilder.withPayload(objectSearch).setCorrelationId(transaction.getTransactionId()).build();
EndAccountSearchResponse searchResponse = (EndAccountSearchResponse) service.search(requestMsg);
if(searchResponse==null){
logger.debug("Returning no content.");
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
logger.debug("Returning Response: {}.", searchResponse.toString());
return ResponseEntity.ok(searchResponse);
}
How can I differentiate outcome from timeout, which is null from 'no content' (searchResponse == null)?
Regards,
You can't "return null". null is an invalid message payload; when a component in an integration flow returns null; the flow stops; nothing is sent back to the gateway. The gateway will always time out (and return null) in this case.
You should either return a special value indicating no result, or throw an exception (and catch it).

Spring #ExceptionHandler returns wrong HttpStatus code

TL;DR: #ExceptionHandler function is returning 200 OK instead of 400 Bad Request for a MissingServletParameterException when calling HttpServletResponse.getStatus & HttpStatus.valueOf(HttpServletResponse.getStatus)).name(). MissingServletParameterException is only used as an example, also happens for other exceptions too.
Hello,
The issue I'm having is that I'm trying to integrate Raygun (a crash reporting system) with our Java/Spring Boot application. The easiest way I've found was to create a custom exception handler that would display the error message to the user as well as pass the exception to Raygun.
Originally, I tried the implementation suggested here with my own Raygun implementation added https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
#ControllerAdvice
class GlobalDefaultExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";
private static ApiAccessToken accessToken = new ApiAccessToken();
private static String databaseName = null;
#ExceptionHandler(value = Exception.class)
public ModelAndView
defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
// If the exception is annotated with #ResponseStatus rethrow it and let
// the framework handle it
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
throw e;
}
// Otherwise setup and send the user to a default error-view.
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
// Display the error message to the user, and send the exception to Raygun along with any user details provided.
RaygunClient client = new RaygunClient("<MyRaygunAPIKey>");
if (accessToken.getUsername() != null && accessToken.getDatabaseName() != null) {
ArrayList tags = new ArrayList<String>();
tags.add("username: " + accessToken.getUsername());
tags.add("database: " + accessToken.getDatabaseName());
client.Send(e, tags);
accessToken = null;
return mav;
} else if (databaseName != null) {
ArrayList tags = new ArrayList<String>();
tags.add("database: " + databaseName);
client.Send(e, tags);
databaseName = null;
return mav;
} else {
client.Send(e);
return mav;
}
}
The problem I encountered with this is that we have both public and private API endpoints. The private API endpoints are used for our iOS applications, whereas the public API endpoints have no front-end. They were designed for businesses to be able to integrate into their own systems (PowerBI, Postman, custom integrations, etc). And so there is no views that I can redirect to using ModelAndView.
Instead, what I've decided to do is instead of using ModelAndView, I'm just returning a string that has been formatted to mimic Spring's default JSON error message.
#ExceptionHandler(value = Exception.class)
public #ResponseBody String defaultErrorHandler(HttpServletRequest req, HttpServletResponse resp, Exception e) throws Exception {
// Create a customised error message that imitates the Spring default Json error message
StringBuilder sb = new StringBuilder("{ \n")
.append(" \"timestamp\": ").append("\"").append(DateTime.now().toString()).append("\" \n")
.append(" \"status\": ").append(resp.getStatus()).append(" \n")
.append(" \"error\": ").append("\"").append(HttpStatus.valueOf(resp.getStatus()).name()).append("\" \n")
.append(" \"exception\": ").append("\"").append(e.getClass().toString().substring(6)).append("\" \n")
.append(" \"message\": ").append("\"").append(e.getMessage()).append("\" \n")
.append(" \"path\": ").append("\"").append(req.getServletPath()).append("\" \n")
.append("}");
String errorMessage = String.format(sb.toString());
// Display the error message to the user, and send the exception to Raygun along with any user details provided.
RaygunClient client = new RaygunClient("<MyRaygunAPIKey>");
if (accessToken.getUsername() != null && accessToken.getDatabaseName() != null) {
ArrayList tags = new ArrayList<String>();
tags.add("username: " + accessToken.getUsername());
tags.add("database: " + accessToken.getDatabaseName());
client.Send(e, tags);
accessToken = null;
return errorMessage;
} else if (databaseName != null) {
ArrayList tags = new ArrayList<String>();
tags.add("database: " + databaseName);
client.Send(e, tags);
databaseName = null;
return errorMessage;
} else {
client.Send(e);
return errorMessage;
}
}
The only issue with this is that when I purposefully cause an exception to be thrown, it returns with a HTTP status of 200 OK which is obviously not correct.
For instance, this is with defaultErrorHandler() commented out (sends nothing to Raygun):
{
"timestamp": "2017-07-18T02:59:45.131+0000",
"status": 400,
"error": "Bad Request",
"exception":
"org.springframework.web.bind.MissingServletRequestParameterException",
"message": "Required String parameter ‘foo’ is not present",
"path": "/api/foo/bar/v1"
}
And this is with it not commented out (sends the exception to Raygun):
{
"timestamp": "2017-07-25T06:21:53.895Z"
"status": 200
"error": "OK"
"exception": "org.springframework.web.bind.MissingServletRequestParameterException"
"message": "Required String parameter 'foo' is not present"
"path": "/api/foo/bar/V1"
}
Any help or advice on what I'm doing wrong would be greatly appreciated. Thank you for your time.
In your controller advice try this way to map exception type to Http-Status as follows:
if (ex instanceof MyException)
{//just an example.
return new ResponseEntity<>(e, HttpStatus.BAD_REQUEST);
}
else
{//all other unhandled exceptions
return new ResponseEntity<>(e, HttpStatus.INTERNAL_SERVER_ERROR);
}
Here MyException is the type of exception you are throwing at runtime. Say I am handling Bad request.
I'm still unsure why it was returning a 200 OK status when an exception was being thrown. But I've realised that what I was doing with trying to create a string that mimics Spring's default json error message, was overly complex and not necessary at all.
Once I had sent the exception through to Raygun, I can just rethrow the exception and let the framework handle it like any exception annotated with #ResponseStatus.
#ExceptionHandler(value = Exception.class)
public void defaultErrorHandler(Exception e) throws Exception {
RaygunClient client = new RaygunClient("<MyRaygunAPIKey>");
// If the exception is annotated with #ResponseStatus rethrow it and let
// the framework handle it
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
throw e;
}
// Otherwise send the exception Raygun and then rethrow it and let the framework handle it
if (accessToken.getUsername() != null && accessToken.getDatabaseName() != null) {
ArrayList tags = new ArrayList<String>();
tags.add("username: " + accessToken.getUsername());
tags.add("database: " + accessToken.getDatabaseName());
client.Send(e, tags);
accessToken = null;
throw e;
} else if (databaseName != null) {
ArrayList tags = new ArrayList<String>();
tags.add("database: " + databaseName);
client.Send(e, tags);
databaseName = null;
throw e;
} else {
client.Send(e);
throw e;
}
}
A bare bones implentation would look like this:
#ExceptionHandler(value = Exception.class)
public void defaultErrorHandler(Exception e) throws Exception {
RaygunClient client = new RaygunClient("<MyRaygunAPIKey>");
// If the exception is annotated with #ResponseStatus rethrow it and let the framework handle it
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
throw e;
}
// Otherwise send the exception Raygun and then rethrow it and let the framework handle it
else {
client.Send(e);
throw e;
}
}

Swagger Spring Annotations: proper response formatting

I have Swagger code annotations for documentation purposes similar to the following:
#RequestMapping(value = "/campaigns/{campaignUuid}",
method = RequestMethod.GET)
#ApiOperation(value = "...",
httpMethod = "GET",
notes = "...",
response = XCampaign.class,
tags = { "Campaigns" })
#ApiResponses(value = { #ApiResponse(code = 200,
message = "OK",
response = XCampaign.class),
#ApiResponse(code = 401,
message = "Unauthorized",
response = HttpErrorResponse.class),
#ApiResponse(code = 400,
message = "Bad Request",
response = HttpErrorResponse.class),
#ApiResponse(code = 404,
message = "Not Found",
response = HttpErrorResponse.class),
#ApiResponse(code = 500,
message = "Internal Server Error",
response = HttpErrorResponse.class) })
Basically, I want to have the following responses documented in Swagger:
On 200:
{
'campaign': {
...
},
links: {
...
}
}
On 4xx or 5xx:
{
error: {
...
}
}
The problem I am having is the "nested" component. If I list the class itself as the Response class, it is obviously not given back as nested JSON. If I try to nest it however, like follows:
public class HttpErrorResponse {
private ErrorResponse error;
private class ErrorResponse {
public final String code;
public final String message;
public ErrorResponse(String code, String message) {
this.code = code;
this.message = message;
}
}
}
Then the Swagger Docs show an empty Object:
HttpErrorResponse {}
I've also taken a look at Spring HATEOAS, and think that I will be able to achieve the 2xx Response type using that, how can I achieve what I want in general with nested types?
Add something like this to your Docket
.alternateTypeRules(
newRule(typeResolver.resolve(DeferredResult.class,
typeResolver.resolve(ResponseEntity.class, WildcardType.class)),
typeResolver.resolve(WildcardType.class)))
Which substitutes DeferredResult<ResponseEntity<T>> with T generically, for more info see the useful guid for how to configure Swagger here.
Also import all required lib, including the one which is tricky to figure out:
import static springfox.documentation.schema.AlternateTypeRules.*;

Categories

Resources