I have to test Jersey 1.19 with jersey-test-framework-grizzly2. There is configuration class with registered REST endpoint and exception mapper class:
public class ConfiguredMyServiceTest extends JerseyTest {
#Override
protected int getPort(int defaultPort) {
return 8080;
}
public static class AppConfig extends DefaultResourceConfig {
public AppConfig() {
getSingletons().add(new ExceptionMapperProvider());
getSingletons().add(new MyService());
}
}
#Override
public WebAppDescriptor configure() {
return new WebAppDescriptor.Builder()
.initParam(WebComponent.RESOURCE_CONFIG_CLASS,
AppConfig.class.getName())
.build();
}
}
When I execute/test REST endpoint which returns HTTP status 200 it works well.
If exception is thrown, exception mapper handles it well and forms return object javax.ws.rs.core.Response with error code:
#Provider
#Singleton
public class ExceptionMapperProvider implements ExceptionMapper<Exception>{
#Override
public Response toResponse(final Exception exception){
return Response.status(HttpStatusCodes.STATUS_CODE_SERVER_ERROR).entity(new BasicResponse(InternalStatus.UNHANDLED_EXCEPTION, exception.getMessage())).type(MediaType.APPLICATION_JSON).build();
}
}
However, I get
com.sun.jersey.api.client.UniformInterfaceException: POST http://localhost:8080/v1/my-service/ returned a response status of 401 Unauthorized
when I try to assert Response in my JUnit tests. How to get well formed response instead of UniformInterfaceException?
Changed expected class type to com.sun.jersey.api.client.ClientResponse
protected ClientResponse executeGet(String path){
WebResource resource = resource().path(path);
Builder builder = resource.header("Content-Type", "application/json;charset=UTF-8");
return builder.get(ClientResponse.class);
}
Now it is able to handle various HTTP statuses and parse underlying response:
ClientResponse clientResponse = executeGet("/info");
if (clientResponse.getStatus() == 200)
CustomResponseType customResponse = clientResponse.getEntity(CustomResponseType.class);
Related
I am implementing a filter to find out any json mapping or parsing issues using ContainerRequestFilter interface. It logs error for all json parsing or mapping issues but for request which doesn't have any json validation issues it throws 400 Bad request. If I remove the filter from the code it works fine for proper json request. Below is the code for the same ..
#Provider
public class AppFilter implements ContainerRequestFilter {
private static final Logger LOG = Logger.getLogger(AppFilter.class);
#Inject
ObjectMapper objectMapper;
#Override
public void filter(ContainerRequestContext context) throws IOException {
try(InputStream stream = new CloseShieldInputStream(context.getEntityStream())) {
objectMapper.readValue(stream,AppRequest.class);
}catch(Exception ex) {
LOG.error(ExceptionUtils.getRootCauseMessage(ex));
}
}
}
#Path("/app")
public class MatchResource {
#POST
#Produces(MediaType.APPLICATION_JSON)
public Uni<Response> processRequest(AppRequest request,
#Context SecurityContext securityContext) {
String tranId = UUID.randomUUID().toString();
ZonedDateTime zonedDateTime = ZonedDateTime.now(TIME_ZONE_ID);
return Uni.createFrom().item(request)
.onItem().invoke(() -> {
Set<ConstraintViolation<AppRequest>> violations = validator.validate(request);
if (!violations.isEmpty()) {
throw new RequestViolationsException(violations,
request.header.applicationReferenceId);
}
}).onFailure()
.transform(t -> LOG.error(ExceptionUtils.getRootCauseMessage(t)));
}
Let me know if I am missing anything in this ..
I need to be able to log http request & response with header & body.
Currently I have an Interceptor and a #AroundInvoke advise which logs the body.
But is it possible to also get hold of the http headers? In some way?
I have included some code snippets below
The project is using Resteasy, ie. having quarkus-rest-client dependency in pom
//#LoggedClient not included in example
#Priority(0)
#Interceptor
public class LoggingInterceptor {
#AroundInvoke
Object logInvocation(InvocationContext ctx) throws Exception {
logRequest(ctx);
try {
Object response = ctx.proceed();
logResponse(response);
return response;
} catch (WebApplicationException e) {
logError(e);
return e;
}
}
private void logError(WebApplicationException e) {/* impl */}
private void logResponse(Object response) {/* impl */}
private void logRequest(InvocationContext ctx) {/* impl which logs httpheader & body(class,method & request) */}
}
#Produces(APPLICATION_JSON)
#Consumes(APPLICATION_JSON)
#Path("/offer")
//#LoggedClient not included in example
public class MyResource {
#Inject
#RestClient
MyClient client;
#POST
#Produces(APPLICATION_JSON)
public Response create(MyRequest req) {
MyResponse response = client.create(req);
return Response.ok().entity(response).build();
}
}
#Path("/somepath")
#RegisterRestClient
public interface MyClient {
#POST
#Produces(MediaType.APPLICATION_JSON)
#Path("/create")
MyResponse create(MyRequest req);
}
I am consuming API which has to type of response success response 200 and Bad response 400 both of them has parameters inside their response body but the problem is am not able to get the bad response parameters it throws this exception
public ResponseEntity<String> balanceInquiry(BalanceInquiryRequestDto balanceInquiryRequestDto) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
httpHeaders.set("API-KEY", "5d6f54d4");
HttpEntity<BalanceInquiryRequestDto> request = new HttpEntity<BalanceInquiryRequestDto>(balanceInquiryRequestDto , httpHeaders);
ResponseEntity<String> postForEntity =
restTemplate.postForEntity(uri , request, String.class);
return postForEntity;
}
it is working good when the response is ok 200
I created a small spring boot project to showcase what you can do.
First a simple service that will give us an error when called:
#RestController
public class Endpoint {
#GetMapping("/error")
public ResponseEntity createError() {
ErrorDetails errorDetails = new ErrorDetails("some error message");
return ResponseEntity.status(400).body(errorDetails);;
}
}
The error details which you want to extract are similar to this in this example:
#AllArgsConstructor
#Getter
#Setter
#ToString
#EqualsAndHashCode
public class ErrorDetails {
private String errorMessage;
}
And then another endpoint with a client that calls the failing service. It returns the error details received:
#RestController
public class ClientDemo {
#Autowired
private RestTemplate restTemplate;
#GetMapping("/show-error")
public String createError() {
try{
return restTemplate.getForEntity("http://localhost:8080/error", String.class).getBody();
} catch(HttpClientErrorException | HttpServerErrorException ex) {
return ex.getResponseBodyAsString();
}
}
}
For sake of completion:
#SpringBootApplication
public class StackoverflowApplication {
public static void main(String[] args) {
SpringApplication.run(StackoverflowApplication.class, args);
}
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
When navigating to http://localhost:8080/show-error you see this:
{
"errorMessage": "some error message"
}
I want to use this code to receive http link with values:
#PostMapping(value = "/v1/notification")
public String handleNotifications(#RequestParam("notification") String itemid) {
// parse here the values
return "result successful result";
}
How I can return http code 200 - successful response?
And also for example if there is a code exception into code processing how can I return error 404?
If you are using spring:
#PostMapping(value = "/v1/notification")
public ResponseEntity handleNotifications(#RequestParam("notification") String itemid) {
// parse here the values
return ResponseEntity.ok().build();
//OR ResponseEntity.ok("body goes here");
}
If you use #RestController it should return 200 by default.
But anyway, you can set a particular response status by #ResponseStatus annotation (even if the methods returns void) or you can return a custom response by ResponseEntity.
EDIT: added error handling
For error handling, you can return a particular response entity:
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body("some body ");
or you can use #ExceptionHandler:
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public void handleError(Exception ex) {
// TODO: log exception
}
You can do it by annotating your method with #ResponseStatus using HttpStatus.OK (However it should be 200 by default), like this:
Some controller
#PostMapping(value = "/v1/notification")
#ResponseStatus(HttpStatus.OK)
public String handleNotifications(#RequestParam("notification") String itemid) throws MyException {
if(someCondition) {
throw new MyException("some message");
}
// parse here the values
return "result successful result";
}
Now, in order to return a custom code when handling a specific exception you can create a whole separate controller for doing this (you can do it in the same controller, though) which extends from ResponseEntityExceptionHandler and is annotated with #RestControllerAdvice and it must have a method for handling that specific exception as shown below:
Exception handling controller
#RestControllerAdvice
public class ExceptionHandlerController extends ResponseEntityExceptionHandler {
#ExceptionHandler(MyException.class)
protected ResponseEntity<Object> handleMyException(MyException ex, WebRequest req) {
Object resBody = "some message";
return handleExceptionInternal(ex, resBody, new HttpHeaders(), HttpStatus.NOT_FOUND, req);
}
}
You can do something like this:
#PostMapping(value = "/v1/notification")
public ResponseEntity<String> handleNotifications(
#RequestParam("notification") String itemid) {
// parse here the values
return new ResponseEntity<>("result successful result",
HttpStatus.OK);
}
I am testing an #RestController which has an API endpoint such as /api/dataobject. If the object (in JSON format) that is posted to this endpoint is missing some part of its meta data, the API should respond with a Http status of bad request (400).
When testing it through Postman, this works, however in my unit test where the controller is mocked it still returns a status 200.
The method in the RestController:
#RequestMapping("/api/dataobject")
public ResponseEntity postDataObject(#RequestBody final DataObject dataObject) throws InvalidObjectException {
if (!dataObjectValidator.validateDataObject(dataObject)) {
throw new InvalidObjectException("Data object was invalid: " + dataObject.toString());
}
return new ResponseEntity(HttpStatus.OK);
}
The InvalidObjectException is caught by a class annotated with #ControllerAdvice which extends ResponseEntityExceptionHandler and is handled as follows:
#ExceptionHandler(value = InvalidObjectException.class)
protected ResponseEntity<Object> handleInvalidObject(final InvalidObjectException exception, final WebRequest request) {
final String bodyOfResponse = exception.getMessage();
return handleExceptionInternal(exception, bodyOfResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
}
Now, the unit test class is as follows:
#RunWith(SpringRunner.class)
#WebMvcTest(DataObjectController.class)
public class DataObjectControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
private DataObjectController dataObjectController;
private final String uri = "/api/idataobject";
#Test
public void noAppName() throws Exception {
DataObject object = getDataObjectNoAppName();
final String body = new Gson().toJson(object);
given(dataObjectController.postDataObject(object)).willReturn(new ResponseEntity(HttpStatus.BAD_REQUEST));
mvc.perform(post(uri)
.content(body)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest());
}
}
Even though the object is invalid, and I've said that the given object would return a HttpStatus 400, I get a 200 status in return.
Clearly I'm missing something here, but what?