OData WebApi 2 Error Handling - exception

In my web service, I override the ExceptionHandler, but it's not clear to me how you would format the exception to fit the OData Error standard. Perhaps i'm approaching it wrong since I can't find any examples online.
From my understanding, with web api 2 there is a concept of global exception handling where you use a custom ExceptionHandler to handle any exceptions thrown in the service. The Exception is still expected to update the ExceptionContext.Result with a new IHttpActionResult(). How do you format the data you input into IHttpActionResult to format to OData Error.
Below is a snippet of the ExceptionHandler, and I'm stuck on how you would override the context.Result with the correct OData HttpResponse message.
public class CustomExceptionHandler: ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
HttpResponseMessage msg = context.Request.CreateErrorResponse(HttpStatusCode.NotFound, new ODataError
{
ErrorCode = context.Exception.Message,
Message = context.Exception.InnerException.Message,
InnerError = new ODataInnerError
{
Message = context.Exception.InnerException.Message
}
});
context.Result = //How do you wrap the OData HttpResponseMessage into a IHttpActionResult
}
}
Any Advice Appreciated,
Thanks,
D

context.Result = new System.Web.Http.Results.ResponseMessageResult(msg);

Related

Spring AMQP RPC consumer and throw exception

I have a consumer (RabbitListner) in RPC mode and I would like to know if it is possible to throw exception that can be treated by the publisher.
To make more clear my explication the case is as follow :
The publisher send a message in RPC mode
The consumer receive the message, check the validity of the message and if the message can not be take in count, because of missing parameters, then I would like to throw Exception. The exception can be a specific business exception or a particular AmqpException but I want that the publisher can handle this exception if it is not go in timeout.
I try with the AmqpRejectAndDontRequeueException, but my publisher do not receive the exception, but just a response which is empty.
Is it possible to be done or may be it is not a good practice to implement like that ?
EDIT 1 :
After the #GaryRussel response here is the resolution of my question:
For the RabbitListner I create an error handler :
#Configuration
public class RabbitErrorHandler implements RabbitListenerErrorHandler {
#Override public Object handleError(Message message, org.springframework.messaging.Message<?> message1, ListenerExecutionFailedException e) {
throw e;
}
}
Define the bean into a configuration file :
#Configuration
public class RabbitConfig extends RabbitConfiguration {
#Bean
public RabbitTemplate getRabbitTemplate() {
Message.addWhiteListPatterns(RabbitConstants.CLASSES_TO_SEND_OVER_RABBITMQ);
return new RabbitTemplate(this.connectionFactory());
}
/**
* Define the RabbitErrorHandle
* #return Initialize RabbitErrorHandle bean
*/
#Bean
public RabbitErrorHandler rabbitErrorHandler() {
return new RabbitErrorHandler();
}
}
Create the #RabbitListner with parameters where rabbitErrorHandler is the bean that I defined previously :
#Override
#RabbitListener(queues = "${rabbit.queue}"
, errorHandler = "rabbitErrorHandler"
, returnExceptions = "true")
public ReturnObject receiveMessage(Message message) {
For the RabbitTemplate I set this attribute :
rabbitTemplate.setMessageConverter(new RemoteInvocationAwareMessageConverterAdapter());
When the messsage threated by the consumer, but it sent an error, I obtain a RemoteInvocationResult which contains the original exception into e.getCause().getCause().
See the returnExceptions property on #RabbitListener (since 2.0). Docs here.
The returnExceptions attribute, when true will cause exceptions to be returned to the sender. The exception is wrapped in a RemoteInvocationResult object.
On the sender side, there is an available RemoteInvocationAwareMessageConverterAdapter which, if configured into the RabbitTemplate, will re-throw the server-side exception, wrapped in an AmqpRemoteException. The stack trace of the server exception will be synthesized by merging the server and client stack traces.
Important
This mechanism will generally only work with the default SimpleMessageConverter, which uses Java serialization; exceptions are generally not "Jackson-friendly" so can’t be serialized to JSON. If you are using JSON, consider using an errorHandler to return some other Jackson-friendly Error object when an exception is thrown.
What worked for me was :
On "serving" side :
Service
#RabbitListener(id = "test1", containerFactory ="BEAN CONTAINER FACTORY",
queues = "TEST QUEUE", returnExceptions = "true")
DataList getData() {
// this exception will be transformed by rabbit error handler to a RemoteInvocationResult
throw new IllegalStateException("mon expecion");
//return dataHelper.loadAllData();
}
On "requesting" side :
Service
public void fetchData() throws AmqpRemoteException {
var response = (DataList) amqpTemplate.convertSendAndReceive("TEST EXCHANGE", "ROUTING NAME", new Object());
Optional.ofNullable(response)
.ifPresentOrElse(this::setDataContent, this::handleNoData);
}
Config
#Bean
AmqpTemplate amqpTemplate(ConnectionFactory connectionFactory, MessageConverter messageConverter) {
var rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(messageConverter);
return rabbitTemplate;
}
#Bean
MessageConverter jsonMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.registerModule(new JavaTimeModule());
var jsonConverter = new Jackson2JsonMessageConverter(objectMapper);
DefaultClassMapper classMapper = new DefaultClassMapper();
Map<String, Class<?>> idClassMapping = Map.of(
DataList.class.getName(), DataList.class,
RemoteInvocationResult.class.getName(), RemoteInvocationResult.class
);
classMapper.setIdClassMapping(idClassMapping);
jsonConverter.setClassMapper(classMapper);
// json converter with returned exception awareness
// this will transform RemoteInvocationResult into a AmqpRemoteException
return new RemoteInvocationAwareMessageConverterAdapter(jsonConverter);
}
You have to return a message as an error, which the consuming application can choose to treat as an exception. However, I don't think normal exception handling flows apply with messaging. Your publishing application (the consumer of the RPC service) needs to know what can go wrong and be programmed to deal with those possibilities.

