Internationalization Support for backend Messages in Spring Application - java

I am trying to implement internationalization in my application. I already went through many blogs & tutorials which explain how we can implement it using different libraries.
The one I am planning to use is I18N with spring.
My application's structure is something like this :-
My application's front end (based on Angular2) consumes Rest APIs that are exposed from the backend.
I am using Spring Rest for implementing the Rest APIs. For every API call I am preparing & sending appropriate messages to UI.
Now by default messages are in English but now I want to add internationalization support to it. How can I do it ?
Below is the example of one of the Rest API that I am exposing and the way I'm sending the messages :-
#CrossOrigin(methods = RequestMethod.POST)
#PostMapping(value = "/user/resetUserAccount", produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody ResponseEntity<String> resetUserAccount(#RequestBody InputObj inputObj) {
boolean isUserAccountReset = userService.resetUserAccount(inputObj);
if (isUserAccountReset) {
return new ResponseEntity<String>(successResponse("User Account Reset Successful").toString(), HttpStatus.OK);
}
return new ResponseEntity<String>(failureResponse("Failed to Reset User Account").toString(), HttpStatus.CONFLICT);
}
I have written 2 helper methods given below that prepare the response messages :-
private JSONObject successResponse(String apiMessage) {
JSONObject success = new JSONObject();
success.put("reponse", "success");
success.put("message", apiMessage);
return success;
}
private JSONObject failureResponse(String apiMessage) {
JSONObject failure= new JSONObject();
success.put("reponse", "failure");
success.put("message", apiMessage);
return failure;
}

Add the following to the configuration class
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(Locale.US); // Set default Locale as US
return slr;
}
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasenames("i18n/messages"); // name of the resource bundle
source.setUseCodeAsDefaultMessage(true);
return source;
}
Create a new directory named i18n inside resources directory and put your messages.properties and the other internationalized property files like messages_ru.properties, messages_fr.properties etc inside it. Create message key and values like below:
messages.properties
msg.success=User Account Reset Successful
msg.failure=Failed to Reset User Account
Now inject the MessageSource Bean where you want to internationalize the message, i.e. your controller and then accept the Locale from headers in controller method and get messages from properties files like below:
#Autowired
private MessageSource messageSource;
#CrossOrigin(methods = RequestMethod.POST)
#PostMapping(value = "/user/resetUserAccount", produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody ResponseEntity<String> resetUserAccount(#RequestHeader("Accept-Language") Locale locale, #RequestBody InputObj inputObj) {
boolean isUserAccountReset = userService.resetUserAccount(inputObj);
if (isUserAccountReset) {
return new ResponseEntity<String>(successResponse(messageSource.getMessage("msg.success",null,locale)).toString(), HttpStatus.OK);
}
return new ResponseEntity<String>(failureResponse(messageSource.getMessage("msg.failure",null,locale)).toString(), HttpStatus.CONFLICT);
}

Related

WebFlux Swagger (Open API) integraton - Post Request sample

I have integrated Swagger (OpenAPI) with Spring Webflux as mentioned here: https://springdoc.org/#spring-weblfuxwebmvcfn-with-functional-endpoints using RouterOperation. The integration works fine and is accessible at /swagger-ui.html
However, for POST APIs, I am not seeing the "Request" sample when I click on "Try it out" button. My Post API accepts a Json as Request Body.
How do I configure this ? Can that be done via Annotations along with RouterOperation or something else ?
Edit: Below is my Router class code:
#Configuration
public class MyRouter {
#RouterOperations({
#RouterOperation(path = "/data", beanClass = MyHandler.class, beanMethod = "getData"),
#RouterOperation(path = "/allData", beanClass = MyHandler.class, beanMethod = "getAllData") })
#Bean
public RouterFunction<ServerResponse> route(MyHandler MyHandler) {
return RouterFunctions
.route(RequestPredicates.POST("/data").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), MyHandler::getData)
.andRoute(RequestPredicates.GET("/allData").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), MyHandler::getAllData);
}
}
Upon adding RouterOperations annotation I can see the swagger-ui showing both the GET and POST APIs correctly but not the request schema sample.
I also came across yaml / json file to describe this. But I am not getting where to put this file in my application so that swagger-ui uses it.
Finally found it
Using #Operation and #Schema, can define the class that is required as input in request body. This will be shown as sample json structure in Swagger-ui. No other configuration required.
#RouterOperations({
#RouterOperation(
path = "/data", beanClass = MyHandler.class, beanMethod = "getData",
operation = #Operation(
operationId = "opGetData",
requestBody = #RequestBody(required = true, description = "Enter Request body as Json Object",
content = #Content(
schema = #Schema(implementation = ApiRequestBody.class))))),
#RouterOperation(path = "/allData", beanClass = MyHandler.class, beanMethod = "getAllData")})
#Bean
public RouterFunction<ServerResponse> route(MyHandler myHandler) {
return RouterFunctions
.route(RequestPredicates.POST("/data").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), myHandler::getData)
.andRoute(RequestPredicates.GET("/allData").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), myHandler::getAllData);
}

