How to make pattern of REST controllers - java

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.

Related

i could not test the Rest post in SoapUI which has #RequestBody in the method, Any help appreciated

#RequestMapping(value="/postdata", method= RequestMethod.POST, consumes="application/json")
public String postdata(#RequestBody String test, #RequestBody String data) {
logger.info("password reset Request " + requestbody.get("test"));
logger.info("password reset Request " + data);
return "Hello";
}
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public java.lang.String mail.controller.EmailNotifictaionController.postdata(java.lang.String,java.lang.String)]
My Input in the SOAPUI is
{
"test":"my",
"data":"god"
}
You are using two #RequestBody which is not the correct way as one request can only have one request body.
You can either combine both the request bodies into single request body or you can build a wrapper class having both the test and data variables like:
public class Body {
public String test;
public String data;
// You can also keep them private and have getters/setters.
}
and use this class in the API method argument
#RequestMapping(value="/postdata", method= RequestMethod.POST, consumes="application/json")
public String postdata(#RequestBody Body body) {
logger.info("password reset Request " + body.test);
logger.info("password reset Request " + body.data);
return "Hello";
}
You can try this way, it should work.

Redirect from Spring REST controller with parameters

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.

SpringBoot how to Send response to other URL

I have the following code:
#RequestMapping(
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE},
path = "api/api1",
method = RequestMethod.POST,
produces = MediaType.ALL_VALUE
)
public ResponseEntity<?> api1CallBack(#RequestBody String requestBody, HttpServletRequest request) throws IOException, GeneralSecurityException, URISyntaxException {
String response="{SOME_JSON}";
URI callbackURL = new URI("http://otherAPIEnv/api2");
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setLocation(callbackURL);
return new ResponseEntity<String>(response,httpHeaders, HttpStatus.OK);
}
I tried the above code, but when I hit the api1 through my curl I get the response on the same machine, but I want the response to be redirected to api2 at otherAPIEnv machine.
Could someone please suggest how to achieve this kind of request and response?
When you send a request to a URL it should respond to the same otherwise client will be in waiting for it until it times out.
So, the approach should be different in this scenario.
First, in your main rest API you have to send a response code to release the client.
Then, in the API method you have to call another method asynchronously which calls api2 and performs the desired operation.
Here is a simple example.
#Autowired
API2Caller api2Caller;
#RequestMapping(
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE},
path = "api/api1",
method = RequestMethod.POST,
produces = MediaType.ALL_VALUE
)
#ResponseStatus(HttpStatus.ACCEPTED)
public void api1CallBack(#RequestBody String requestBody, HttpServletRequest request) throws IOException, GeneralSecurityException, URISyntaxException {
api2Caller.callApi2(requestBody);
}
and the APICaller should look like following
#Component
public class API2Caller {
#Async
public SomeResultPojo callApi2() {
// use RestTemplate to call the api2
return restTemplate.postForObject("http://otherAPIEnv/api2", request, SomeResultPojo.class);
}
}
But you can choose your most comfortable way to perform asynchronous operation.
Look like a job for redirect.
String redirectMe() {
return "redirect:http://otherAPIEnv/api2"
}
As for the curl. You have POST mapping of the method so be sure to try it with curl -X POST... or change it to GET.
This the more modular and more generic way to do such kind of things:
public #ResponseBody ClientResponse updateDocStatus(MyRequest myRequest) {
ClientResponse clientResponse = new ClientResponse(CTConstants.FAILURE);
try {
HttpHeaders headers = prepareHeaders();
ClientRequest request = prepareRequestData(myRequest);
logger.info("cpa request is " + new Gson().toJson(request));
HttpEntity<ClientRequest> entity = new HttpEntity<ClientRequest>(request, headers);
String uri = cpaBaseUrl + updateDocUrl ;
ClientResponse serviceResponse = Utilities.sendHTTPRequest(uri, entity);
clientResponse = serviceResponse;
if (serviceResponse != null) {
if (CTConstants.SUCCESS.equalsIgnoreCase(serviceResponse.getStatus())) {
clientResponse.setStatus(CTConstants.SUCCESS);
clientResponse.setMessage(" update success.");
}
}
} catch (Exception e) {
logger.error("exception occurred ", e);
clientResponse.setStatus(CTConstants.ERROR);
clientResponse.setMessage(e.getMessage());
}
return clientResponse;
}
public static ClientResponse sendHTTPRequest(String uri, HttpEntity<ClientRequest> entity) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new SimpleClientHttpRequestFactory());
SimpleClientHttpRequestFactory rf = (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
rf.setReadTimeout(CTConstants.SERVICE_TIMEOUT);
rf.setConnectTimeout(CTConstants.SERVICE_TIMEOUT);
ParameterizedTypeReference<ClientResponse> ptr = new ParameterizedTypeReference<ClientResponse>() {
};
ResponseEntity<ClientResponse> postForObject = restTemplate.exchange(uri, HttpMethod.POST, entity, ptr);
return postForObject.getBody();
}
You need to use redirect and modify the return type of your method
public String api1CallBack(#RequestBody String requestBody, HttpServletRequest request) throws IOException {
return "redirect:http://otherAPIEnv/api2";
}

Various Response Entity in Spring Controller

In my controller I am having if condition and two different response type. I will get response in JSON format from "if" condition, but I am getting response from else condition like unexpected '0 , instead I need to get my error message'.
My controller code snippet
#RequestMapping(value = "/saveuser", produces = { "application/json" }, consumes = { "application/json" }, method = RequestMethod.POST)
public ResponseEntity<?> addUser(#RequestBody TestUser user)
throws NotFoundException {
System.out.println(user.getAnswer().size());
if(questioncount == user.getAnswer().size())
{
return new ResponseEntity<TestUser>(service.addUser(user),
HttpStatus.OK);
}else {
String one="one";
String erromessage = "Only" + questioncount +" questions are allowed";
System.out.println(erromessage);
return new ResponseEntity<String>(erromessage,HttpStatus.NOT_ACCEPTABLE);
}
}

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

Categories

Resources