Usage of ResponseEntity<T> - spring

I have following code (it's a method in a controller) to download a attachment/Document:
#RequestMapping(value="downloadattachment.htm",method=RequestMethod.GET)
public ResponseEntity<Blob> downloadAttachment(#RequestParam("attachmentid")
int attachmentId){
//Attachment is a POJO.
Attachment attachment= commonDao.getAttachment(attachmentId);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(new MediaType(attachment.getContentType()));
responseHeaders.set("Content-Disposition",
"attachment; filename=\"" + attachment.getFileName() +"\"");
return new ResponseEntity<Blob>(attachment.getFileData(), responseHeaders, HttpStatus.CREATED);
}
When I run this it gives following error:
The request sent by the client was syntactically incorrect ().
I'm using tiles so my view resolver is:
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
</bean>
Is it related to tiles in some way as all view resolving is happening through tiles definition?
But I think (as according to spring reference), when using #ResponseBody or ResponseEntity<T>, return type of a handler method is written straight to the HTTP response body(and not placed in a Model, or interpreted as a view name).

The problem is with the request and not the response.
#RequestParam("attachmentid") int attachmentId
I think you are not sending parameter attachmentId from client or the request is going to some other controller method.

Related

Camel & CXF & REST: ERROR No message body writer has been found for class java.util.ArrayList, ContentType: application/json

In my Spring configuration file:
<bean id="jacksonJsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
<bean id="restJacksonProviderList" class="java.util.ArrayList">
<constructor-arg>
<list>
<ref bean="jacksonJsonProvider"/>
</list>
</constructor-arg>
</bean>
//......
<route id="RestMyRoute">
<from id="RestRequest" uri="cxfrs:/rest/MyService?resourceClasses=com.myself.services.MyService&bindingStyle=SimpleConsumer&providers=#restJacksonProviderList" />
<to uri="direct:doRoute" />
</route>
The Service interface:
#GET
#Path("/my/something/{id}")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#WebMethod
#WebResult(name = "getSomethingResponse")
public List<MySomething> getSomething(
#PathParam("id") #WebParam(name = "id") String id);
The code above works! I can send the get request to the URl and I get a JSON response.
Now, I do a small change: Instead of defining the web service's URL (and the route) by XML configuration, I define them by Java code:
public class MyRoute extends RouteBuilder {
private String uriRest = "cxfrs:/rest/MyService?resourceClasses=com.myself.services.MyService&bindingStyle=SimpleConsumer&providers=#restJacksonProviderList";
#Override
public void configure() throws Exception {
from(uriRest).
to("log:input").
to("direct:doRoute").
to("log:output");
}
}
When I hit the web service URL, I am getting 500 Internal Server Error and in the logs (Tomcat) I see JAXRSUtils ERROR No message body writer has been found for class java.util.ArrayList, ContentType: application/json
Actually the debugger tells me that defining the URI by Java code is recognized, since I do hit the code inside the route.
I saw this error in many answers here, basically they say to add a Json provider and assign it to the CXF endpoint.
Seems to me like it is what I have done. But it does not work.
Any idea what I am doing wrong here?
As peeskillet said, it's because there isn't a list of providers registered under the name restJacksonProviderList. You can get the JndiContext like this and bind a list to it in the configure method of your routebuilder:
JndiContext registry = (JndiRegistry) context.getRegistry();
registry.bind("restJacksonProviderList", Arrays.asList(new JacksonJsonProvider()));
Edit after comments:
Change & for & in your cxfrs uri definition, & is only needed in xml.

Spring Controller sending image to client results in 406

I'm trying to send an image to the front end upon request, it works if i put it in the request body as part JSON, but i want to use image/png, makes more sense, but i get a 406 when i try that.
Controller:
#RequestMapping(value = RESTPaths.EQUIPMENT_FILE_GET_IMAGE + "/{equipmentId}", method = RequestMethod.GET,
produces = MediaType.IMAGE_PNG_VALUE)
public #ResponseBody byte[] insertDataFile(#PathVariable("equipmentId") final Long equipmentId)
throws InternalServerError {
return equipmentFileService.getImage(equipmentId);
}
Test (client):
mockMvc.perform(
get(RESTPaths.EQUIPMENT_FILE_CONTROLLER + RESTPaths.EQUIPMENT_FILE_GET_IMAGE + "/" + equipment.getId())
.with(httpBasic("user", "password")).accept(MediaType.IMAGE_PNG)
.contentType(TestUtil.APPLICATION_JSON_UTF8)).andDo(MockMvcResultHandlers.print()).andExpect(status().isOk());
}
What am i missing?
Try adding the mvc annotation in servlet-context.xml file which registers a ByteArrayHttpMessageConverter
<mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>image/jpeg</value> <value>image/png</value> </list> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
Solved:
#RequestMapping(value = RESTPaths.EQUIPMENT_FILE_GET_IMAGE + "/{equipmentId}", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<byte[]> getImage(#PathVariable("equipmentId") final Long equipmentId)
throws InternalServerError {
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_JPEG);
return new ResponseEntity<byte[]>(equipmentFileService.getImage(equipmentId), headers, HttpStatus.OK);
}