Swagger basic authorization not working with #Api annotation

Swagger basic authorization not working with #Api annotation .But when used with #Apioperation it is working fine . I want to apply basic authorization at controller level rather than at method level .
used like this :
#RestController
#Slf4j
#Api(value="API related ",authorizations = {#Authorization(value="basicAuth")})
#RequestMapping(value="invoices",produces =MediaType.APPLICATION_JSON_UTF8_VALUE)
#SuppressWarnings("rawtypes")
public class InvoiceController {
#SuppressWarnings("unchecked")
#GetMapping
#ApiOperation(value = "${InvoiceController.getAll.notes}", notes="${InvoiceController.getAll.notes}",response = Invoice.class)
#ApiResponses(value = {#ApiResponse(code = 200, message = "Successfully retrieved list of invoices")})
public #ResponseBody ResponseEntity<Response> getAll(#Valid PaginationDto pagination,#Valid InvoiceFilterCriteriaDto filter)
throws GenericServiceException{
}
}
in main class , created Docket like below by mentioning the basic auth :
List<SecurityScheme> schemeList = new ArrayList<>();
schemeList.add(new BasicAuth("basicAuth"));
return new Docket(DocumentationType.SWAGGER_2)
.forCodeGeneration(true)
.produces(new HashSet<>(Arrays.asList( new String[] { MediaType.APPLICATION_JSON_UTF8_VALUE.toString()})))
.apiInfo(apiInfo())
.securitySchemes(schemeList)
Fixed by adding securityContexts to Docket config.
Please refer to https://www.baeldung.com/spring-boot-swagger-jwt

Using RestTemplate in spring-boot returns with 404

