Hello Web API series tutorials-Web API and internationalization, helloapi
Reference page:
Http://www.yuanjiaocheng.net/webapi/test-webapi.html
Http://www.yuanjiaocheng.net/webapi/web-api-controller.html
Http://www.yuanjiaocheng.net/webapi/config-webapi.html
Http://www.yuanjiaocheng.net/webapi/web-api-route.html
Http://www.yuanjiaocheng.net/webapi/parameter-binding.html
Software Internationalization enables functional and code design to handle multiple languages and cultural customs during software design and document development. When creating different language versions, you do not need to redesign the software engineering method of source code. This is common in many mature software development platforms. For. net developers, we can use the following two methods to internationalize the software.
- Language configuration file
- Resource file
On the. net platform, the internationalization of software mainly relies on the internationalization of working threads. In. in the processing Thread of the net Framework, we set the Thread. the CurrentCulture attribute is used to sort the date, time, number, currency value, and text. The format of the default value of the load Convention and string comparison is determined. By default, this attribute comes from the user culture in "region and language options" of "Control Panel. Of course, you can manually force the attribute value of Thread. CurrentCulture during software running. The CurrentUICulture attribute is used to determine the resource format to be presented to the user. It is most useful for the software operation interface because it identifies the language to use when displaying the UI element. In. net, different UI resource files are usually set for the software, so that the software runtime uses the CurrentUICulture attribute value to select the resource file rendering software interface in different languages. The CurrentUICulture and CurrentCulture attributes of a thread are generally set to the same CultureInfo object, that is, they use the same language and country information, but they can also be set to different objects. For example, when an American borrowed a computer with a Simplified Chinese version in Beijing, the software can meet the needs of American users.
The following example explains that different results are generated when the same date string is converted to a date type for different attributes of Thread. CurrentCulture.
1 static void Main (string [] args) 2 {3 string birthdate = "02/06/2013"; 4 DateTime dateTime; 5 dateTime = DateTime. parse (birthdate); 6 Console. writeLine (dateTime. toString ("MM dd, yyyy"); 7 Thread. currentThread. currentCulture = new System. globalization. cultureInfo ("en-GB"); 8 dateTime = DateTime. parse (birthdate); 9 Console. writeLine (dateTime. toString ("MM dd, yyyy"); 10 Console. read (); 11}
The following example explains that for different Thread. CurrentUICulture attribute values, the Framework selects different resource files to read the values, so as to present different information to users in different languages. Before running, you must create the following resource files in the project.
The content of each resource file is as follows:
The language code in the file name represents different countries and languages used, such as en-US = US English, en-GB = UK English, and so on. If the file name does not contain language code, the default resource file is called by the framework based on the region of the current computer. Because I am in China and use a simplified Chinese operating system, Chinese characters in the default resource file are read.
1 static void Main(string[] args) 2 { 3 string hello; 4 hello = International.Hello; 5 Console.WriteLine(hello); 6 Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("ja-JP"); 7 hello = International.Hello; 8 Console.WriteLine(hello); 9 Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");10 hello = International.Hello;11 Console.WriteLine(hello);12 Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-GB");13 hello = International.Hello;14 Console.WriteLine(hello);15 Console.Read();16 }
In Web APIs, how can we determine the country in which users are from and the Language and Culture they are using? Because Web APIs cannot actively read information from all regions of users, they can only passively obtain such information from users. This information is contained in the HTTP Request Header submitted by the user. For example:
Accept-Language: en-us, en-gb; q = 0.8, en; q = 0.7
In this HTTP request header, the information is the language and regional culture that the user can accept. The parameter q after each country and language is called the relative quality factor (Relative quality factor), Indicating the priority that the user can accept for the language (value range: 0.0 ~ 1.0, the default value is 1.0 ). In general, the above header indicates that the user's first choice is American English. If it is not supported, it doesn't matter. Then, let's talk about British English, other types of English are also supported.
When the above request header information is submitted to the server, it will be encapsulated in the HttpRequestMessage class (System. net. http namespace) in the Headers attribute, we can easily read in the Web API controller. See the following example
1 public void Post(Object obj)2 {3 HttpHeaderValueCollection<StringWithQualityHeaderValue> acceptedLanguages = Request.Headers.AcceptLanguage;4 foreach (StringWithQualityHeaderValue language in acceptedLanguages)5 {6 Debug.WriteLine(language.Value);7 Debug.WriteLine(language.Quality);8 }9 }
Of course, in actual development, the above method is not recommended. A more scientific method is to create a custom DelegatingHandler. In simple terms, DelegatingHandler is a filter in the HTTP request channel. We can read the Accept-Language information in the DelegatingHandler and set the Thread. CurrentCulture and CurrentUICulture attributes of the processing Thread. The source code is as follows:
1 namespace HelloWebAPI. infrastructure 2 {3 public class CultureHandler: DelegatingHandler 4 {5 6 private List <string> supportedCulture = new List <string> () 7 {8 "zh-cn ", "en-us", "ja-jp" 9}; 10 11 protected override System. threading. tasks. task <HttpResponseMessage> SendAsync (HttpRequestMessage request, System. threading. cancellationToken cancellationToken) 12 {13 HttpHeaderValueCollection <StringW IthQualityHeaderValue> acceptedLanguage = request. Headers. AcceptLanguage; 14 if (acceptedLanguage! = Null & acceptedLanguage. Count> 0) 15 {16 StringWithQualityHeaderValue preferredLanguage = 17 acceptedLanguage. OrderByDescending (e => e. Quality ?? 1.0D) 18. Where (e =>! E. quality. hasValue | e. quality. value> 0.0D) 19. firstOrDefault (20 e => supportedCulture. contains (e. value, StringComparer. ordinalIgnoreCase); 21 if (preferredLanguage! = Null) 22 {23 // if needed, you can also set the CurrentCulture attribute 24 Thread. currentThread. currentUICulture = CultureInfo. getCultureInfo (preferredLanguage. value); 25} 26 27 if (acceptedLanguage. any (e => e. value = "*"&&(! E. Quality. HasValue | e. Quality. Value> 0.0D) 28 {29 string selectedCulture = 30 supportedCulture. FirstOrDefault (e =>! AcceptedLanguage. any (31 ee => 32 ee. value. equals (e, StringComparison. ordinalIgnoreCase) & ee. quality. hasValue & 33 ee. quality. value = 0.0D); 34 if (! String. isNullOrWhiteSpace (selectedCulture) 35 {36 // if necessary, you can also set the CurrentCulture attribute 37 Thread. currentThread. currentUICulture = CultureInfo. getCultureInfo (selectedCulture); 38} 39} 40} 41 return base. sendAsync (request, cancellationToken); 42} 43} 44}
In the above Code, assume that the Supported languages include American English, simplified Chinese, and Japanese. If the user supports multiple languages, the DelegatingHandler will also sort by the relative quality factor q in the header information to determine the preferred language.
Open the WebApiConfig. cs file, Register the custom DelegatingHandler, and add the following statement to the static Register method.
1 public static void Register(HttpConfiguration config)2 {3 config.Routes.MapHttpRoute(4 name: "DefaultApi",5 routeTemplate: "api/{controller}/{id}",6 defaults: new { id = RouteParameter.Optional }7 );8 config.MessageHandlers.Add(new CultureHandler());9 }
Application Scenario 1: provide users with responses in different languages based on their country and language
In the project, create an App_GlobalResources folder, which is the exclusive folder of ASP.net and is used to store resource files. Create resource files in different languages. The file contains a string "NotFound" with different values of the same key. This string is used to prompt that the user cannot find the specified file or resource.
In the controller, create an example action as follows:
1 public class EmployeesController: ApiController2 {3 public HttpResponseMessage Get (int id) 4 {5 // business logic, 6 return Request is omitted here. createErrorResponse (HttpStatusCode. notFound, Resources. message. notFound); 7} 8}
For debugging, we use Fiddler. After running the project, we can get the response in different languages by setting the Accept-Lanuage in the HTTP request header.
Application Scenario 2: Processes and submits data submitted by users based on their country and language.
Looking back at the example at the beginning of the article, users in different countries may have different understandings about the same time and date string. For example, "05/06/2013", dates in a format similar to this are common in computers. But it is exactly this expression in the American opinion that it is May 6, 2013, because the United States is used to the date format of MM/dd/yyyy; but in the British opinion it will be June 5, 2013, because the date format they understand is dd/MM/yyyy. Therefore, for Web APIs, if we are faced with users with a complicated cultural background, we have to be careful when processing user data. Otherwise, it will cause irreparable losses. How can this problem be solved?
First, you must know that in the Web API framework, Microsoft has delegated this task to a third-party class library JSON.net for JSON serialization and deserialization. After reading the documentation, we found that JSON.net uses the date converter to convert the date format, and has two built-in commonly used date converters, JavaScriptDateTimeConverter and IsoDateTimeConverter. When these two date converters are running, you can set the thread Culture by reading the Culture attribute of the SerializerSettings class.
Therefore, we can set the Culture attribute in the SerializerSettings class of JSON.net to be internationalized. Therefore, in the WebApiConfig. cs file, we can directly set it.
1 public static void Register(HttpConfiguration config)2 {3 config.Routes.MapHttpRoute(4 name: "DefaultApi",5 routeTemplate: "api/{controller}/{id}",6 defaults: new { id = RouteParameter.Optional }7 );8 config.Formatters.JsonFormatter.SerializerSettings.Culture = new System.Globalization.CultureInfo("en-US");9 }
However, this is not the best solution at all. Because it cannot set the Culture attribute based on the HTTP request header information, the WebApiConfig class is only a configuration class, not a filter, and cannot access HTTP requests.
Therefore, the final solution to the problem is that we should avoid the SerializerSettings class, write the DataTimeConverter class to convert the date, and set the thread's Cultrue to the custom CultureHandler.
The source code of DataTimeConverter is as follows:
1 namespace HelloWebAPI.Infrastructure 2 { 3 public class DateTimeConverter : DateTimeConverterBase 4 { 5 public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 6 { 7 writer.WriteValue(((DateTime)value).ToString()); 8 } 9 10 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)11 {12 return DateTime.Parse(reader.Value.ToString());13 }14 }15 }
At the same time, we also need to change Thread. CurrentThread. CurrentUICulture in the source code of CultureHandler to Thread. CurrentThread. CurrentCulture.
Finally, we need to register the date converter in WebApiConfig. cs. To avoid conflicts with the built-in DateTimeConverter in the Framework, the fully qualified name of the class is used here.
1 public static void Register(HttpConfiguration config) 2 { 3 config.Routes.MapHttpRoute( 4 name: "DefaultApi", 5 routeTemplate: "api/{controller}/{id}", 6 defaults: new { id = RouteParameter.Optional } 7 ); 8 config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new HelloWebAPI.Infrastructure.DateTimeConverter()); 9 config.MessageHandlers.Add(new CultureHandler());10 }
Start the project and use Fiddler to submit the JSON data of the example as follows:
{Id: 123, firstName: "Gates", lastName: "Bill", age: 58, birthdate: "05/06/1955 "}
Set different Accept-Language headers for debugging.