I have some controller's method:
#RequestMapping("/")
#AuthorizedRNUser
public Object index(UserStateVO userStateVO) {
return userStateVO;
}
Also I have HandlerMethodArgumentResolver for UserStateVO parameter
public class UserStateArgumentHandlerResovler implements HandlerMethodArgumentResolver{
#Autowired
RNService service;
#Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getMethod().isAnnotationPresent(AuthorizedRNUser.class) && methodParameter.getParameterType() == UserStateVO.class;
}
#Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
UserStateVO userState = service.getUserState();
if (isNull(userState))
// here i need to return 403 HTTP response
throw new RuntimeException("User is not allowed");
return userState;
}
}
And if the UserStateVO is null I need to return 403 HTTP response, but I do not know is it possible? How best to check UserStateVO and pass it into a controller or return HTTP response?
Use the same method as handling exceptions in MVC exception-handling-in-spring-mvc
Add your custom exception, e.g
public class BadRequestException extends RuntimeException {
private static final long serialVersionUID = 1L;
public BadRequestException(String message) {
super(message);
}
}
And either annotate it with #ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "User is not allowed") or add #ControllerAdvice class with method like
#ExceptionHandler(value = { BadRequestException.class })
#ResponseStatus(value = HttpStatus.FORBIDDEN)
#ResponseBody
public Map<String, String> handleBadRequestException(BadRequestException e) {
Map<String, String> retMessages = new HashMap<>();
retMessages.put("message", e.getMessage());
return retMessages;
}
what remains is just to throw it
if (isNull(userState))
// here i need to return 403 HTTP response
throw new BadRequestException("User is not allowed");
I have the following controller logic. However, if I navigate to a non-existing page (e.g. /random-page), I end up with a TemplateInputException. How can I catch this and go to the 404 page?
#RequestMapping(value = { "{path:(?!resources|error).*$}", "{path:(?!resources|error).*$}/**" }, headers = "Accept=text/html")
public String index(final HttpServletRequest request) {
try {
String path = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
return path.split("/")[1];
} catch (Exception e) {
log.error("Failed to render the page. {}",e);
return "error/general";
}
}
Thymeleaf seems to be ignoring the ExceptionHandler:
#ExceptionHandler(Exception.class)
public ModelAndView handleAllException(Exception ex) {
ModelAndView model = new ModelAndView("error/generic_error");
model.addObject("errMsg", "this is Exception.class");
return model;
}
My workaround this problem for spring-boot (exception is view with message param):
#Controller
public class ErrorController implements org.springframework.boot.autoconfigure.web.ErrorController {
private static final String ERROR_PATH = "/error";
#Autowired
private ErrorAttributes errorAttributes;
#Override
public String getErrorPath() {
return ERROR_PATH;
}
#RequestMapping(ERROR_PATH)
public String error(HttpServletRequest request, Model model) {
Map<String, Object> errorMap = errorAttributes.getErrorAttributes(new ServletRequestAttributes(request), false);
String exception = (String) errorMap.get("exception");
if (exception != null && exception.contains("TemplateInputException")) {
errorMap.put("message", "Неверный запрос");
}
model.addAllAttributes(errorMap);
return "exception";
}
}
I'm building a webapp modelled on the MVC architecture. Form data from a JSP page gets forwarded to a Dispatcher Servlet which delegates to the Controller. I've managed to inject a single controller and it works just fine.
For multiple controllers I've created a Mapping Handler in the servlet which maps the request to the relevant Controller by reading in a properties file. I've injected the Mapping Handler but I'm still getting a NullPointerException.
An additional ControllerBundle just abstracts the Controller and method name into one object. I'm using tomee webprofile 1.7.4
DispatcherServlet.java:
package a1;
// imports
public class DispatcherServlet extends HttpServlet {
#Inject
#Named("MappingHandler")
private MappingHandler MAPPING_HANDLER;
// ** Single controller **
// #Inject
// #Named("customerController")
// private CustomerController controller;
#Override
public void init() throws ServletException {
MAPPING_HANDLER.generate();
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
String pathInfo = req.getPathInfo();
Controller controller = MAPPING_HANDLER.getController(pathInfo);
String methodName = MAPPING_HANDLER.getMethod(pathInfo);
Method method = controller.getClass().getDeclaredMethod(methodName, HttpServletRequest.class);
method.setAccessible(true);
method.invoke(controller, req);
req.getRequestDispatcher("/results.jsp").forward(req, resp);
// ** Single controller **
// String methodName = "addCustomer";
// Method method = controller.getClass().getDeclaredMethod(methodName, HttpServletRequest.class);
//
// method.setAccessible(true);
// method.invoke(controller, req);
// req.getRequestDispatcher("/success.jsp").forward(req, resp);
} catch (SecurityException | IllegalArgumentException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
MappingHandler.java:
package a1;
// imports
#Named("MappingHandler")
public class MappingHandler {
private static final Map<String, ControllerBundle> CONTROLLER_BUNDLE_MAP = new HashMap<>();
private static final String PACK = "a1.";
void generate() {
try {
Properties props = new Properties();
InputStream is1 = this.getClass().getClassLoader().getResourceAsStream("mappings_h6.properties");
props.load(is1);
Enumeration e = props.propertyNames();
while (e.hasMoreElements()) {
String left = (String) e.nextElement();
String right = props.getProperty(left);
// ControllerBundle: abstraction containing both the Controller and the name of the method
ControllerBundle controllerBundle = new ControllerBundle(right, PACK);
CONTROLLER_BUNDLE_MAP.put(left, controllerBundle);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Controller getController(String pathInfo) {
ControllerBundle controllerBundle = CONTROLLER_BUNDLE_MAP.get(pathInfo);
Controller controller = controllerBundle.getController();
return controller;
}
String getMethod(String pathInfo) {
ControllerBundle controllerEntity1 = CONTROLLER_BUNDLE_MAP.get(pathInfo);
String methodName = controllerEntity1.getMethodName();
return methodName;
}
}
ControllerBundle.java:
package a1;
// imports
public class ControllerBundle {
private Controller controller;
private String methodName;
private static final Map<String, Controller> CONTROLLER_MAP = new HashMap<>();
public ControllerBundle(String value, String pack) {
String[] valueSplit = value.split("#");
String controllerName = valueSplit[0];
Controller controller = findController(pack, controllerName);
String methodName = valueSplit[1];
this.controller = controller;
this.methodName = methodName;
}
// Keep only one copy of every controller
private static Controller findController(String pack, String controllerName) {
try {
Class currentControllerClass = Class.forName(pack + controllerName);
for (String s : CONTROLLER_MAP.keySet()) {
Class existingControllerClass = CONTROLLER_MAP.get(s).getClass();
if (currentControllerClass == existingControllerClass) {
return (Controller) existingControllerClass.newInstance();
}
}
Controller currentController = (Controller) currentControllerClass.newInstance();
CONTROLLER_MAP.put(controllerName, currentController);
return currentController;
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
Controller getController() {
return controller;
}
String getMethodName() {
return methodName;
}
}
mappings_h6.properties:
/customer/add=CustomerController#addCustomer
/customer/update=CustomerController#updateCustomer
/account/create=AccountController#createAccount
/account/freeze=AccountController#freezeAccount
beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
do you have a beans.xml in WEB-INF or WEB-INF/classes/META-INF? Otherwise on tomee 1.x CDI is not activated.
My problem is to how to call this. I could do
MyObject o = new MyObject();
myController.save(o, "value");
but this is not what I would like to do. I would like the MyObject to be in the request post body? How can this be done?
#Requestmapping(value="/save/{value}", method=RequestMethod.POST)
public void post(#Valid MyObject o, #PathVariable String value{
objectService.save(o);
}
Just to be clear I am talking about unit testing.
Edit:
#RequestMapping(value = "/", method = RequestMethod.POST)
public View postUser(ModelMap data, #Valid Profile profile, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return dummyDataView;
}
data.put(DummyDataView.DATA_TO_SEND, "users/user-1.json");
profileService.save(profile);
return dummyDataView;
}
See sample code below that demonstrates unit testing a controller using junit and spring-test.
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class })
#Transactional
#ContextConfiguration(locations = {
"classpath:rest.xml"
})
public class ControllerTest{
private MockHttpServletRequest request;
private MockHttpServletResponse response;
#Autowired
private RequestMappingHandlerAdapter handlerAdapter;
#Autowired
private RequestMappingHandlerMapping handlerMapping;
#Before
public void setUp() throws Exception
{
this.request = new MockHttpServletRequest();
request.setContentType("application/json");
this.response = new MockHttpServletResponse();
}
#Test
public void testPost(){
request.setMethod("POST");
request.setRequestURI("/save/test"); //replace test with any value
final ModelAndView mav;
Object handler;
try{
MyObject o = new MyObject();
//set values
//Assuming the controller consumes json
ObjectMapper mapper = new ObjectMapper();
//set o converted as JSON to the request body
//request.setContent(mapper.writeValueAsString(o).getBytes());
request.setAttribute("attribute_name", o); //in case you are trying to set a model attribute.
handler = handlerMapping.getHandler(request).getHandler();
mav = handlerAdapter.handle(request, response, handler);
Assert.assertEquals(200, response.getStatus());
//Assert other conditions.
}
catch (Exception e)
{
}
}
}
You need to use RequestBody:
#Requestmapping(value="/save/{value}", method=RequestMethod.POST)
public void post(#RequestBody MyObject o, #PathVariable String value{
objectService.save(o);
}
general info about request body documentation : http://static.springsource.org/spring/docs/3.0.x/reference/mvc.html#mvc-ann-requestbody
I'm getting image data (as byte[]) from DB. How to return this image in #ResponseBody ?
EDIT
I did it without #ResponseBody using HttpServletResponse as method parameter:
#RequestMapping("/photo1")
public void photo(HttpServletResponse response) throws IOException {
response.setContentType("image/jpeg");
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
IOUtils.copy(in, response.getOutputStream());
}
Using #ResponseBody with registered org.springframework.http.converter.ByteArrayHttpMessageConverter converter as #Sid said doesn't work for me :(.
#ResponseBody
#RequestMapping("/photo2")
public byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
if you are using Spring version of 3.1 or newer you can specify "produces" in #RequestMapping annotation. Example below works for me out of box. No need of register converter or anything else if you have web mvc enabled (#EnableWebMvc).
#ResponseBody
#RequestMapping(value = "/photo2", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
With Spring 4.1 and above, you can return pretty much anything (such as pictures, pdfs, documents, jars, zips, etc) quite simply without any extra dependencies. For example, the following could be a method to return a user's profile picture from MongoDB GridFS:
#RequestMapping(value = "user/avatar/{userId}", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<InputStreamResource> downloadUserAvatarImage(#PathVariable Long userId) {
GridFSDBFile gridFsFile = fileService.findUserAccountAvatarById(userId);
return ResponseEntity.ok()
.contentLength(gridFsFile.getLength())
.contentType(MediaType.parseMediaType(gridFsFile.getContentType()))
.body(new InputStreamResource(gridFsFile.getInputStream()));
}
The things to note:
ResponseEntity with InputStreamResource as a return type
ResponseEntity builder style creation
With this method you dont have to worry about autowiring in the HttpServletResponse, throwing an IOException or copying stream data around.
In addition to registering a ByteArrayHttpMessageConverter, you may want to use a ResponseEntity instead of #ResponseBody. The following code works for me :
#RequestMapping("/photo2")
public ResponseEntity<byte[]> testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
return new ResponseEntity<byte[]>(IOUtils.toByteArray(in), headers, HttpStatus.CREATED);
}
By using Spring 3.1.x and 3.2.x, this is how you should do it:
The controller method:
#RequestMapping("/photo2")
public #ResponseBody byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
And the mvc annotation in servlet-context.xml file:
<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>
In addition to a couple of answers here a few pointers (Spring 4.1).
Incase you don't have any messageconverters configured in your WebMvcConfig, having ResponseEntity inside your #ResponseBody works well.
If you do, i.e. you have a MappingJackson2HttpMessageConverter configured (like me) using the ResponseEntity returns a org.springframework.http.converter.HttpMessageNotWritableException.
The only working solution in this case is to wrap a byte[] in the #ResponseBody as follows:
#RequestMapping(value = "/get/image/{id}", method=RequestMethod.GET, produces = MediaType.IMAGE_PNG_VALUE)
public #ResponseBody byte[] showImageOnId(#PathVariable("id") String id) {
byte[] b = whatEverMethodUsedToObtainBytes(id);
return b;
}
In this case do rememeber to configure the messageconverters properly (and add a ByteArrayHttpMessageConverer) in your WebMvcConfig, like so:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(mappingJackson2HttpMessageConverter());
converters.add(byteArrayHttpMessageConverter());
}
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper);
return converter;
}
#Bean
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter() {
ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
arrayHttpMessageConverter.setSupportedMediaTypes(getSupportedMediaTypes());
return arrayHttpMessageConverter;
}
private List<MediaType> getSupportedMediaTypes() {
List<MediaType> list = new ArrayList<MediaType>();
list.add(MediaType.IMAGE_JPEG);
list.add(MediaType.IMAGE_PNG);
list.add(MediaType.APPLICATION_OCTET_STREAM);
return list;
}
I prefere this one:
private ResourceLoader resourceLoader = new DefaultResourceLoader();
#ResponseBody
#RequestMapping(value = "/{id}", produces = "image/bmp")
public Resource texture(#PathVariable("id") String id) {
return resourceLoader.getResource("classpath:images/" + id + ".bmp");
}
Change the media type to what ever image format you have.
In your application context declare a AnnotationMethodHandlerAdapter and registerByteArrayHttpMessageConverter:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<util:list>
<bean id="byteArrayMessageConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
</util:list>
</property>
</bean>
also in the handler method set appropriate content type for your response.
#RequestMapping(value = "/get-image",method = RequestMethod.GET)
public ResponseEntity<byte[]> getImage() throws IOException {
RandomAccessFile f = new RandomAccessFile("/home/vivex/apache-tomcat-7.0.59/tmpFiles/1.jpg", "r");
byte[] b = new byte[(int)f.length()];
f.readFully(b);
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
return new ResponseEntity<byte[]>(b, headers, HttpStatus.CREATED);
}
Worked For Me.
You should specify the media type in the response. I'm using a #GetMapping annotation with produces = MediaType.IMAGE_JPEG_VALUE. #RequestMapping will work the same.
#GetMapping(value="/current/chart",produces = MediaType.IMAGE_JPEG_VALUE)
#ResponseBody
public byte[] getChart() {
return ...;
}
Without a media type, it is hard to guess what is actually returned (includes anybody who reads the code, browser and of course Spring itself). A byte[] is just not specific. The only way to determine the media type from a byte[] is sniffing and guessing around.
Providing a media type is just best practice
It's work for me in Spring 4.
#RequestMapping(value = "/image/{id}", method = RequestMethod.GET)
public void findImage(#PathVariable("id") String id, HttpServletResponse resp){
final Foto anafoto = <find object>
resp.reset();
resp.setContentType(MediaType.IMAGE_JPEG_VALUE);
resp.setContentLength(anafoto.getImage().length);
final BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(anafoto.getImageInBytes()));
try {
FileCopyUtils.copy(in, resp.getOutputStream());
resp.flushBuffer();
} catch (final IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Non of the answers worked for me, so I've managed to do it like that:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("your content type here"));
headers.set("Content-Disposition", "attachment; filename=fileName.jpg");
headers.setContentLength(fileContent.length);
return new ResponseEntity<>(fileContent, headers, HttpStatus.OK);
Setting Content-Disposition header I was able to download the file with the #ResponseBody annotation on my method.
This is how I do it with Spring Boot and Guava:
#RequestMapping(value = "/getimage", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public void getImage( HttpServletResponse response ) throws IOException
{
ByteStreams.copy( getClass().getResourceAsStream( "/preview-image.jpg" ), response.getOutputStream() );
}
In spring 4 it's very easy you don't need to make any changes in beans. Only mark your return type to #ResponseBody.
Example:-
#RequestMapping(value = "/image/{id}")
public #ResponseBody
byte[] showImage(#PathVariable Integer id) {
byte[] b;
/* Do your logic and return
*/
return b;
}
I think you maybe need a service to store file upload and get that file.
Check more detail from here
1) Create a Storage Sevice
#Service
public class StorageService {
Logger log = LoggerFactory.getLogger(this.getClass().getName());
private final Path rootLocation = Paths.get("upload-dir");
public void store(MultipartFile file) {
try {
Files.copy(file.getInputStream(), this.rootLocation.resolve(file.getOriginalFilename()));
} catch (Exception e) {
throw new RuntimeException("FAIL!");
}
}
public Resource loadFile(String filename) {
try {
Path file = rootLocation.resolve(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
return resource;
} else {
throw new RuntimeException("FAIL!");
}
} catch (MalformedURLException e) {
throw new RuntimeException("FAIL!");
}
}
public void deleteAll() {
FileSystemUtils.deleteRecursively(rootLocation.toFile());
}
public void init() {
try {
Files.createDirectory(rootLocation);
} catch (IOException e) {
throw new RuntimeException("Could not initialize storage!");
}
}
}
2) Create Rest Controller to upload and get file
#Controller
public class UploadController {
#Autowired
StorageService storageService;
List<String> files = new ArrayList<String>();
#PostMapping("/post")
public ResponseEntity<String> handleFileUpload(#RequestParam("file") MultipartFile file) {
String message = "";
try {
storageService.store(file);
files.add(file.getOriginalFilename());
message = "You successfully uploaded " + file.getOriginalFilename() + "!";
return ResponseEntity.status(HttpStatus.OK).body(message);
} catch (Exception e) {
message = "FAIL to upload " + file.getOriginalFilename() + "!";
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(message);
}
}
#GetMapping("/getallfiles")
public ResponseEntity<List<String>> getListFiles(Model model) {
List<String> fileNames = files
.stream().map(fileName -> MvcUriComponentsBuilder
.fromMethodName(UploadController.class, "getFile", fileName).build().toString())
.collect(Collectors.toList());
return ResponseEntity.ok().body(fileNames);
}
#GetMapping("/files/{filename:.+}")
#ResponseBody
public ResponseEntity<Resource> getFile(#PathVariable String filename) {
Resource file = storageService.loadFile(filename);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"")
.body(file);
}
}
When using produces with MediaType.IMAGE_JPEG_VALUE, make sure that you are returning byte[], but not Byte[]. Very strange, but spring cannot convert it and raises an exception: no converter found.