Spring Webflow 2 and bookmarkable URLs

Currently due to the Post/Redirect/Get pattern all flow urls are something like <site_url>/flow_name?execution=? and input GET parameters are not preserved. Thus the users can't copy the url, or bookmark it.
Any suggestions how could this be done neatly ?
We can bookmark a SWF based application's URL by customising FlowHandlerAdapter of SWF API.
Here is a sample:
My SWF configuration file would have:
<bean id="flowController" class="org.springframework.webflow.mvc.servlet.FlowController">
<property name="flowHandlerAdapter" ref="customFlowHandlerAdapter" />
</bean>
<bean id="customFlowHandlerAdapter" class="com.xyz.CustomFlowHandlerAdapter">
<property name="flowExecutor" ref="flowExecutor" />
<property name="flowUrlHandler" >
<bean class="com.xyz.CustomURLFlowHandler" />
</property>
</bean>
My CustomFlowHandlerAdapter would have:
public class CustomFlowHandlerAdapter extends FlowHandlerAdapter {
...
#Override
public ModelAndView handle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
FlowHandler flowHandler = (FlowHandler) handler;
checkAndPrepare(request, response, false);
String flowExecutionKey = this.getFlowUrlHandler()
.getFlowExecutionKey(request);
if (flowExecutionKey != null)
try {
ServletExternalContext context = createServletExternalContext(
request, response);
FlowExecutionResult result = this.getFlowExecutor().resumeExecution(
flowExecutionKey, context);
handleFlowExecutionResult(result, context, request, response,
flowHandler);
} catch(org.springframework.webflow.execution.repository.NoSuchFlowExecutionException ex){
response.sendRedirect(request.getRequestURI());
} catch(org.springframework.webflow.execution.repository.BadlyFormattedFlowExecutionKeyException ex){
response.sendRedirect(request.getRequestURI());
} catch (FlowException e) {
handleFlowException(e, request, response, flowHandler);
}
....
Here Iam catching NoSuchFlowExecutionException and am redirecting to the exact flow URL without any parameters. Here you can capture and re-include your parameters
Thus I am able to bookmark my URL from any state(always flow starts from first) also I will be able to send my own parameters if required.
you can always use and bookmark a link to one of your flow's start point.for instance you can do <site_url>/flow_name?personId=123&projectId=456 assuming you have two inputs to your flow personId and projectId. But you need to know the url (you will have to give it to the users), you cannot use the one on your address bar.
even if you want to do that, you won't be able to use and bookmark a link to a specific state in your flow (unless you add some logic to the start of your flow to direct you to a specific event depending on the value of an input).

Spring REST consumption results in HTTP Status 406 - Not Acceptable

I get this error when i try to consume a REST API:
Exception in thread "main" org.springframework.web.client.HttpClientErrorException: 406 Not Acceptable
Here's the client code that gets executed:
public static void main(String[] args) {
Car c = getCarById(4);
System.out.println(c);
}
public static #ResponseBody Car getCarById(int id){
return new RestTemplate().getForObject("http://localhost:8080/rest/cars/{id}", Car.class, id);
}
Here's the code of the Controller which maps the request:
#RequestMapping(value="/cars/{id}", method=RequestMethod.GET, headers = {"Accept=text/html,application/xhtml+xml,application/xml"}, produces="application/xml")
public #ResponseBody Car getCarById(#PathVariable("id") int id){
return carService.getCarById(id);
}
Why is this error (406-Not Acceptable) happening although the mappers should take care of mapping to the correct types?
You're sending an Accept= header instead of an Accept: header.
I got this answer when I had a wrong Accept: header in my request. I was trying to request an image/jpeg, but my request contained "Accept: application/json".
The solution was to use the correct entity class to query for (I was querying for Object just to see what would come), in my case Resource.class.
add this to spring mvc dispatcher:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- JSON format support for Exception -->
<bean id="methodHandlerExceptionResolver"
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
</list>
</property>
</bean>
In my case, I fixed this not on the server side but in the client side. I was using Postman and was getting the 406 error. But using a browser was processing the request just fine. So I looked at the request headers in the browser and added the Accept header in Postman, like this: Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
In addition, you can fix add "Accept", "/"
val headers = HttpHeaders()
headers.add("Accept", "*/*")
val httpEntity = HttpEntity("parameters", headers)
restTemplate.exchange(....)
restTemplate.exchange("http://localhost:" + serverPort + "/product/1",
HttpMethod.GET,
httpEntity,
String.javaClass)
Sorry, it's Kotlin
I got same type of error due to mismatch of the value of ContentType & Accept. Solved the issue by setting the same string value for both of them.
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.valueOf(CONTENT_TYPE));
httpHeaders.setAccept(Arrays.asList(MediaType.valueOf(CONTENT_TYPE)));
I have had the same problem and finally it was a library problem. If you don't use maven you have to be sure you have include json core library and all its dependencies.
If your method has input parameters loaded in json format and you don't have this libraries you will have a 415 error.
I think the two error has the same origin: incomplete libraries.

