Related
I am using angular 8 at front end and java spring boot at back end while running the application I am getting this error. I am new to angular and I guess the error is because of response body from back end but I couldn't solve it.
I have tried the other answers but nothing seems to work.
auth.service.ts
// Method from server should return QueryResultsModel(items: any[], totalsCount: number)
// items => filtered/sorted result
findUsers(queryParams: QueryParamsModel): Observable<QueryResultsModel> {
const httpHeaders = new HttpHeaders();
httpHeaders.set('Content-Type', 'application/json');
return this.http.post<QueryResultsModel>(API_USERS_URL + '/use' + '/findUsers', queryParams, { headers: httpHeaders});
}
Web service java spring boot
// Get All User
#PostMapping(value = "/user/use/findUsers")
public ResponseEntity<List<User>> getAllUserFind() {
try {
return new ResponseEntity<>((List<User>)userService.getAllUser(), HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
You need to find out the exact cause of error by gracefully catching the error in your observable resolution. Use
return this.http.post<QueryResultsModel>(API_USERS_URL + '/use' + '/findUsers', queryParams, { headers: httpHeaders})
.pipe(catchError(error)=>{
console.log(error);
return of(null);
});
This will print out the exact error on your console. Remember to import Rx Operators.
import { of } from 'rxjs';
import { catchError } from 'rxjs/operators';
Metronic theme ?
It's an amazing Template.
QueryResultsModel is a Model
// fields
items: any[]; <---- This is where you should iterate
importedCount?: number;
activeCount?: number;
totalCount: number;
errorMessage: string;
Good Luck !
Using latest Spring Boot as of May 2018. I've created a 404 response like this.
#ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {
private final int errorId;
public NotFoundException(String errorMsg) {
super("-1," + errorMsg);
this.errorId = -1;
}
public NotFoundException(int errorId, String errorMsg) {
super(errorId + "," + errorMsg);
this.errorId = errorId;
}
public int getErrorId() {
return errorId;
}
}
The annotation #ResponseStatus(HttpStatus.NOT_FOUND) makes my NotFoundException appear like a 404 reponse like this
{
"timestamp":1527751944754,
"status":404,
"error":"Not Found",
"exception":"com.myapp.exception.NotFoundException",
"message":"1000,Could not find data for owner: 1234","path":"/resource/owner/1234"
}
I hoped that property "getErrorId" would appear in the response automatically, like this
{
"timestamp":1527751944754,
"status":404,
"error":"Not Found",
"exception":"com.myapp.exception.NotFoundException",
"message":"Could not find data for owner: 1234","path":"/resource/owner/1234",
"errorId": 1000
}
Is the a simply way (like an annotiation to the getErrorId method) of having the property "errorId" in the response?
You use #ControllerAdvice and #ExceptionHanlder in Spring. that is exception controller. In fact, you will make custom exception controller and define exception.
This is sample code for you :
#ControllerAdvice("your.package")
public class CommonExceptionHandler {
#ExceptionHandler(value = NoHandlerFoundException.class)
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public #ResponseBody ResponseEntity<?> setNotFoundException(Exception exception) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
// this is sample map. you will make your custom model and you use exception parameter.
Map<String, String> map = new HashMap<String, String>();
map.put("timestamp", String.valueOf(new Date().getTime()));
map.put("status", HttpStatus.NOT_FOUND.toString());
map.put("error", "Not Found");
map.put("exception", exception.getMessage());
map.put("message", "Could not find data for owner: 1234");
map.put("path", "/resource/owner/1234");
map.put("errorId", "1000");
String json = mapper.writeValueAsString(map);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(json);
}
}
what ever Byeon0gam told everything is fine, here i am going to show another way means little bit of difference in maintaining code.
We know already ,
we can handle exceptions in spring-rest by 4 ways:
1. Using ResponseEntity Class.
2. Using #ResponseStatus Annotation.
3. Using #ExceptionHandler() Annotation.
4. Return Error Representation instead of default HTML error Page.
By using Those we can handle Exceptions at Method or Class level only.
But, if you want to handle Globally means throughout application , please follow below steps.
Handling Global Exception:
To Handle all Exceptions in our applications ,
First we need to create a class, after we need to use #ControllerAdvice Annotation on top of a class. In that class body , we can handle the exceptions raised in our application.
In that Class , we will create Exception handling methods , on top of every method we will use #ExceptionHandler() annotation for navigating Exceptions and for Handling .
If any exception raises in our application , based on #ExceptionHandler(“argument”) annotation argument the exception hadling method will be invoked and remaining handling code will be excuted.
#ControllerAdvice
public class SpringRestGlobalExceptionHandler {
#ExceptionHandler(Exception.class)
public ResponseEntity<?> exceptionHandler(HttpServletRequest req, Exception e)
{
JSONObject obj =new JSONObject();
obj.put("msgTxt","Unknown Server Error, Please Contact Admin." );
obj.put("reqUrl", req.getRequestURI());
obj.put("stackTrace", e.toString());
obj.put("isErrorFlag", true);
obj.put("httpStatusCode", HttpStatus.OK.value());
gstcDaoi.saveExceptionOrErrorLog(prepareTGstcExceptionOrErrorLogObject(obj));
e.printStackTrace();
return new ResponseEntity<>(obj, HttpStatus.OK);
}
I create a function to download a CSV File. I will use that to download simple reports. I got the error below on Netbeans using Wildfly and JAX RS
RESTEASY002005: Failed executing POST /reports/downloadCSV/: org.jboss.resteasy.core.NoMessageBodyWriterFoundFailure: Could not find MessageBodyWriter for response object of type: java.io.FileWriter of media type: application/octet-stream
Here is my Code:
Controller
Update on ParametersClass
#POST
#Path("/downloadCSV")
#Produces("application/octet-stream")
public Response downloadCSV(ParametersClass param) {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
FileWriter fileWriter = null;
Date date = new Date();
try {
fileWriter = new FileWriter("MainReport_"+dateFormat.format(date)+".csv");
fileWriter.append(csvService.mainReport(dateFormat.parse(param.getStartDate()),dateFormat.parse(param.getEndDate())));
fileWriter.flush();
fileWriter.close();
ResponseBuilder response = Response.ok((Object) fileWriter);
response.header("Content-Disposition","attachment; filename=\"MainReport_"+dateFormat.format(date)+".csv\"");
return response.build();
} catch (ParseException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
}
The csvService returns a String like:
Column1,column2,column3
cellInfo1,cellInfo2,cellInfo3
,cellInfo2,cellInfo3
cellInfo1,,cellInfo3
cellInfo1,cellInfo2,
,,cellInfo3
I tried using a different #Produces => #Produces('text/csv') , #Produces('application/octet-stream')
If I remove the Annotation #Produces I got the following error:
RESTEASY002010: Failed to execute: javax.ws.rs.NotSupportedException: RESTEASY003200: Could not find message body reader for type: class com.xo.CSVFile of content type: application/x-www-form-urlencoded;charset=UTF-8
AJAX
var dateRange = new Object();
dateRange.startDate = '2017-07-20';
dateRange.endDate = '2017-08-10';
$.ajax({
type: 'POST',
url: appPath + '/api/reports/downloadCSV/',
data: JSON.stringify(dateRange),
async:true,
success: function(data) {
}
});
What I'm doing wrong ? Could you help to me please!
.
SOLUTION
Thanks to #albert-bos
1st. Check the link in the solution from #albert-bos below.
2nd: Check this link too
3rd:
Controller:
#POST
#Path("/downloadCSV")
#Produces("text/csv")
public List<LinkedHashMap<String, String>> downloadCSV(ParametersClass param) {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
return csvService.mainReport(dateFormat.parse(param.getStartDate()),dateFormat.parse(param.getEndDate()));
} catch (ParseException ex) {
return null;
}
}
MessageBodyWriter:
I create a class called CSVMessageBodyWritter (check the link) but I adpated the method writeTo:
#Override
public void writeTo(Object t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
CsvSchema schema = null;
CsvSchema.Builder schemaBuilder = CsvSchema.builder();
if(t!=null){
List<LinkedHashMap<String, String>> reportArray = (List<LinkedHashMap<String, String>>) t;
LinkedHashMap<String, String> headers = reportArray.get(0);
for (String col : headers.keySet()) {
schemaBuilder.addColumn(col);
}
schema = schemaBuilder.build().withLineSeparator("\r");
CsvMapper mapper = new CsvMapper();
mapper.writer(schema).writeValues(entityStream).writeAll(reportArray);
}
}
JAX-RS only supports a few Content-Types by default (also depending on implementation), like XML and JSON.
The idea of JAX-RS is that it will convert an object to a certain type (e.g. XML or JSON). This is so you can re-use the same object for XML and JSON, without knowing the output in your Controller. Then If you want something different like CSV, you need to define your own BodyWriter, see example here: http://www.javaprocess.com/2015/08/a-simple-csv-messagebodywriter-for-jax.html
The problem here is that your controller is to specific for CSV and isn't very flexible. You could put your output of csvService into an object and let the BodyWriter convert it into CSV.
JS
window.open("http://localhost:8080/xmlcompare-rs/xmlcompare/excelmisreport");
Rest
#GET
#Path("excelmisreport")
#Produces("application/vnd.ms-excel")
public Response getExcelReport() {
ResponseBuilder response = Response.ok((Object) file);
response.header("Content-Disposition",
"attachment; filename=MISReport.xls");
return response.build();
}
I don't have enough rep so can't add a comment for the answer of Albert Bos.
There is a PITFALL with that solution: you can suddenly run into the problem when you get empty csv file even if it should have a data. It happens because of result of getSize method.
I'm not sure which version of JAX-RS is supposed to be used in that example (in the article), but accordingly to the jax-rs documentation, result of getSize is ignored for JAX-RS 2.0, but for JAX-RS 1.X it seems to be used and "return 0" makes downloaded file empty. Make sure you return "-1". I've encountered it when tried to implement csv export in JIRA rest plugin (I guess it's based on first version of JAX-RS).
I'm using Spring MVC for add & update form.
Don't know why cannot redirect back to same form to show error message while submitted BindingResult (form validation, wrong input etc..) got any errors.
This happen when using PUT method(update things), not POST method (add things)...
Please anyone suggestion what should I do.
Thanks in advance.
Error in the browser I received:
HTTP Status 405 - HTTP method PUT is not supported by this URL
type Status report
messageHTTP method PUT is not supported by this URL
descriptionThe specified HTTP method is not allowed for the requested resource.
GlassFish Server Open Source Edition 4.1.1
Console nothing show up... just the log I make showing which field of input got error.
Here the methods inside controller:
//Add - no problem can show validation error message in the JSP form
#RequestMapping(value = "/userDetail", method = RequestMethod.POST)
public String saveUserDetail(#Valid UserDetail userDetail,
Errors errors) {
if (errors.getErrorCount() > 0) {
log.info("User attempt create user: " + userDetail.toString());
for (FieldError error : errors.getFieldErrors()) {
log.info(error.getField() + ": " + error.getDefaultMessage());
}
//redirect to same page to show error return infor (NotEmpty etc...)
return "/user/inputUserDetail"; //same structure for PUT method below, but not working show me 405 error
} else {
try {
userDetailService.addUserDetail(userDetail);
} catch (Exception e) {
log.error("Failed to add new UserDetail: " + userDetail.toString());
}
return "redirect:/user/userDetails";
}
}
//Update - need UserDetail model attribute
#RequestMapping(value = "/userDetail", method = RequestMethod.PUT)
public ModelAndView updateUserDetail(#Valid UserDetail userDetail,
Errors errors, Map<String, Object> map) {
if (errors.getErrorCount() > 0) {
log.info("User attempt update user: " + userDetail.toString());
for (FieldError error : errors.getFieldErrors()) {
log.info(error.getField() + ": " + error.getDefaultMessage());
}
//I want to redirect to same form but show validation of previous inputted got error (NotEmpty, Email, typeMissMatch etc...)
//Below tested all failed
//Use this when return type is String
//return "/user/inputUserDetail"; //405 error
//return inputUserDetail(map); //405 error, direct call another method in same controller to show update form
//return "redirect:/user/userDetail/" + userDetail.getUdId(); //ok, but this one is redirect, no current errors bind to the update form
//Use this when return type is ModelAndView
map.put(BindingResult.MODEL_KEY_PREFIX + "userDetail", errors);
map.put("userDetail", userDetail);
ModelAndView mv = new ModelAndView();
mv.addObject(new RedirectView("FaddistWardrobe/user/userDetail"));
mv.addAllObjects(map);
return mv;
} else {
try {
//update user detail
userDetailService.updateUserDetail(userDetail);
} catch (Exception e) {
log.error("Failed to update UserDetail: " + userDetail.toString());
}
//This one okay can perform while form data correct
//If return String like above method, result same, both ok
return new ModelAndView("redirect:/user/userDetails");
}
}
The mapping I follow REST url style PUT method for update user, don't know it is possible cannot call internal JSP while using PUT method? Or any configuration I miss out for PUT method setup?
This webapp already configured org.springframework.web.filter.HiddenHttpMethodFilter...
Appreciate & welcome any suggestions, I just started to learn Spring MVC...
Hope someone help... Thanks again.
The problem is trying to perform like PUT => Redirect => PUT
(1) Above code okay when try to add correct data because it perform like PUT => Redirect => GET
(2) Fail due to trying display error in the same page using PUT method
Solution is change to PUT => Redirect => GET as below coding:
//Update - need UserDetail model attribute
#RequestMapping(value = "/userDetail", method = RequestMethod.PUT)
public String updateUserDetail(#Valid UserDetail userDetail,
Errors errors, Map<String, Object> map,
final RedirectAttributes redirectAttributes) {
if (errors.getErrorCount() > 0) {
//log.info("User attempt update user: " + userDetail.toString());
for (FieldError error : errors.getFieldErrors()) {
log.info(error.getField() + ": " + error.getDefaultMessage());
}
//redirect to same page to show error return infor (NotEmpty etc...)
redirectAttributes.addFlashAttribute("userDetail", userDetail);
redirectAttributes.addFlashAttribute(BindingResult.MODEL_KEY_PREFIX + "userDetail", errors);
return "redirect:/user/userDetail/error";
} else {
try {
//update user detail
userDetailService.updateUserDetail(userDetail);
} catch (Exception e) {
String tmp = e.getLocalizedMessage();
String tmp2 = e.toString();
//log.error("Failed to update UserDetail: " + userDetail.toString());
}
return "redirect:/user/userDetails";
}
}
//Display form with validation error after update fail (PUT -> Redirect -> GET)
#RequestMapping(value = "/userDetail/error", method = RequestMethod.GET)
public String inputUserDetail_error() {
//Here will auto retrieve previous redirectAttributes data and put into the JSP page.
return "/user/inputUserDetail";
}
Just use redirectAttributes store temporary model and when the user after redirected, can retrieved back previous model and use it.
Hope able help other fresh to SpringMVC with this solution.
Thanks.
Using spring, with this code :
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
for(HttpMessageConverter httpMessageConverter : messageConverters){
System.out.println(httpMessageConverter);
}
ResponseEntity<ProductList> productList = restTemplate.getForEntity(productDataUrl,ProductList.class);
I get
org.springframework.http.converter.ByteArrayHttpMessageConverter#34649ee4
org.springframework.http.converter.StringHttpMessageConverter#39fba59b
org.springframework.http.converter.ResourceHttpMessageConverter#383580da
org.springframework.http.converter.xml.SourceHttpMessageConverter#409e850a
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter#673074aa
org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter#1e3b79d3
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#52bb1b26
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.mycopmany.ProductList] and content type [text/html;charset=UTF-8]
The a snippet of the pojo :
#XmlRootElement(name="TheProductList")
public class ProductList {
#XmlElement(required = true, name = "date")
private LocalDate importDate;
From a Spring point of view, none of the HttpMessageConverter instances registered with the RestTemplate can convert text/html content to a ProductList object. The method of interest is HttpMessageConverter#canRead(Class, MediaType). The implementation for all of the above returns false, including Jaxb2RootElementHttpMessageConverter.
Since no HttpMessageConverter can read your HTTP response, processing fails with an exception.
If you can control the server response, modify it to set the Content-type to application/xml, text/xml, or something matching application/*+xml.
If you don't control the server response, you'll need to write and register your own HttpMessageConverter (which can extend the Spring classes, see AbstractXmlHttpMessageConverter and its sub classes) that can read and convert text/html.
You could also simply tell your RestTemplate to accept all media types:
#Bean
public RestTemplate restTemplate() {
final RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
messageConverters.add(converter);
restTemplate.setMessageConverters(messageConverters);
return restTemplate;
}
If you are using Spring Boot, you might want to make sure you have the Jackson dependency in your classpath. You can do this manually via:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
Or you can use the web starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
In addition to all the answers, if you happen to receive in response text/html while you've expected something else (i.e. application/json), it may suggest that an error occurred on the server side (say 404) and the error page was returned instead of your data.
So it happened in my case. Hope it will save somebody's time.
If you can't change server media-type response, you can extend GsonHttpMessageConverter to process additional support types
public class MyGsonHttpMessageConverter extends GsonHttpMessageConverter {
public MyGsonHttpMessageConverter() {
List<MediaType> types = Arrays.asList(
new MediaType("text", "html", DEFAULT_CHARSET),
new MediaType("application", "json", DEFAULT_CHARSET),
new MediaType("application", "*+json", DEFAULT_CHARSET)
);
super.setSupportedMediaTypes(types);
}
}
You can make up a class, RestTemplateXML, which extends RestTemplate. Then override doExecute(URI, HttpMethod, RequestCallback, ResponseExtractor<T>), and explicitly get response-headers and set content-type to application/xml.
Now Spring reads the headers and knows that it is `application/xml'. It is kind of a hack but it works.
public class RestTemplateXML extends RestTemplate {
#Override
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor) throws RestClientException {
logger.info( RestTemplateXML.class.getSuperclass().getSimpleName() + ".doExecute() is overridden");
Assert.notNull(url, "'url' must not be null");
Assert.notNull(method, "'method' must not be null");
ClientHttpResponse response = null;
try {
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
// Set ContentType to XML
response.getHeaders().setContentType(MediaType.APPLICATION_XML);
if (!getErrorHandler().hasError(response)) {
logResponseStatus(method, url, response);
}
else {
handleResponseError(method, url, response);
}
if (responseExtractor != null) {
return responseExtractor.extractData(response);
}
else {
return null;
}
}
catch (IOException ex) {
throw new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + url + "\":" + ex.getMessage(), ex);
}
finally {
if (response != null) {
response.close();
}
}
}
private void logResponseStatus(HttpMethod method, URI url, ClientHttpResponse response) {
if (logger.isDebugEnabled()) {
try {
logger.debug(method.name() + " request for \"" + url + "\" resulted in " +
response.getRawStatusCode() + " (" + response.getStatusText() + ")");
}
catch (IOException e) {
// ignore
}
}
}
private void handleResponseError(HttpMethod method, URI url, ClientHttpResponse response) throws IOException {
if (logger.isWarnEnabled()) {
try {
logger.warn(method.name() + " request for \"" + url + "\" resulted in " +
response.getRawStatusCode() + " (" + response.getStatusText() + "); invoking error handler");
}
catch (IOException e) {
// ignore
}
}
getErrorHandler().handleError(response);
}
}
Try this:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
Or you can use
public void setSupportedMediaTypes(List supportedMediaTypes)
method which belongs to AbstractHttpMessageConverter<T>, to add some ContentTypes you like. This way can let the MappingJackson2HttpMessageConverter canRead() your response, and transform it to your desired Class, which on this case,is ProductList Class.
and I think this step should hooked up with the Spring Context initializing. for example, by using
implements ApplicationListener {
...
}
A refinement of Vadim Zin4uk's answer is just to use the existing GsonHttpMessageConverter class but invoke the setSupportedMediaTypes() setter.
For spring boot apps, this results into adding to following to your configuration classes:
#Bean
public GsonHttpMessageConverter gsonHttpMessageConverter(Gson gson) {
GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
converter.setGson(gson);
List<MediaType> supportedMediaTypes = converter.getSupportedMediaTypes();
if (! supportedMediaTypes.contains(TEXT_PLAIN)) {
supportedMediaTypes = new ArrayList<>(supportedMediaTypes);
supportedMediaTypes.add(TEXT_PLAIN);
converter.setSupportedMediaTypes(supportedMediaTypes);
}
return converter;
}
This is not answering the problem but if anyone comes to this question when they stumble upon this exception of no suitable message converter found, here is my problem and solution.
In Spring 4.0.9, we were able to send this
JSONObject jsonCredential = new JSONObject();
jsonCredential.put(APPLICATION_CREDENTIALS, data);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
ResponseEntity<String> res = restTemplate.exchange(myRestUrl), HttpMethod.POST,request, String.class);
In Spring 4.3.5 release, we starting seeing errors with the message that converter was not found.
The way Convertors work is that if you have it in your classpath, they get registered.
Jackson-asl was still in classpath but was not being recognized by spring. We replaced Jackson-asl with faster-xml jackson core.
Once we added I could see the converter being registered.
I also had the same error message :
"Could not extract response: no suitable HttpMessageConverter found for response type ..."
This occured when I was trying to get info from a link that did not return the object type I wanted to convert or when the link did not return anything. I handled it using a try catch block :
try {
status = restTemplate
.getForObject(statusResourceUrl, Status.class);
//TODO add new exceptions if necessary or simply use Exception
} catch (BeanCreationException | UnknownContentTypeException | HttpClientErrorException e) {
status.setStatus("DOWN");
System.out.println("exception " + e.getMessage());
}
I was also facing the same issue in last week. Tried the above solution which marked as accepted but didnt worked.
When this will come : While calling the external URL(REST call) and the response is the complex object.
What was wrong : was adding unnecessary the converter using the below code
org.springframework.web.client.restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
Solution: Just add the below dependency
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
Because spring boot add all the default message converters. No need to add any extra.
Not even any JaxB dependency. If present please delete and Try it will work
Danke!!