Redirect from Spring REST controller with parameters - java

I have two different Spring applications (app-one, app-two), app-one should receive a response and then redirect to app-two with some parameters. So, I have the following REST controller in app-one:
#RestController
public class RedirectController
{
#GetMapping(value = "/redirect")
public ResponseEntity<Void> redirectEndpoint(HttpServletRequest request,
RedirectAttributes redirectAttributes)
{
// Do some business logic
// Set parameters
redirectAttributes.addAttribute("attribute", "Value 1");
redirectAttributes.addFlashAttribute("flashAttribute", "Value 2");
// Redirect to success URL
String redirectURL = "http://app-two/success";
HttpHeaders headers = new HttpHeaders();
headers.setLocation(URI.create(redirectURL));
return ResponseEntity.status(HttpStatus.FOUND).headers(headers).build();
}
}
And the following REST controller in app-two:
#RestController
public class SuccessController
{
#GetMapping(value = "/success")
public ResponseEntity<Void> successEndpoint(HttpServletRequest request, Model model,
#RequestParam(value = "attribute", required = false) String attribute,
#ModelAttribute(value = "flashAttribute") String flashAttribute)
{
// Get parameters
System.out.println("attribute: " + attribute);
System.out.println("flashAttribute: " + flashAttribute);
String flashAttributeFromModelMap = (String) model.asMap().get("flashAttribute");
System.out.println("flashAttributeFromModelMap: " + flashAttributeFromModelMap);
Map<String, ?> flashMap = RequestContextUtils.getInputFlashMap(request);
if (flashMap != null)
{
String flashAttributeFromFlashMap = (String) flashMap.get("flashAttribute");
System.out.println("flashAttributeFromFlashMap: " + flashAttributeFromFlashMap);
}
// Do some business logic
return ResponseEntity.status(HttpStatus.OK).build();
}
}
I was able to redirect successfully by returning FOUND (302). But when adding attributes to RedirectAttributes (in this case attribute and flashAttribute), these attributes are not found after the redirection done (attribute gets null and flashAttribute gets empty).
I tried to get the attributes values by different ways (#RequestParam, #ModelAttribute, model.asMap().get(), and RequestContextUtils.getInputFlashMap(request).get()) but none of them gets the correct value.
What I need is to get the correct attributes' values in successEndpoint. Any suggestions on how to accomplish that?
Thanks in advance.

Related

I can not take a value from the header in the Controller in Spring Boot

I have built a web application in Spring Boot that has a form and besides the values from the form that it inserts in the database it takes from the header the username. This is the code from the Controller:
#PostMapping("/BilantErr/")
public String postIndex(#RequestParam(name = "cui") String cui, #RequestParam(name = "an_raportare") String anRaportare,
#RequestParam(name = "perioada") String perioada,
#RequestHeader("iv-user") String operator, #RequestParam(name = "motivatie") String motivatie, Model model) {
String page = "";
//String operator = "serban";
try {
if (repository.insert(cui, anRaportare, operator, motivatie, perioada) == true) {
page = "success";
} else {
page = "error";
}
} catch (Exception e) {
Logger logger = LoggerFactory.getLogger(BilantErr.class);
}
return page;
}
The error that I am getting is : Resolved [org.springframework.web.bind.MissingRequestHeaderException: Required request header 'iv-user' for method parameter type String is not present]
What may be the problem ? There is an application already built in JSF that works and takes the user from the header and I have to replace it with a Spring app. What am I doing wrong ? Thanks
MissingRequestHeaderException means the HTTP request doesn't contain a "iv-user" header. You must have a look to your request first. You can read all headers of the HTTP request by following code snippet:
#GetMapping("/listHeaders")
public ResponseEntity<String> multiValue(
#RequestHeader MultiValueMap<String, String> headers) {
headers.forEach((key, value) -> {
LOG.info(String.format(
"Header '%s' = %s", key, value.stream().collect(Collectors.joining("|"))));
});
return new ResponseEntity<String>(
String.format("Listed %d headers", headers.size()), HttpStatus.OK);
}
The request header "iv-user" is required but does not seem to be present in the request you receive. You could fix your request or make the header optional: #RequestHeader(value = "iv-user", required = false)

How to make pattern of REST controllers

I'm writing my first spring application, and would like to get experience to make an optimal and attractive code on spring.
I've some restcontrollers which have the big part of similar code
#RequestMapping(path = "/1154",
method = RequestMethod.POST,
headers = {"Content-Type=application/json"},
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public CreateUserResp processRequest(#RequestBody #Valid CreateUserReq request, BindingResult bindingResult) {
CreateUserResp response = new CreateUserResp();
if (bindingResult.hasErrors()){
response.setResultCode(102); // Validation error
response.setErrMsg("Wrong " + bindingResult.getFieldError().getDefaultMessage() + " value.");
} else {
// main service
request = UserService.doSomething();
}
return response;
}
#RequestMapping(path = "/1155",
method = RequestMethod.POST,
headers = {"Content-Type=application/json"},
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ChangeUserResp processRequest(#RequestBody #Valid ChangeUserReq request, BindingResult bindingResult) {
ChangeUserResp response = new ChangeUserResp();
if (bindingResult.hasErrors()){
response.setResultCode(102); // Validation error
response.setErrMsg("Wrong " + bindingResult.getFieldError().getDefaultMessage() + " value.");
} else {
// main service
request = ChangeService.doSomething();
}
return response;
}
#RequestMapping(path = "/1156",
method = RequestMethod.POST,
headers = {"Content-Type=application/json"},
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public AddUserResp processRequest(#RequestBody #Valid AddUserReq request, BindingResult bindingResult) {
AddUserResp response = new AddUserResp();
if (bindingResult.hasErrors()){
response.setResultCode(102); // Validation error
response.setErrMsg("Wrong " + bindingResult.getFieldError().getDefaultMessage() + " value.");
} else {
// main service
request = AddService.doSomething();
}
return response;
}
#RequestMapping(path = "/1157",
method = RequestMethod.POST,
headers = {"Content-Type=application/json"},
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ModifyUserResp processRequest(#RequestBody #Valid ModifyUserReq request, BindingResult bindingResult) {
ModifyUserResp response = new ModifyUserResp();
if (bindingResult.hasErrors()){
response.setResultCode(102); // Validation error
response.setErrMsg("Wrong " + bindingResult.getFieldError().getDefaultMessage() + " value.");
} else {
// main service
request = ModifyService.doSomething();
}
return response;
}
etc ....
The only differences in (path, #RequestBody and Responce objects, and called service ). So, I'll have 10-12 controllers like this one. Is it the way to make this code more optimal and not to write this repeatable blocks of code 10 times (spring methods or may be using generic class or methods). It's just example, not real code. Thx
Especial thanks to people who are very busy to answer but have time to put minus.
I have something quite similar in my application.
For example this is how my editProfile method from the User controller looks like:
#PostMapping(value = EDIT_CONTACT_INFO)
public ResponseEntity<?> editContactInfo(
#Autowired HttpServletRequest httpServletRequest,
#RequestBody #Valid ContactInfoDTO.Req requestBody,
BindingResult bindingResult
)
{
if (bindingResult.hasErrors())
// 400 - BAD REQUEST
return ErrorsDTO.from(bindingResult).responseEntity();
String userName = ControllerUtils.getUserName(httpServletRequest);
User user = userService.findByUserName(userName);
ContactInfo contactInfo = modelMapper.map(requestBody, ContactInfo.class);
if (!userService.editContactInfo(user, contactInfo))
// 500 - INTERNAL SERVER ERROR
return ErrorsDTO.from(INTERNAL_SERVER_ERROR).responseEntity();
// 200 - OK
return ResponseEntity.ok(null);
}
The majority of my API looks quite similar to yours. I've just written my custom mechanism to report errors, and I use a ResponseEntity instance to return data.
Also I have a library to pass data from DTOs to my Model and back (it's called ModelMapper).
edit: it looks like this blogpost covers your question:
http://blog.codeleak.pl/2013/09/request-body-validation-in-spring-mvc-3.2.html
If you really want to get down and dirty, you could write an interceptor, with a pointcut on a new annotation ValidateBinding and an argument of BindingResult. It would probably look something like:
#Around("#annotation(ValidateBinding) && execution(* *(..)) && args(bindingResult)
public Object handleInvalidBindings(ProceedingJoinPoint p, BindingResult bindingResult) {
if (bindingResult.hasErrors()){
GenericResponse response = createTypedResponse(p);
response.setResultCode(102); // Validation error
response.setErrMsg("Wrong " + bindingResult.getFieldError().getDefaultMessage() + " value.");
return response;
}
return pjp.proceed();
}
private GenericResponse createTypedResponse(ProceedingJoinPoint p) {
MethodSignature signature = (MethodSignature) p.getSignature();
Method method = signature.getMethod();
Class responseClass = method.getReturnType();
if(!GenericResponse.class.isAssignableFrom(responseClass)) {
throw new IllegalArgumentException("Could not create proper response class - it should implement the GenericResponse interface");
return (GenericResponse) responseClass.newInstance();
}
But I'm not guaranteeing the expression or the code works. This is more a rough guess on how it could look.
To do this, you'd need an interface GenericResponse which is implemented by your responseclasses, and has a setResultCode and setErrMsg.

How to reflect the incoming GET request in text?

I'm using Spring MVC and Springboot (assume latest version) to put up a simple web service that returns some text.
I was able to figure out how to use #RequestMapping and #PathVariable annotations to show one of the URL path variables as a response from the server (e.g. if the user goes to .../my_user_id/ in the browser, they can then see some text in the browser that includes that user_id... since the service returned it as a response).
I need help with figuring out how to capture the GET HTTP request the user's browser makes and then display it in text form as a response in the browser (I want to display the headers and the request body as plain text).
I've done some research, but none of the solutions available work properly. Is anyone aware of the right approach / feasibility here?
An approach I tried:
http://www.mkyong.com/java/how-to-get-http-request-header-in-java/
Some threads on the error I get back when I tried the above approach:
Spring RestTemplate - how to enable full debugging/logging of requests/responses?
http://forum.spring.io/forum/spring-projects/roo/84244-circular-view-path-resourcenotfound
Circular view path
How to avoid the "Circular view path" exception with Spring MVC test
http://myshittycode.com/2014/01/17/mockmvc-circular-view-path-view-would-dispatch-back-to-the-current-handler-url-view-again/
More on Spring MVC, which I'm heavily using:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html
Anyhow. When using the below #Controller, I get an error:
#RestController
public class HelloController {
#RequestMapping("/")
public String index() {
return "Welcome to your home directory";
}
#RequestMapping(value="/mydata/{userId}", method=RequestMethod.GET)
public String printTheUser(#PathVariable String userId) {
return "The data for " + userId + " would live here";
}
#RequestMapping("/summary_data/")
public String index3() {
return "All summary data would appear here";
}
private String server = "localhost";
private int port = 8080;
#RequestMapping("/request_mirror/**")
public #ResponseBody String mirrorRest(#RequestBody String body, HttpMethod method, HttpServletRequest request,
HttpServletResponse response) throws URISyntaxException {
URI uri = new URI("http", null, server, port, request.getRequestURI(), request.getQueryString(), null);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity =
restTemplate.exchange(uri, method, new HttpEntity<String>(body), String.class);
return responseEntity.getBody();
}
}
When running this code and navigating to localhost:8080/request_mirror/stuff3/, I get the following error:
Whitelabel Error Page. This application has no explicit mapping for /error, so you are seeing this as a fallback.
Mon Feb 08 15:41:13 EST 2016
There was an unexpected error (type=Bad Request, status=400).
Required request body content is missing: org.springframework.web.method.HandlerMethod$HandlerMethodParameter#a35a9b3f
Now, when I try a different approach (another #Controller) - the code looks like:
#Controller
#RequestMapping("/site")
public class SecondController{
#Autowired
private HttpServletRequest request;
#RequestMapping(value = "/{input:.+}", method = RequestMethod.GET)
public ModelAndView getDomain(#PathVariable("input") String input) {
ModelAndView modelandView = new ModelAndView("result");
modelandView.addObject("user-agent", getUserAgent());
modelandView.addObject("headers", getHeadersInfo());
return modelandView;
}
//get user agent
private String getUserAgent() {
return request.getHeader("user-agent");
}
//get request headers
private Map<String, String> getHeadersInfo() {
Map<String, String> map = new HashMap<String, String>();
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = (String) headerNames.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
return map;
}
}
For the above code (SecondController), (sourced from http://www.mkyong.com/java/how-to-get-http-request-header-in-java/), I get the following error, when I try to navigate to localhost:8080/site/stuff123456789... (but I can see the header keys and values from the request in the Map upon inspection... just not sure how to display them as text in the browser as the response).
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Mon Feb 08 16:10:47 EST 2016
There was an unexpected error (type=Internal Server Error, status=500).
Circular view path [stuff123456789]: would dispatch back to the current handler URL [/site/stuff123456789] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
EDIT: Use the HttpEntity to get the body in case it's empty.
I'm not sure exactly what you're trying to achieve, but think this might be close:
#RequestMapping(value="/echoRequest")
public #ResponseBody String echoRequest(HttpEntity<String> httpEntity, HttpServletRequest req) {
String out = "";
List<String> names = req.getHeaderNames();
for (String name : names) {
out += (name + ": " + req.getHeader(name) + "\n");
}
if (httpEntity.hasBody()) {
out += httpEntity.getBody();
}
return out;
}

Pass an ID to frontend from the server

I am building a web app and I need to pass the user's id to my front-end(angular) so I can do some ajax calls.
My question is, how can I give(store) the user's id to my front end? For example, a user can create a group by specifying a group name, but I need to be able to pass the users Id aswell in my ajax request(My backend endpoint needs both an Id and a name).
Angular:
$scope.createGroup = function() {
$http.post(BASE_URL + "/group", $scope.groupRequest).
success(function (data, status, headers, config) {
console.log("Success");
}).error(function(data, status, headers, config) {
$log.info("Error: status =" + status + ", body =" + JSON.stringify(data));
});
}
Front-end controller
#RequestMapping(value = "/group", method = RequestMethod.POST)
public ResponseEntity createGroup(#RequestBody Map body) {
try {
return restTemplate.postForEntity(URI.create(BASE_URL + "/group"), body, Map.class);
} catch (HttpClientErrorException e) {
LOG.warn("Error when trying to fetch groups", e);
return ResponseEntity.badRequest().body(e.getResponseBodyAsString());
}
}
Java Back end controller
#RequestMapping(value = "/group", method = RequestMethod.POST)
public ResponseEntity createGroup(#RequestBody #Valid GroupRequest groupRequest, BindingResult validation) {
if (validation.hasErrors()) {
throw new ValidationException(validation.getFieldErrors());
}
Long groupId = groupService.createGroup(groupRequest);
URI groupLocationURI = URI.create("/group/" + groupId);
return ResponseEntity.created(groupLocationURI).build();
}
Thanks

Spring controller(A).method invoked controller(B).method but finds empty Model

LoginFormController-->Post is invoked after the form is submitted/posted. At the end, it invokes another Controller called LandingFormController-->loadForm.
Well, in the loadForm the values in the Model seems to be empty. Is there a way I can persist a Bean in session or request and get it in the loadForm method? Bonus points: if you could point to some documents to refer :)
Thanks
#Controller
#RequestMapping(value="/login")
public class LoginFormController {
#RequestMapping(method=RequestMethod.POST)
public ModelAndView post(#ModelAttribute User user, BindingResult result, SessionStatus status) {
logger.info("post");
new ReceiptUserValidator().validate(user, result);
if (result.hasErrors()) {
return new ModelAndView("login");
}
else {
logger.info("Email Id: " + user.getEmailId());
//status.setComplete();
Map<String, Object> model = new HashMap<String, Object>();
model.put("userId", user.getEmailId());
model.put("now", new Date().toString());
return new ModelAndView("redirect:/landing.htm", "model", model);
}
}
Controller B below that gets called
#Controller
#RequestMapping(value="/landing")
public class LandingFormController {
protected final Log logger = LogFactory.getLog(getClass());
#RequestMapping(method=RequestMethod.GET)
public String loadForm(Model model) {
logger.info("LandingFormController loadForm: " + model.asMap().keySet());
return "landing";
}
}
The code is performing a redirect which causes the properties placed in the model to be lost. Use flash attributes to pass the attributes to the next controller.
Flash attributes provide a way for one request to store attributes
intended for use in another. This is most commonly needed when
redirecting — for example, the Post/Redirect/Get pattern. Flash
attributes are saved temporarily before the redirect (typically in the
session) to be made available to the request after the redirect and
removed immediately.
LoginFormController
#Controller
#RequestMapping(value="/login")
public class LoginFormController {
#RequestMapping(method=RequestMethod.POST)
public ModelAndView post(#ModelAttribute User user, BindingResult result,
SessionStatus status, final RedirectAttributes redirectAttrs) {
logger.info("post");
new ReceiptUserValidator().validate(user, result);
if (result.hasErrors()) {
return new ModelAndView("login");
}
else {
logger.info("Email Id: " + user.getEmailId());
//status.setComplete();
redirectAttrs.addFlashAttribute("userId", user.getEmailId());
redirectAttrs.addFlashAttribute("now", new Date().toString());
return new ModelAndView("redirect:/landing.htm", "model", model);
}
}
Documentation
As an alternative solution you could simply not perform a redirect from the controller.
Appending retrieving solution.
Modify the loadForm
public String loadForm(#ModelAttribute("user") User user) {
logger.info("LandingFormController loadForm: " + user.getEmailId());
return "landing";
}

Categories

Resources