In Quarkus, it seem that the entity returned by an exception mapper get wrapped in another entity.
Give an JAX-RS exception mapper like:
#Provider
public class WebhookExceptionMapper implements ExceptionMapper<WebhookException> {
#Override
public Response toResponse(final WebhookException e) {
return Response.status(e.getError().getCode().getStatus())
.entity(Entity.entity(e.getError(), MediaType.APPLICATION_JSON))
.build();
}
}
I get the following error response:
{
"entity": {
"code": "SOME_ERROR_CODE",
"msg": "Error message"
},
"variant": {
"language": null,
"mediaType": {
"type": "application",
"subtype": "json",
"parameters": {},
"wildcardType": false,
"wildcardSubtype": false
},
"encoding": null,
"languageString": null
},
"annotations": [],
"mediaType": {
"type": "application",
"subtype": "json",
"parameters": {},
"wildcardType": false,
"wildcardSubtype": false
},
"language": null,
"encoding": null
}
I would like the following to be returned:
{
"code": "SOME_ERROR_CODE",
"msg": "Error message"
}
Is that possible?
As is seen by looking at the package name, the javax.ws.rs.client.Entity class is only meant to be used on the client side. On the server side, you don't need to use it. What you are actually seeing is the Entity object being serialized, not the error.
If you want to set the type, just use the type() method on the Response.ResponseBuilder (that you get back from calling Response.status()). And to set the body just use the entity() method.
return Response.status(e.getError().getCode().getStatus())
.entity(e.getError())
.type(MediaType.APPLICATION_JSON)
.build();
Related
I've created two lambda functions (one Java, one Python) and added both of them to an ALB.
The code of the functions just converts the event to JSON and returns it:
public APIGatewayV2HTTPResponse handleRequest(APIGatewayV2HTTPEvent event, Context context) {
logger.info("path="+event.getRawPath());
APIGatewayV2HTTPResponse response = new APIGatewayV2HTTPResponse();
response.setStatusCode(200);
response.setBody(new ObjectMapper().writeValueAsString(event));
When I call the ALB for the Python lambda, I get
{
"requestContext": {
"elb": {
"targetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:821844782278:targetgroup/lambda-fu7bq1bfvaetnsi28l5g/3440f9a769be4e61"
}
},
"httpMethod": "GET",
"path": "/test",
"queryStringParameters": {},
"headers": {
"accept": "*/*",
"host": "core-external-dev-135473791.us-west-2.elb.amazonaws.com",
"user-agent": "curl/7.85.0",
"x-amzn-trace-id": "Root=1-63a5d7ad-1aadf14e6ecfb0e733ba868d",
"x-forwarded-for": "98.167.119.9",
"x-forwarded-port": "443",
"x-forwarded-proto": "https"
},
"body": "",
"isBase64Encoded": false
}
However, for the Java lambda I get
{
"version": null,
"routeKey": null,
"rawPath": null,
"rawQueryString": null,
"cookies": null,
"headers": {
"accept": "*/*",
"host": "core-external-dev-135473791.us-west-2.elb.amazonaws.com",
"user-agent": "curl/7.85.0",
"x-amzn-trace-id": "Root=1-63a5d77d-457cc4773e94430d0d64d668",
"x-forwarded-for": "98.167.119.9",
"x-forwarded-port": "443",
"x-forwarded-proto": "https"
},
"queryStringParameters": {},
"pathParameters": null,
"stageVariables": null,
"body": "",
"isBase64Encoded": false,
"requestContext": {
"routeKey": null,
"accountId": null,
"stage": null,
"apiId": null,
"domainName": null,
"domainPrefix": null,
"time": null,
"timeEpoch": 0,
"http": null,
"authorizer": null,
"requestId": null
}
}
So while the headers are there, there's nothing else related to the request.
Ah found the problem, i need to change the class of the event when i switched the code from function URL to ALB ( to ApplicationLoadBalancerRequestEvent )
I am trying to create one mapping.json under the mappings folder with multiple stubs as below. But I am facing the following error
Wiremock: v2.5.1 (standalone)
Mapping.json file looks,
[
{
"scenarioName": "Savings account Stub",
"request": {
"url": "/ws/*****",
"method": "POST",
"bodyPatterns" : [{
"contains" : "AccountRequest"
}
]
},
"response": {
"status": 200,
"bodyFileName": "******"
}
},
{
"scenarioName": "Current account Stub",
"request": {
"method": "POST",
"url": "/ws/*****",
"bodyPatterns": [
{
"contains": "AccountListRequest"
}
]
},
"response": {
"status": 200,
"bodyFileName": "******"
}
}]
Error:
Exception in thread "main" wiremock.com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of com.github.tomakehurst.wiremock.stubbing.StubMapping out of START_ARRAY token
Is there any possibility to create multiple stubs for the same URL in single mapping file? Can anyone tell me what is the exact issue?
Looking at the stubbing documentation, I think you want your mappings.json to look like...
{
"mappings": [
{
"scenarioName": "foo",
"request": {},
"response": {}
}, {
"request": {}
}
],
"importOptions": {
"duplicatePolicy": "IGNORE",
"deleteAllNotInImport": true
}
}
You'd then want to make a POST request to /__admin/mappings/import with your mappings.json as the request body. The reason for this is that I believe multiple mappings in a single file are only supported via the import option.
In my play framework application, I have registered APIs in route file as:
POST /api/rmt-create-request controllers.Api.CreateRMTRequestForm
On action of controller, I am using following code to access formData submitted with form submit as :
public Result CreateRMTRequestForm()
{
Map<String, String[]> params = request().body().asMultipartFormData().asFormUrlEncoded();
Its working fine as API when I submit the form with forntend application.
I am trying to create APIs documentation with swagger.ui in which within swagger.json file I have written following JSON data.
"paths": {"/api/rmt-create-request": {
"post": {
"tags": [
"RMT APIs"
],
"description" : "Return newly created request data",
"operationId": "create-new-rmt-request",
"consumes": ["application/x-www-form-urlencoded"],
"parameters": [
{
"name": "rootNodeName",
"in": "formData",
"description": "Root node class name for item",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/rmt-request-data"
}
}
}
},
"default": {
"$ref": "#/components/responses/default"
}
}
}
},
While inspecting RequestHeader data, its not showing content-Type property with value 'multipart/form-data' as well as formData are not attached, which makes controller to throw null exception.
Can anyone help whats missing in swagger.json file ?
You are mixing OpenAPI 2.0 and 3.0 syntax.
In OpenAPI 3.0, request body (including form data) is defined using the requestBody keyword instead of in: formData parameters.
Also, OAS3 does not use consumes. The media types consumed by the operation are specified inside the requestBody.
"paths": {
"/api/rmt-create-request": {
"post": {
"tags": [
"RMT APIs"
],
"description": "Return newly created request data",
"operationId": "create-new-rmt-request",
"requestBody": {
"content": {
"multipart/form-data": { // or "application/x-www-form-urlencoded" - depending on what you need
"schema": {
"type": "object",
"properties": {
"rootNodeName": {
"type": "string",
"description": "Root node class name for item"
}
}
}
}
}
}
}
}
}
More information: Describing Request Body
I have a simple REST API for which I am just returning Resonse.ok().build(), since the body of the function is asynchronous
I was expecting an empty response with a 200 http status code, but instead I got a full description of what seems to be the Response calls as entity.
What did I do wrong?
Here is the json response that I received from my API call
{
"context": {
"headers": {},
"entity": null,
"entityType": null,
"entityAnnotations": [],
"entityStream": {
"committed": false,
"closed": false
},
"length": -1,
"language": null,
"location": null,
"committed": false,
"mediaType": null,
"allowedMethods": [],
"links": [],
"entityTag": null,
"stringHeaders": {},
"lastModified": null,
"date": null,
"acceptableMediaTypes": [
{
"type": "*",
"subtype": "*",
"parameters": {},
"quality": 1000,
"wildcardType": true,
"wildcardSubtype": true
}
],
"acceptableLanguages": [
"*"
],
"entityClass": null,
"responseCookies": {},
"requestCookies": {},
"lengthLong": -1
},
"status": 200,
"length": -1,
"language": null,
"location": null,
"metadata": {},
"cookies": {},
"mediaType": null,
"allowedMethods": [],
"links": [],
"statusInfo": "OK",
"entityTag": null,
"stringHeaders": {},
"entity": null,
"lastModified": null,
"date": null,
"headers": {}
}
the REST api looks like this
#RestController
#RequestMapping("/accountworkers")
#Api(value = "/updater")
public class AccountUpdater {
private static Logger LOGGER = LoggerFactory.getLogger(AccountUpdater.class);
#Autowired
private AccountUpdaterController auController;
#RequestMapping(value = "/updater", method = RequestMethod.GET)
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response runUpdater() {
LOGGER.info("Running account updater");
auController.doAccountUpdateAsync();
return Response.ok().build();
}
}
You are trying to mix JAX-RS and Spring MVC. These are not the same thing and are not compatible. By returning Response, which Spring MVC doesn't recognize, it it serializing it like it would any other entity. If you are using Spring MVC, then you want to be using ResponseEntity.
return ResponseEntity.ok().build()
If you are using Spring MVC, you should remove any dependencies for Jersey/JAX-RS so you don't get confused as to what you can and cannot use.
Aside, #Produces is also for JAX-RS. For Spring MVC, you are supposed to add the produces inside the #RequestMapping annotation.
#RequestMapping(value="/", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
Spring, als always, provides very useful defaults to handle Validation errors. But sometimes it looks difficult to customize those. In my case I have a custom validation that uses a javascript function to validate a field in a domain object. The default validation error produces 4 message codes that use the object name, the field name, the field type and the validation type. So far so good. But I would like to add an additional code that contains the name of the js-function as a component. How could I do that?
Or more general my question is: where do I find a documentation of the way Spring builds the default error messages, and how they can be manipulated.
In my case I get an output like:
{
"timestamp": 1457092927829,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.web.bind.MethodArgumentNotValidException",
"errors": [
{
"codes": [
"JSValidated.order.validFrom",
"JSValidated.validFrom",
"JSValidated.java.time.ZonedDateTime",
"JSValidated"
],
"arguments": [
{
"codes": [
"order.validFrom",
"validFrom"
],
"arguments": null,
"defaultMessage": "validFrom",
"code": "validFrom"
},
"checkOrder",
"static/OrderValidator.js"
],
"defaultMessage": "validation checkValidFrom failed",
"objectName": "order",
"field": "validFrom",
"rejectedValue": 1196586930,
"bindingFailure": false,
"code": "JSValidated"
},
{
"codes": [
"NotEmpty.order.id",
"NotEmpty.id",
"NotEmpty.java.lang.String",
"NotEmpty"
],
"arguments": [
{
"codes": [
"order.id",
"id"
],
"arguments": null,
"defaultMessage": "id",
"code": "id"
}
],
"defaultMessage": "may not be empty",
"objectName": "order",
"field": "id",
"rejectedValue": null,
"bindingFailure": false,
"code": "NotEmpty"
}
],
"message": "Validation failed for object='order'. Error count: 2",
"path": "/order"
}
How can I add or change the codes? How can I add or change the list of arguments? Where is all the stuff documented?
You can use a global exception handler using #ExceptionHandler
You can define which exceptions should be handled. You have access to the thrown exception, which contains also the validation errors.
Create your own error class, that contains the properties you want to return.
Map the validation error into your error object and return it along with the HTTP status of your choice.
BindingException is one Exception I got from validation and the handler looks like this :
#ExceptionHandler(BindException.class)
#ResponseBody
public ResponseEntity<Object> handle(HttpServletRequest req, BindException ex) {
ExceptionResponse response = new ExceptionResponse(ex);
return new ResponseEntity<>(response, HttpStatus.EXPECTATION_FAILED);
}
And the error class ExceptionResponse :
#JsonInclude(Include.NON_NULL)
#JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
public static class ExceptionResponse{
String exception;
String message;
String trace;
public ExceptionResponse(Exception exception) {
super();
this.exception = exception.getClass().getName();
this.message = exception.getMessage();
this.trace = Arrays.toString(exception.getStackTrace());
}
This is a json serialization of the result of method getErrorAttributes(RequestAttributes requestAttributes,
boolean includeStackTrace) of class
org.springframework.boot.autoconfigure.web.DefaultErrorAttributes.
This class can be extended to add additional properties.
Codes and messages are added by validators, do if you'd like to change them you need to customize validators used.