RestClientException: Could not extract response. no suitable HttpMessageConverter found

Using the curl command:
curl -u 591bf65f50057469f10b5fd9:0cf17f9b03d056ds0e11e48497e506a2 https://backend.tdk.com/api/devicetypes/59147fd79e93s12e61499ffe/messages
I am getting a JSON response:
{"data":[{"device":"18SE62","time":1494516023,"data":"3235","snr":"36.72",...
I save the response on a txt file and parse it using jackson, and everything is fine
ObjectMapper mapper = new ObjectMapper();
File f = new File(getClass().getResource
("/result.json").getFile());
MessageList messageList = mapper.readValue(f, MessageList.class);
and I assume I should get the same result using RestTemplate but that's not the case
RestTemplate restTemplate = new RestTemplate();
MessageList messageList =
restTemplate.getForObject("http://592693f43c87815f9b8145e9:f099c85d84d4e325a2186c02bd0caeef#backend.tdk.com/api/devicetypes/591570373c87894b4eece34d/messages", MessageList.class);
I got an error instead
Exception in thread "main" org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.tdk.domain.backend.MessageList] and content type [text/html;charset=iso-8859-1]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:109)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:655)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:287)
at com.tdk.controllers.restful.client.RestTemplateExample.main(RestTemplateExample.java:27)
I tried to set the contentType:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
MessageList messageList =
restTemplate.getForObject(url, entity, MessageList.class);
but then I got a compilation error
The method getForObject(String, Class<T>, Object...) in the type RestTemplate is not applicable for the arguments (String, HttpEntity<String>,
Class<MessageList>)
I also tried to add a the Jackson Message converter
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
//Add the Jackson Message converter
messageConverters.add(new MappingJackson2HttpMessageConverter());
//Add the message converters to the restTemplate
restTemplate.setMessageConverters(messageConverters);
MessageList messageList =
restTemplate.getForObject(url, MessageList.class);
But then I got this error:
Exception in thread "main" org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.tdk.domain.backend.MessageList] and content type [text/html;charset=iso-8859-1]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:109)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:655)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:287)
at com.tdk.controllers.restful.client.RestTemplateExample.main(RestTemplateExample.java:51)
I also tried adding the class
#Configuration
#EnableWebMvc
public class MvcConf extends WebMvcConfigurationSupport {
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(converter());
addDefaultHttpMessageConverters(converters);
}
#Bean
MappingJackson2HttpMessageConverter converter() {
MappingJackson2HttpMessageConverter converter
= new MappingJackson2HttpMessageConverter();
return converter;
}
}
but I got the error:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.tdk.domain.backend.MessageList] and content type [text/html;charset=iso-8859-1]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:109)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:655)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:287)
The main problem here is content type [text/html;charset=iso-8859-1] received from the service, however the real content type should be application/json;charset=iso-8859-1
In order to overcome this you can introduce custom message converter. and register it for all kind of responses (i.e. ignore the response content type header). Just like this
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
//Add the Jackson Message converter
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
// Note: here we are making this converter to process any kind of response,
// not only application/*json, which is the default behaviour
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
messageConverters.add(converter);
restTemplate.setMessageConverters(messageConverters);
While the accepted answer solved the OP's original problem, most people finding this question through a Google search are likely having an entirely different problem which just happens to throw the same no suitable HttpMessageConverter found exception.
What happens under the covers is that MappingJackson2HttpMessageConverter swallows any exceptions that occur in its canRead() method, which is supposed to auto-detect whether the payload is suitable for json decoding. The exception is replaced by a simple boolean return that basically communicates sorry, I don't know how to decode this message to the higher level APIs (RestClient). Only after all other converters' canRead() methods return false, the no suitable HttpMessageConverter found exception is thrown by the higher-level API, totally obscuring the true problem.
For people who have not found the root cause (like you and me, but not the OP), the way to troubleshoot this problem is to place a debugger breakpoint on onMappingJackson2HttpMessageConverter.canRead(), then enable a general breakpoint on any exception, and hit Continue. The next exception is the true root cause.
My specific error happened to be that one of the beans referenced an interface that was missing the proper deserialization annotations.
UPDATE FROM THE FUTURE
This has proven to be such a recurring issue across so many of my projects, that I've developed a more proactive solution. Whenever I have a need to process JSON exclusively (no XML or other formats), I now replace my RestTemplate bean with an instance of the following:
public class JsonRestTemplate extends RestTemplate {
public JsonRestTemplate(
ClientHttpRequestFactory clientHttpRequestFactory) {
super(clientHttpRequestFactory);
// Force a sensible JSON mapper.
// Customize as needed for your project's definition of "sensible":
ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule())
.configure(
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter() {
public boolean canRead(java.lang.Class<?> clazz,
org.springframework.http.MediaType mediaType) {
return true;
}
public boolean canRead(java.lang.reflect.Type type,
java.lang.Class<?> contextClass,
org.springframework.http.MediaType mediaType) {
return true;
}
protected boolean canRead(
org.springframework.http.MediaType mediaType) {
return true;
}
};
jsonMessageConverter.setObjectMapper(objectMapper);
messageConverters.add(jsonMessageConverter);
super.setMessageConverters(messageConverters);
}
}
This customization makes the RestClient incapable of understanding anything other than JSON. The upside is that any error messages that may occur will be much more explicit about what's wrong.
I was having a very similar problem, and it turned out to be quite simple; my client wasn't including a Jackson dependency, even though the code all compiled correctly, the auto-magic converters for JSON weren't being included. See this RestTemplate-related solution.
In short, I added a Jackson dependency to my pom.xml and it just worked:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.1</version>
</dependency>
One way to debug this issue is to first take the response as a String.class and then use
Gson().fromJson(StringResp.body(), MyDTO.class)
It will most likely still fail, but this time it will throw the fields that caused the error in the first place. Following the modification, we can resume using the previous approach as before.
ResponseEntity<String> respStr = restTemplate.exchange(URL,HttpMethod.GET, entity, String.class);
Gson g = new Gson();
The following step will generate an error with the fields that are causing the problem.
MyDTO resp = g.fromJson(respStr.getBody(), MyDTO.class);
I don't have the error message with me, but it will point to the problematic field and explain why. Resolve those and try the previous approach again.
If the above response by #Ilya Dyoshin didn't still retrieve,
try to get the response into a String Object.
(For my self thought the error got solved by the code snippet by Ilya, the response retrieved was a failure(error) from the server.)
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
ResponseEntity<String> st = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class);
And Cast to the ResponseObject DTO (Json)
Gson g = new Gson();
DTO dto = g.fromJson(st.getBody(), DTO.class);
In my case #Ilya Dyoshin's solution didn't work: The mediatype "*" was not allowed.
I fix this error by adding a new converter to the restTemplate this way during initialization of the MockRestServiceServer:
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter =
new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(
Arrays.asList(
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_OCTET_STREAM));
restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
mockServer = MockRestServiceServer.createServer(restTemplate);
(Based on the solution proposed by Yashwant Chavan on the blog named technicalkeeda)
JN Gerbaux
You need to create your own converter and implement it before making a GET request.
RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
messageConverters.add(converter);
restTemplate.setMessageConverters(messageConverters);
This way you can get the object response using resttemplate and set contentType using MediaType.APPLICATION_JSON
public List<Employee> getListofEmployee()
{
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<List<Employee>> response = restTemplate.exchange("http://hello-server/rest/employees",
HttpMethod.GET,entity, new ParameterizedTypeReference<List<Employee>>() {});
return response.getBody(); //this returns List of Employee
}
Please add the shared dependency having jackson databind package . Hope this will clear the issue.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
In my case it was caused by the absence of the jackson-core, jackson-annotations and jackson-databind jars from the runtime classpath.
It did not complain with the usual ClassNothFoundException as one would expect but rather with the error mentioned in the original question.
Spring sets the default content-type to octet-stream when the response is missing that field. All you need to do is to add a message converter to fix this.
Other possible solution : I tried to map the result of a restTemplate.getForObject with a private class instance (defined inside of my working class). It did not work, but if I define the object to public, inside its own file, it worked correctly.
I was trying to use Feign, while I encounter same issue, As I understood HTTP message converter will help but wanted to understand how to achieve this.
#FeignClient(name = "mobilesearch", url = "${mobile.search.uri}" ,
fallbackFactory = MobileSearchFallbackFactory.class,
configuration = MobileSearchFeignConfig.class)
public interface MobileSearchClient {
#RequestMapping(method = RequestMethod.GET)
List<MobileSearchResponse> getPhones();
}
You have to use Customer Configuration for the decoder, MobileSearchFeignConfig,
public class MobileSearchFeignConfig {
#Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
#Bean
public Decoder feignDecoder() {
return new ResponseEntityDecoder(new SpringDecoder(feignHttpMessageConverter()));
}
public ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(new MappingJackson2HttpMessageConverter());
return new ObjectFactory<HttpMessageConverters>() {
#Override
public HttpMessageConverters getObject() throws BeansException {
return httpMessageConverters;
}
};
}
public class MappingJackson2HttpMessageConverter extends org.springframework.http.converter.json.MappingJackson2HttpMessageConverter {
MappingJackson2HttpMessageConverter() {
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.valueOf(MediaType.TEXT_HTML_VALUE + ";charset=UTF-8"));
setSupportedMediaTypes(mediaTypes);
}
}
}
In my case i was missing the No Args contructor
#Data
#AllArgsConstructor
#NoArgsConstructor
for those who are no using Lombok do add no args constructor in the mapping pojo
public ClassA() {
super();
// TODO Auto-generated constructor stub
}
also dont forget to add the Bean of Restemplate in main file if you are using the same
Infuriating problem right?
You just wanna get the result of the call, and you have a deSerialization error...that you have no clue where to look for.
Well, all is not lost.
If you change the type of call to String, you can get the JSON equivalent and then write a test to see why it is not serializing:
RestTemplate restTemplate = new RestTemplate();
String messageListString = restTemplate.getForObject("http://592693f43c87815f9b8145e9:f099c85d84d4e325a2186c02bd0caeef#backend.tdk.com/api/devicetypes/591570373c87894b4eece34d/messages", String.class);
Here is an example with an input param I used in my Kotlin project:
fun givenCUT_whenFetchingBillableItemsForAPastMonthWithoutBillingData_thenWeSucceedInGettingAnEmptyXmlResponse() {
val restTemplate = RestTemplate()
val uri = "http://localhost:$port/api/test/billing/xml/month/{month}/"
val params: MutableMap<String, String> = HashMap()
params["month"] = "2022-09-01"
val stringResponse = restTemplate.getForObject(uri, String::class.java, params)
assertNotNull(stringResponse)
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<bi:billableItems xmlns:bi=\"urn:blahblahblah\"/>\n", stringResponse)
}
If you step through that test, and harvest the actual JSON of your endpoint, you can then use a test like this, to pump it in, and see why Jackson or Gson is complains:
#Test
fun givenCUT_whenDeSerializingBEStateCorrectionsResponse_thenWeGetAnInstanceOfAListOfBillingOrdersSuccessfully() {
//Raw JSON harvested from BillingOrderControllerTest
val harvestedFEJsonBillingOrderList = "YOUR JSON Harvested from above goes here"
val mapper = ObjectMapper()
mapper.registerModule(JavaTimeModule())
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
val deSerBillingOrderList = mapper.readValue(harvestedFEJsonBillingOrderList, Array<BillingOrder>::class.java)
assertNotNull(deSerBillingOrderList)
assertEquals(1, deSerBillingOrderList.size)
}
The post is just as easy...the following is a snippet but you can see I finally gave up and commented out the serialization error part, and reverted to the String version and did the necessary with the test above in Jackson; I just this minute did that, and found 4 issues in the de-serialized JSON that Jackson explicitly reported on, and that I will fix. Then, I will revert the below to the typed version and it should have solved the problem:
try {
val result = restTemplate!!.postForEntity(uri, billingOrders, String::class.java)
/* val result = restTemplate!!.postForObject(
baseUrl,
billingOrders,
ResponseEntity<List<BillingOrder>>::class.java)*/
assertNotNull(result)
} catch (e: Exception) {
log.error("Failed restTemplate.postForObject with $e")
}