I am trying to send a body in a post request in a springboot application using rest template. Here is the controller:(I removed #RequestBody because I used application/x-www-form-urlencoded header)
#RestController
#CrossOrigin
#RequestMapping("/api")
public class SentimentParserController {
#Autowired
private SentimentParserService sentimentParserService;
#RequestMapping(value = "/something", method = RequestMethod.POST, consumes="application/x-www-form-urlencoded")
public ResponseEntity<mcResponse>getTheSentiments( mcSentimentRequestDTO sentimentRequestDTO){
return sentimentParserService.getSentimentsMc(sentimentRequestDTO);
}
}
I want to send the sentimentRequestDTO object(lang, key, and text) as the body in a post request to get the mcResponse:
public mcResponse parseTheSentiments(String text, Languages lang, String key) throws Exception {
RestTemplate restTemplate = new RestTemplate();
String request = "http://localhost:8080";
mcSentimentRequestDTO mSentiments =new mcSentimentRequestDTO(key,"EN",text);
HttpHeaders headers = new HttpHeaders();
headers.add("content-type", "application/x-www-form-urlencoded");
MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
map.add("key", key);
map.add("txt", text);
map.add("lang", Languages.ENGLISH.toString());
HttpEntity<MultiValueMap<String, String>> request1 = new HttpEntity<MultiValueMap<String, String>>(map, headers);
mcResponse response = restTemplate.postForObject(request, request1 , mcResponse.class );
return response;
}
However, I am getting the following error: 404 null.
Can you please help me? Thanks in advance
and here is the service class:
public ResponseEntity<mcResponse> getSentimentsMc(mcSentimentRequestDTO sentimentRequestDTO){
ResponseEntity<mcResponse> dto = null;
try {
dto = sentimentConverter.getTheSentiments(mcsParser.parseTheSentiments(sentimentRequestDTO.getText(),
Languages.ENGLISH, sentimentRequestDTO.getKey()));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return dto;
}
Looks like variable request should be
String request = "http://localhost:8080/something";
Also if controller class has prefix, this prefix also should be in request.
I mean if your class looks like this
#RestController
#RequestMapping("/myApi")
public class CertificateController {
....
#RequestMapping(value = "/something", method = RequestMethod.POST)
public ResponseEntity<mcResponse>getTheSentiments( mcSentimentRequestDTO sentimentRequestDTO){
return sentimentParserService.getSentimentsMc(sentimentRequestDTO);
}
Then request should be
String request = "http://localhost:8080/myApi/something";
It sounds like the controller isn't getting included in the spring context. If you just have an app annotated with #SpringBootApplication, then make sure that your controller is in a package that is the same as or lower than your annotated application.
To check the controller is being picked up you can add the following logging options to your application.properties
logging.level.org.springframework.beans=debug
logging.level.org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping=trace
When your server starts up you should see something like the following in the log
1. To show the controller is in the spring-context
DefaultListableBeanFactory : Creating shared instance of singleton bean 'sentimentParserController'
2. To show the mapping for the /api/something url
RequestMappingHandlerMapping : Mapped 1 handler method(s) for class SentimentParserController: {public org.springframework.http.ResponseEntity SentimentParserController.getTheSentiments(mcSentimentRequestDTO)={[/api/something],methods=[POST]}}
If you see both of these, then what you say you're doing should work. Just make sure you are sending the request to /api/something and the server is running on port 8080.

How to retrieve file from angular multipart/form data request in spring mvc

I use ngFileUpload directive to send data to the client:
$scope.upload = function (dataUrl, formValid) {
if (formValid && formValid === true) {
console.log($scope.user);
Upload.upload({
url: 'http://localhost:8080/user/',
data: $scope.setPhotoAndReturnUser(Upload.dataUrltoBlob(dataUrl))
.......
$scope.setPhotoAndReturnUser = function (photo) {
$scope.user.photo.image = photo;
return $scope.user;
};
User object contains additional user information username, email etc.
This is how request looks like:
#RestController
#RequestMapping("/user")
public class UserRestController {
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity newUser(HttpServletRequest request) {
MultipartHttpServletRequest mRequest = (MultipartHttpServletRequest) request;
UserDTO user = new UserDTO(mRequest);
if (service.validate(user)) {
registrationService.register(user, request);
return new ResponseEntity(HttpStatus.NO_CONTENT);
}
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
My domain transfer object constructor:
public UserDTO(MultipartHttpServletRequest request) {
this.userEmail = request.getParameter("userEmail");
this.userName = request.getParameter("userName");
this.userPass = request.getParameter("userPass");
And this is the exception i get:
Caused by: java.lang.NoClassDefFoundError:
com/fasterxml/jackson/annotation/JsonProperty$Access
My multipart resolver configuration:
#Bean
MultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(1000000);
return resolver;
}
I use:
jackson-core 2.4.6
jackson-databind: 2.6.4
But i cannot fully understand what is going on, i dont have any domain objects in my method parameters, and in accordance to developer tools the request is encoded like multipart/form data. Should i instead try to use spring anonymous authentication, and split the registration process in to two different requests?
Problem was in the wrong naming of the request parameters, i have to do this instead of sending all data as one object:
Upload.upload({
url: 'http://localhost:8080/user/',
file:Upload.dataUrltoBlob(dataUrl)
fields: {...}
Also i have problem with my maven dependancies , for spring 4 recomended jackson libery is
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.3</version>
</dependency>

Spring : Responding to a REST-ful API call

I am trying to implement REST API endpoints in y application.
#Controller
public class HerokuController {
#RequestMapping(value = "/heroku/resources/", method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<JSONObject> provision(#RequestBody JSONObject body){
System.out.println("called! \n");
JSONObject response = new JSONObject();
response.put("id", 555);
response.put("message", "Provision successful!");
return new ResponseEntity<JSONObject>(response,HttpStatus.OK);
}
So I wrote this class containing a method which mapping is (heroku/ressources).
But when I try to call it, I get a 404 error because /WEB-INF/heroku/resources.jsp not found. However, I don't even want to get a view but a HTTP response.
Can anyone tell me which configuration file should we generally modify to tell Spring that this controller doesn't want to send back a view but a HTTP response?
The method is however called if I change it to this :
#RequestMapping(value = "/heroku/resources/", method = RequestMethod.POST)
public ModelAndView provision(final HttpServletRequest request){
System.out.println("called! \n");
JSONObject response = new JSONObject();
response.put("id", 555);
response.put("message", "Provision successful!");
final Map<String, Object> result = new HashMap<String, Object>();
return new ModelAndView("jsonView",result);
}
So changing the return type to "ModelAndView".
thanks.
You are missing the #ResponseBody
#RequestMapping(value = "/heroku/resources/", method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody ResponseEntity<JSONObject> provision(#RequestBody JSONObject body){
System.out.println("called! \n");
JSONObject response = new JSONObject();
response.put("id", 555);
response.put("message", "Provision successful!");
return new ResponseEntity<JSONObject>(response,HttpStatus.OK);
}
I had the same problem once, for fix that you can use #RestController instead of #controller (this will send Json by default) and you can definy your method like this
#RequestMapping(value = "/heroku/resources/", method = RequestMethod.POST)
public JsonOut provision(#RequestBody JsonIn json)
I always made my object with the value that i will get from the client, and alway the definition of the output
Ex
public class JsonOut{
protected String id;
protected String message;
...set ....get
}
and you have to put in the spring xml file this two value
<mvc:annotation-driven />
<context:annotation-config/>
With this configuration you will have json always!
This will work with spring 4, i dont know if with spring 3 will work

Categories