Showing posts with label rest-api. Show all posts

Spring internationalization(i18n) example for response and errors

Nowadays,  your application has users from around the world. For ease of your user, you want them to use web application in their local language. You must consider internationalization (i18n) while creating your web application.

In following example we will learn how you can apply internationalization to your spring application. In my previous articles we learned Creating first rest api in spring-boot and you can browse all spring article for starter.

Language specific properties file
Create properties file under src/main/resource folder for individual language you want to support in your application. We create package locale under folder and placed all file in it.

messages_de.properties
springboot.hello=Hallo
springboot.customerror=benutzerdefinierte Fehlermeldung
messages_en.properties
springboot.hello=Hello
springboot.customerror=custom error message
messages_fr.properties
springboot.hello=Bonjour
springboot.customerror=message d'erreur personnalisé
messages_zh.properties
For Chinese character you have to convert character / word into Unicode. How to convert Chinese character or word to Unicode online?
springboot.hello=\u4f60\u597d
springboot.customerror=\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f

Source code (ApplicationConfig.java)
Load your language properties file in spring context using ReloadableResourceBundleMessageSource.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
 * @author javaQuery
 * @since 2018-02-18
 * @github https://github.com/javaquery/spring-boot-examples
 */
@Configuration
public class ApplicationConfig extends WebMvcConfigurerAdapter{
    @Bean
    public ReloadableResourceBundleMessageSource messageSource(){
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:locale/messages");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }
 
    /**
     * Internationalization for parameter and payload validation messages. 
     * @return
     */
    @Bean
    public LocalValidatorFactoryBean validator() {
       LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
       bean.setValidationMessageSource(messageSource());
       return bean;
    }

    @Override
    public Validator getValidator() {
       return validator();
    }
}

Source code (Application.java)
Add ApplicationConfig.java file package com.javaquery.examples.springboot.main.config in Application.java for component scan.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

/**
 * @author javaQuery
 * @since 2018-01-31
 * @github https://github.com/javaquery/spring-boot-examples
 * 
 * @change message internationalization (locale)
 */
@SpringBootApplication
@ComponentScan(basePackages = { "com.javaquery.examples.springboot.rest",  "com.javaquery.examples.springboot.main.config"})
@EntityScan("com.javaquery.examples.springboot.model")
@EnableJpaRepositories("com.javaquery.examples.springboot.model.repositories")
public class Application {
    public static void main(String[] args) {
       SpringApplication.run(Application.class, args);
    }
}

Source code (LocaleController.java)
import java.util.Locale;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * Springboot internationalization example
 * @author javaQuery
 * @since 2018-04-06  
 */
@RestController
@RequestMapping("/api")
@Validated
public class LocaleController {
 
    @Autowired
    private MessageSource messageSource; 
 
    /**
     * Response internationalization.
     * @param name
     * @param locale
     * @return
     */
    @GetMapping("/locale")
    public ResponseEntity<?> sayHello(@RequestParam("name") String name, Locale locale){
       String localeHello = messageSource.getMessage("springboot.hello", null, locale);
       return ResponseEntity.ok(localeHello + " " + name);
    }
 
    /**
     * Validation parameter internationalization. 
     * @param firstname
     * @param lastname
     * @param notEmptyParam
     * @param locale
     * @return
     */
    @GetMapping("/locale/param")
    public ResponseEntity<?> parameterValidation(@Size(min = 1, max = 5, message = "firstname {javax.validation.constraints.Size.message}") @RequestParam("firstname") String firstname,
       @NotEmpty(message = "lastname {org.hibernate.validator.constraints.NotEmpty.message}") @RequestParam("lastname") String lastname,
       @NotEmpty(message = "{springboot.customerror}") String notEmptyParam, Locale locale){
         String localeHello = messageSource.getMessage("springboot.hello", null, locale);
         return ResponseEntity.ok(localeHello + " " + firstname + " " + lastname);
    }
}
We used properties file key springboot.hello to send language specific response of "Hello".

Internationalization for error messages
parameterValidation method demonstrate use of internationalization code for validation.
 - You can create your own custom code like {springboot.customerror}.
 - Use inbuilt codes. You find out internationalization code from source of annotation.


Enable Internationalization
To enable internationalization for http request send header accept-language in your request.

Request/Response
cURL POST localhost:8080/api/locale?name=vicky
-H 'accept-language: fr'
-code 200
-body Bonjour vicky
cURL POST localhost:8080/api/locale/param?firstname=VickyV&lastname=Thakor
-H 'accept-language: de'
-code 400
-body {"errors":["firstname muss zwischen 1 und 5 liegen","benutzerdefinierte Fehlermeldung"]}

Creating first rest api in spring-boot

spring-boot
Today we are going to learn how to create rest api in spring-boot. This example is created using gradle, if you are not familiar with how to setup gradle project in eclipse follow the steps stated in Setup first gradle project in eclipse

build.gradle
update your build.gradle as follow and refresh the project, gradle will download the required dependencies(jar files).
buildscript {
    ext {
        springBootVersion = '1.5.7.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
}

Source code (HelloWorldController.java)
Spring follows Model-View-Controller so we'll use widely used package structure and filenames. Now create package like com.example.package.rest.controller
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author javaQuery
 * @since 2018-01-31
 * @github https://github.com/javaquery/spring-boot-examples
 */
@RestController
@RequestMapping("/api") 
public class HelloWorldController {
 
 /**
  * This will send Hello World in response with HTTP status code 200.
  * @return
  */
 @GetMapping("/helloworld")
 public ResponseEntity sayHello(){
  /* return response */
  return ResponseEntity.ok("Hello World");
 }
}
Lets quickly understand annotations used in HelloWorldController.java

  • @RestController - To denote class will be service as rest service
  • @RequestMapping("/api") - default/root path for all apis created under HelloWorldController.java
  • @GetMapping("/helloworld") - sayHello method will serve for GET http request made for path /api/helloworld

Source code (Application.java)
Create Application.java (main-class) in package best suited your project,
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

/**
 * @author javaQuery
 * @since 2018-01-31
 * @github https://github.com/javaquery/spring-boot-examples
 */
@SpringBootApplication
@ComponentScan(basePackages = { "com.javaquery.examples.springboot.rest" })
public class Application {
 public static void main(String[] args) {
  SpringApplication.run(Application.class, args);
 }
}
Lets quickly understand annotations used in Application.java

  • @SpringBootApplication - denotes entry point of spring-boot application
  • @ComponentScan - files under given package(s) are part/component of spring application so spring will process the annotations.
Now run Application.java like any other java application with main method. It'll start local server and your rest API is ready to test. Open up localhost:8080/api/helloworld in your browser.

Project Structure

Source code is also available on my github repository: https://github.com/javaquery/spring-boot-examples. You can also check spring tutorials on http://www.javaquery.com/p/spring-tutorials.html and new you can do Passing and validating RequestParam in spring-boot rest api