How to respond via an JsonResult from Asp.Net Core middleware?

I would like to respond via a JsonResult from a piece of Asp.Net Core middleware but it's not obvious how to accomplish that. I have googled around alot but with little success. I can respond via a JsonResult from a global IActionFilter by setting the ActionExecutedContext.Result to the JsonResult and that's cool. But in this case I want to effectively return a JsonResult from my middleware. How can that be accomplished?
I framed the question with regard to the JsonResult IActionResult but ideally the solution would work for using any IActionResult to write the response from the middleware.
Middleware is a really low-level component of ASP.NET Core. Writing out JSON (efficiently) is implemented in the MVC repository. Specifically, in the JSON formatters component.
It basically boils down to writing JSON on the response stream. In its simplest form, it can be implemented in middleware like this:
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
// ...
public async Task Invoke(HttpContext context)
{
var result = new SomeResultObject();
var json = JsonConvert.SerializeObject(result);
await context.Response.WriteAsync(json);
}
For others that may be interested in how to return the output of a JsonResult from middleware, this is what I came up with:
public async Task Invoke(HttpContext context, IHostingEnvironment env) {
JsonResult result = new JsonResult(new { msg = "Some example message." });
RouteData routeData = context.GetRouteData();
ActionDescriptor actionDescriptor = new ActionDescriptor();
ActionContext actionContext = new ActionContext(context, routeData, actionDescriptor);
await result.ExecuteResultAsync(actionContext);
}
This approach allows a piece of middleware to return output from a JsonResult and the approach is close to being able to enable middleware to return the output from any IActionResult. To handle that more generic case the code for creating the ActionDescriptor would need improved. But taking it to this point was sufficient for my needs of returning the output of a JsonResult.
As explained by #Henk Mollema, I have also made use of Newtonsoft.Json JsonConvert class to serialize the object into JSON through SerializeObject method. For ASP.NET Core 3.1 I have used JsonConvert inside the Run method. Following solution worked for me:
Startup.cs
using Newtonsoft.Json;
// ...
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Run(async context =>
{
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(new
{
message = "Yay! I am a middleware"
}));
});
}
}
As of ASP.NET Core 5.0, you can use WriteAsJsonAsync to write JSON to the response stream. This method handles serialization and setting the content type header appropriately.
For example
using Microsoft.AspNetCore.Http;
// ..
public async Task InvokeAsync(HttpContext context)
{
var result = new SomeResultObject();
await context.Response.WriteAsJsonAsync(result);
}