spring restTemplate POST parameters from complex object

I'm attempting to test our REST service using restTemplate using the postForObject(...) method.
unit test:
#Test
public void testPostOrder() {
String url = BASE_URL + "/orders/";
OrderDto orderDtoInput = new OrderDto();
orderDtoInput.setCustomerId(34);
UpdateReportDto updateReport = restTemplate.postForObject(url,
orderDtoInput, UpdateReportDto.class, new Object[] {});
}
the interesting piece of my configuration:
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<ref bean="formHttpMessageConverter" />
<ref bean="marshallingHttpMessageConverter" />
</list>
</property>
</bean>
<bean id="formHttpMessageConverter" class="org.springframework.http.converter.FormHttpMessageConverter">
</bean>
I understand that the FormHttpMessageConverter will convert to and from MultiValueMap and media type
application/x-www-form-urlencoded.
Is there any magic, or tools I can use or wire in to convert my Dto to a MultiValueMap ??? or do I need to cycle over the object properties and build my own MultiValueMap in my test?
my server is expecting to get POST parameters that look something like this:
id=11752&firstName=Joe&active=true&address1=1122&address2=2233&c
ellPhone=123-321-1234&childrensName1=bobby1&childrensName2=bobby2&childrensName3=bobby3&childrensName4=bobby4&city=someCity&
customHobbies=loves To Fly Planes&distributorId=407&email=doc#surgeon.com&fax=321-123-1234&fellowship=good fellows&fishing=false&golf=true&hunting=false&
insuranceCompany1=ins1&insuranceCompany2=ins2&insuranceCompany3=ins3&insuranceCompany4=ins4&lastName=Brownie&
mailMerge=true&medicalSchool=Granada U&officeDays=4&officeManager=manager&officeManagerPhone=456.654.4567&other=true&
paNurse=nurse 1&paNursePhone=345-543-3456&
phone=234-432-2345&
salesRepresentativeId=1935&specialty=meatball surgery&spouseName=Betty&state=AL&
surgeryDays=22&title=doc&version=2&zip=47474
promptValues[0].id=12&promptValues[0].miscPromptId=882&promptValues[0].value=meFirst&
promptValues[1].id=13&promptValues[1].miscPromptId=881&promptValues[1].value=youToo&residency=Jamaica General&
surgeonClinics[0].address1=newAddress&surgeonClinics[0].address2=newAddress2&surgeonClinics[0].city=clinic City&
surgeonClinics[0].email=email#clinic1.com&surgeonClinics[0].fax=123.456.7890&surgeonClinics[0].id=33273&
surgeonClinics[0].name=clinic name&surgeonClinics[0].phone=890-098-4567&
surgeonClinics[0].zip=34567&surgeonClinics[0].surgeryCenter1=MySurgeryCenter1&
surgeonClinics[0].surgeryCenter2=MySurgeryCenter2&
surgeonClinics[1].address1=newAddress11&surgeonClinics[1].address2=newAddress22&surgeonClinics[1].city=clinic2 City&
surgeonClinics[1].email=email#clinic2.com&surgeonClinics[1].fax=123.456.7890&surgeonClinics[1].id=33274&
surgeonClinics[1].name=clinic2 name&surgeonClinics[1].phone=890-098-4567&
surgeonClinics[1].zip=34567&
surgeonClinics[1].surgeryCenter1=MySurgeryCenter21&surgeonClinics[1].surgeryCenter2=MySurgeryCenter22&
Here's what I don't get: our RestServiceController method knows how to take this crazy parameter list and re-create our Dto object. We can successfully call it using curl. It seems that some reciprocal magic should exist on the client side to turn the Dto into the parameter list.
Here's the signature of the server side controller method:
// createOrder
#RequestMapping(method = { RequestMethod.POST, RequestMethod.PUT }, value = "/orders/")
#ResponseBody
public UpdateReportDto createOrder(OrderDto orderDto,
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) {
You can use RestTemplate with message converters. I have tested it and it works
RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
messageConverters.add(new MappingJackson2HttpMessageConverter());
restTemplate.setMessageConverters(messageConverters);
restTemplate.postForEntity(url, requestBodyObject, returnTypeClass);

Resources