return string instead of model - java

I am using spring 3.2 and I have come with one requirement and can't figure out how to achieve it, first please look for below
We mostly use model in Spring MVC which is use for data binding
#ResponseBody annotation returns the string as http response
So my requirement is I want to use both together in single method base on condition, Here is my code
#RequestMapping(value="userAddEditSubmit.htm", method={RequestMethod.GET, RequestMethod.POST})
public String userAddEditSubmit(
#ModelAttribute("user") User user,
HttpServletRequest request, HttpServletResponse response, HttpSession session,
Model model
) throws Exception {
try {
//Here is my logic
return "redirect:" + url;
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
So above is my method which returns specific jsp with model attribute, but now in one condition I have requirement to return String data instead of whole jsp in the same method, what can I do to achieve this? Any help will be highly appreciated.

You can simply return null from that method when your condition is met and write to the response yourself. Spring assumes that when a method returns null it has handled the response itself.

Related

Custom Spring Data Rest Controller with HATEOAS Support

I am implementing Spring Data REST in one of my project. I have to write a few custom Repository in order to write custom queries. I am using a Controller in front of my REST Repository. In order to get a HATEOAS response, I am using PersistentEntityResourceAssembler in my controller. This controller works fine for a single entity but in case of a list, I get an exception "PersistentEntity must not be null!"
#RequestMapping(value="/employmentType", method=RequestMethod.GET, produces="application/hal+json")
#ResponseBody
public ResponseEntity<?> getEmploymentTypes(HttpServletRequest request, HttpServletResponse response,PersistentEntityResourceAssembler resourceAssembler) throws TenantUnavailableException, TenantInvalidException
{
try
{
List<EmploymentType> employmentTypeList = employmentTypeRepository.findAll();
if(null==employmentTypeList || employmentTypeList.size()==0)
return new ResponseEntity<ApiResponse>(new ApiResponse(false, ENTITY_NOT_FOUND),
HttpStatus.NOT_FOUND);
// Accessing the 0th index works fine
//In case of a full list, it throws "Persistant Entity must not be null !" exception
return ResponseEntity.ok(resourceAssembler.toResource(employmentTypeList.get(0)));
}
catch (Exception e)
{
e.printStackTrace();
return new ResponseEntity<ApiResponse>(new AppResponse(false, REQUEST_NOT_PROCESSED),
HttpStatus.INTERNAL_SERVER_ERROR);
}
}
I am trying to leverage maximum spring functionality with minimum coding support from my end. I do not want to write a ResourceAssembler for each and every persistent entity in my project.
Please suggest if anyone has any ideas.
To work with list of 'resources' you can use class Resources, for example, like this:
List<EmploymentType> types = employmentTypeRepository.findAll();
Resources<Resource<EmploymentType>> resources = Resources.wrap(types);
resources.add(/* you can add some links here */);
return ResponseEntity.ok(resources);
From Resources javadoc:
General helper to easily create a wrapper for a collection of entities.

How to parse a URL and run a method with Spring MVC 'reflectively'?

I have a Spring Boot application that uses Spring MVC in the usual manner, with a bunch of #RequestMapping methods, Freemarker definitions, and the like. This is all tied together with a WebMvcConfigurerAdapter class.
I'd like to provide a service where the user submits a list of valid URLs, and the webapp would work out which controller would be called, passes in the parameters, and returns a combined result for every URL — all in one request.
This would save the user from having to make hundreds of HTTP calls, but would still allow them to make one-off requests if need be. Ideally, I'd just inject an auto-configured Spring bean, so I don't have to repeat the URL resolving and adapting and handling that Spring does internally, and the controller's list of other controllers would never go out of sync with the real list of controllers.
I expected to write something like this (simplified to only deal with one URL, which is pointless but easier to understand):
#Autowired BeanThatSolvesAllMyProblems allMappings;
#PostMapping(path = "/encode", consumes = MediaType.TEXT_PLAIN_VALUE)
#ResponseBody
public String encode(#RequestBody String inputPath) {
if (allMappings.hasMappingForPath(inputPath)) {
return allMappings.getMapping(inputPath).execute();
} else {
return "URL didn't match, sorry";
}
}
Instead, I've had to define Spring beans I don't know what they do and have been repeating some of what Spring is meant to do for me, which I'm worried won't work quite the same as it would if the user just made the call themselves:
// these two are #Beans, with just their default constructor called.
#Autowired RequestMappingHandlerMapping handlers;
#Autowired RequestMappingHandlerAdapter adapter;
#PostMapping(path = "/encode", consumes = MediaType.TEXT_PLAIN_VALUE)
#ResponseBody
public String encode(#RequestBody String inputText) {
final HttpServletRequest mockRequest = new MockHttpServletRequest(null, inputText);
final StringBuilder result = new StringBuilder();
this.handlers.getHandlerMethods().forEach((requestMappingInfo, handlerMethod) -> {
if (requestMappingInfo.getPatternsCondition().getMatchingCondition(mockRequest) != null) {
try {
final MockHttpServletResponse mockResponse = new MockHttpServletResponse();
result.append("Result: ").append(adapter.handle(mockRequest, mockResponse, handlerMethod));
result.append(", ").append(mockResponse.getContentAsString());
result.append("\n");
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
});
return result.toString();
}
I thought I was doing quite well going down this path, but it's failing with Missing URI template variable errors, and not only do I have no idea how to put the request parameters in (another thing which Spring could be able to handle itself), but I'm not even sure that this is the right way to go about doing this. So how do I simulate a Spring MVC request "reflectively", from within the webapp itself?
JSON API spec. solves this problem by allowing sending multiple operations per request. There even exists a quite mature implementation that supports this feature which is called Elide. But I guess this is might not fully meet your requirements.
Anyway, here's what you can do.
You have to take into consideration that DispatcherServlet holds handlerMappings list that is used to detect appropriate request handler and handlerAdaptors. The selection strategy for both lists is configurable (see DispatcherServlet#initHandlerMappings and #initHandlerAdapters).
You should work out a way you would prefer to retrieve this lists of handlerMappings/initHandlerAdapters and stay in sync with DispatcherServlet.
After that you can implement your own HandlerMapping/HandlerAdaptor (or present a Controller method as in your example) that would handle the request to /encode path.
Btw, HandlerMapping as javadoc says is
Interface to be implemented by objects that define a mapping between
requests and handler objects
or simply saying if we take DefaultAnnotationHandlerMapping that would map our HttpServletRequests to #Controller methods annotated with #RequestMapping. Having this mapping HandlerAdapter prepares incoming request to consuming controller method, f.ex. extracting request params, body and using them to call controller's method.
Having this, you can extract URLs from main request, create a list of stub HttpRequests holding the information needed for further processing and loop through them calling this:
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
having a handlerMapping you call
HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
and then you can finally call
ha.handle(processedRequest, response, mappedHandler.getHandler());
which in turn would execute the controller's method with params.
But having all this, I would not recommend to following this approach, instead, think about usage of JSON API spec or any other.
How about using Springs RestTemplate as client for this? You could call your controllers within the spring controller as if it would be an external resource:
#ResponseBody
public List<String> encode(#RequestBody List inputPaths) {
List<String> response = new ArrayList<>(inputPaths.size());
for (Object inputPathObj : inputPaths) {
String inputPath = (String) inputPathObj;
try {
RequestEntity.BodyBuilder requestBodyBuilder = RequestEntity.method(HttpMethod.GET, new URI(inputPath)); // change to appropriate HttpMethod, maybe some mapping?
// add headers and stuff....
final RequestEntity<Void> requestEntity = requestBodyBuilder.build(); // when you have a request body change Void to e.g. String
ResponseEntity<String> responseEntity = null;
try {
responseEntity = restTemplate.exchange(requestEntity, String.class);
} catch (final HttpClientErrorException ex) {
// add your exception handling here, e.g.
responseEntity = new ResponseEntity<>(ex.getResponseHeaders(), ex.getStatusCode());
throw ex;
} finally {
response.add(responseEntity.getBody());
}
} catch (URISyntaxException e) {
// exception handling here
}
}
return response;
}
Note that generic do not work for the #RequestBody inputPaths.
See alse http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html and https://spring.io/guides/gs/consuming-rest/ .
I agree with the other answers that you should consider this feature outside of your project, instead of having it in the code. It is a question of design and you can choose the approach you want. Based on your comment that these are GET requests, you can achieve what you want with a request dispatcher to trigger your requests within your special Controller service method for each URL and capture the response with a HttpServletResponseWrapper instance.
In the following code sample, the "consolidate" method takes comma separated URLs like this ("http://localhost:8080/index/index1,index2", here "index1,index2" is the URL list), consolidates their text output into a single payload and returns it. For this example URL, the consolidated outputs of http://localhost:8080/index1 and http://localhost:8080/index2 will be returned. You might want to extend/modify this with added parameters, validation, etc for the URLs. I tested this code with Spring Boot 1.2.x.
#Controller
public class MyController {
#RequestMapping("/index/{urls}")
#ResponseBody
String consolidate(#PathVariable String[] urls, HttpServletRequest request, HttpServletResponse response) {
StringBuilder responseBody = new StringBuilder();
//iterate for each URL provided
for (String url : urls) {
RequestDispatcher dispatcher = request.getServletContext().getRequestDispatcher("/" + url);
HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper((HttpServletResponse) response) {
private CharArrayWriter output = new CharArrayWriter();
#Override
public PrintWriter getWriter() {
return new PrintWriter(output);
}
#Override
public String toString() {
return output.toString();
}
};
try {
dispatcher.include(request, wrapper);
//append the response text
responseBody.append(wrapper.toString());
} catch (ServletException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//This holds the consolidated output
return responseBody.toString();
}
#RequestMapping("/index1")
String index1() {
return "index1";
}
#RequestMapping("/index2")
String index2() {
return "index2";
}
}

how to return a response body when header is missing in spring REST api

I am passing a header to a spring REST api like:
#RequestHeader(value="test-header")
header is mandatory here for the API, so I do not want to keep it optional.
when no header is passed, any call to the API returns a standard 400 error indicating that request is syntantically wrong and then it does not enter the REST API. But, I want to construct a proper ResponseBody and return a json for this error. I am not sure about the best way to do this. I thought about using spring interceptor and check if this header was passed or not, but then I am not sure if I can create a responsebody from here. Atleast I could not figure out how to do so.
will interceptor approach work for this? If yes, how? If not, then what are the options? Can someone please help on this?
Update:
This is how the REST API is:
public void methodA(#RequestHeader(value="test-header") String header, #RequestBody User user, HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
...
...
}
When the header is present, it will enter the REST API and continue with the logic. But, if the header is not present, it does not enter the API and simply returns a standard 400 error.
The interceptor that I wrote is like:
public class XXXInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
...
...
...
return true;
}
}
STEP1: Use spring validator annotation like #valid to validate your request.
STEP 2: Write your custom validator class. which will be responsible to check the header and see if it has value or it has the expected value.
STEP 3: If the request is not correct validator throws your custom exception.
STEP 4: write your exception handler class. In the class define what response must me returned if the exception in STEP 3 is caught.
For more information on Exception Handling in Spring.
In our current projet we do use a java interceptor to authenticate the request but nothing beyound that.
Write a method with the annotation #ExceptionHandler and use ServletRequestBindingException.class as this exception is thrown in case of miss. You can return any type of object from this method.
For example
#ExceptionHandler(ServletRequestBindingException.class)
public ResponseEntity<ResponseObject> handleHeaderError(){
ResponseObject responseObject=new ResponseObject();
responseObject.setStatus(Constants.ResponseStatus.FAILURE.getStatus());
responseObject.setMessage(header_missing_message);
ResponseEntity<ResponseObject> responseEntity=new ResponseEntity<ResponseObject>(responseObject, HttpStatus.BAD_REQUEST);
return responseEntity;
}
Another approach would be using Spring Interceptors (HandlerInterceptorAdapter), as you mentioned in your question, with #ControllerAdvice and return your JSON in an #ExceptionHandler method.
Take a look at the following post: http://www.journaldev.com/2651/spring-mvc-exception-handling-exceptionhandler-controlleradvice-handlerexceptionresolver-json-response-example
This is coming late but then, a very straightforward way to deal with this type of issue is to use a Controller Advice class which allows you to handle exceptions across the whole application in one global handling component.
The exception throw by spring is the MissingRequestHeaderException which you can then provide a custom handler in your controller advice class.
#Slf4j
#RestControllerAdvice
public class ControllerAdvice {
#ExceptionHandler(MissingRequestHeaderException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public ErrorResponse handleMissingRequestHeaderException(MissingRequestHeaderException ex) {
log.error(ex.getMessage(), ex);
return new ErrorResponse("Missing request header: " + ex.getHeaderName());
}
}
public class ErrorResponse implements Serializable {
private String message;
public ErrorResponse(String message) {
this.message = message;
}
}

Proper form validation with Spring MVC 3 - catching PersistenceException

I have been looking for a way to make form validation as easy and unobtrusive as possible in Spring MVC 3. I like the way spring can handle Bean Validation by passing #Valid to my model (that has been annotated with validator annotations) and using the result.hasErrors() method.
I am setting up my controller actions like this:
#RequestMapping(value = "/domainofexpertise", method = RequestMethod.PUT)
public String addDomainOfExpertise(#ModelAttribute("domainOfExpertise")
#Valid DomainOfExpertise domainOfExpertise, final BindingResult result) {
if (result.hasErrors()) {
return "/domainofexpertise/add";
} else {
domainOfExpertiseService.save(domainOfExpertise);
return "redirect:/admin/domainofexpertise/list";
}
}
Which works like a charm. Database exceptions (like trying to save something with a unique constraint on a field) will still get through. Is there any way to incorporate catching those exceptions in the validation process going on behind the scenes? This way of validating is very concise so I want to avoid having to manually catch them in my controller.
Any information on this?
Here is an example I use to convert PersistentExceptions to a friendlier message. It is a method that goes in the Controller. Will this work for you?
/**
* Shows a friendly message instead of the exception stack trace.
* #param pe exception.
* #return the exception message.
*/
#ExceptionHandler(PersistenceException.class)
#ResponseBody
#ResponseStatus(HttpStatus.BAD_REQUEST)
public String handlePersistenceException(final PersistenceException pe) {
String returnMessage;
if (pe.getCause()
instanceof ConstraintViolationException) {
ConstraintViolationException cve =
(ConstraintViolationException) pe.getCause();
ConstraintViolation<?> cv =
cve.getConstraintViolations().iterator().next();
returnMessage = cv.getMessage();
} else {
returnMessage = pe.getLocalizedMessage();
}
if (pe instanceof EntityExistsException) {
returnMessage = messages.getMessage("user.alreadyexists");
}
return returnMessage;
}

How to change "pathInfo" of HttpServletRequest

I am afraid to ask a strange question but I want to change "pathInfo" of HttpServletRequest at a handler method of a Controller. Please take look at below.
I know I can get "pathInfo" by using getPathInfo(). However. I don't know how to set up the pathInfo. Is it possible ? Any help will be appreciated
#RequestMapping(value = "show1" method = RequestMethod.GET)
public String show1(Model model, HttpServletRequest request) {
// I want to set up "PathInfo" but this kind of methods are not provided
//request.setPathInfo("/show2");
// I thought that BeanUtils.copy may be available.. but no ideas.
// I have to call show2() with the same request object
return show2(model, request);
}
// I am not allowed to edit this method
private String show2(Model model, HttpServletRequest request) {
// I hope to display "http://localhost:8080/contextroot/show2"
System.out.println(request.getRequestURL());
return "complete";
}
You can't set these values.
The only option is to create a wrapper for your request, something like this:
return show2(model, new HttpServletRequestWrapper(request) {
public StringBuffer getRequestURL() {
return new StringBuffer(
super.getRequestURL().toString().replaceFirst("/show1$", "/show2"));
}
});
Path Info is set by the browser (client) when it requests a certain URL.

Categories

Resources