Multiple retrofit response and API's error handling

I'm using Retrofit and Gson to get data from REST service. It's works perfectly, but only when API not returning error. Normally, API return list of objects (as json), but when error occurring, API return a single error object.
I'm trying to obtain Call<List<Link>> but when API error occurred I'm getting Gson parse error (Expected BEGIN_OBJECT but was BEGIN_ARRAY).
I've only one solution: retrieving single string and then in enqueue's onResponse() try to parse response, but here is a lot of boilerplate code.
Is there any better solution of this problem? How to handle API's errors?
You can use next construction:
#Override
public void onResponse(Call<YourModel> call, Response<YourModel> response) {
if (response.isSuccessful()) {
// Do awesome stuff
} else if (response.code == 401) {
// Handle unauthorized
} else {
// Handle other responses
}
}
Full answer: How to get Retrofit success responce status codes
EDIT
For your case you can use Call<JsonElement> as response type and parse it in Response:
call.enqueue(new Callback<JsonElement>() {
#Override
public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
if(response.isSuccessful()){
JsonElement jsonElement = response.body();
if(jsonElement.isJsonObject()){
JsonObject objectWhichYouNeed = jsonElement.getAsJsonObject();
}
// or you can use jsonElement.getAsJsonArray() method
//use any json deserializer to convert to your class.
}
else{
System.out.println(response.message());
}
}
#Override
public void onFailure(Call<JsonElement> call, Throwable t) {
System.out.println("Failed");
}
});

How to get response in JSON format using #ExceptionHandler in Spring MVC

I am new to this #ExceptionHandler. I need to return response in JSON format if there is any exception. My code is returning response in JSON format if the operation is successful. But when any exception is thrown it is return HTML response as I have used #ExceptionHandler.
Value and reason in #ResponseStatus is coming properly but in HTML. How can I can change it to a JSON response? Please help.
In my controller class i have this methods:
#RequestMapping(value = "/savePoints", method = RequestMethod.POST, consumes = "application/json", produces = "application/json;charset=UTF-8")
public #ResponseBody
GenericResponseVO<TestResponseVO> saveScore(
#RequestBody(required = true) GenericRequestVO<TestVO> testVO) {
UserContext userCtx = new UserContext();
userCtx.setAppId("appId");
return gameHandler.handle(userCtx, testVO);
}
Exception handling method:
#ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Error in the process")
#ExceptionHandler(Exception.class)
public void handleAllOtherException() {
}
You can annotate the handler method with #ResponseBody and return any object you want and it should be serialized to JSON (depending on your configuration of course). For instance:
public class Error {
private String message;
// Constructors, getters, setters, other properties ...
}
#ResponseBody
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(MethodArgumentNotValidException.class)
public Error handleValidationException(MethodArgumentNotValidException e) {
// Optionally do additional things with the exception, for example map
// individual field errors (from e.getBindingResult()) to the Error object
return new Error("Invalid data");
}
which should produce response with HTTP 400 code and following body:
{
"message": "Invalid data"
}
Also see Spring JavaDoc for #ExceptionHandler which lists possible return types, one of which is:
#ResponseBody annotated methods (Servlet-only) to set the response content. The return value will be converted to the response stream using message converters.
Replace
#ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Error in the process")
by
#ResponseStatus(value = HttpStatus.NOT_FOUND)
the 'reason' attribute force html render!
I've waste 1 day on that.....