The previous chapter covered the speech APIs. Throughout this chapter, we will look closer at more language APIs. We will learn how to use spellcheck features. We will then discover how to detect languages, key phrases, and sentiment in text. Finally, we will look at the translator text API to see how we can detect languages and translate text.
By the end of this chapter, we will have covered the following topics:
Before we get into the details, we want to set ourselves up for success. At the time of writing, none of the language APIs that we will be covering have NuGet client packages. As such, we will need to call directly to the REST endpoints. Because of this, we will do some work beforehand to make sure that we get away with writing less code.
We will not be adding the APIs to our smart-house application. Using the following steps, create a new project using the MVVM template that we created in Chapter 1, Getting Started with Microsoft Cognitive Services:
Newtonsoft.Json
. This will help us deserialize API responses and serialize request bodies.MainView.xaml
file, add a TabControl
element. All our additional views will be added as TabItems
in the MainView
.All the APIs follow the same pattern. They call on their respective endpoints using either POST
or GET
requests. Further on, they pass on parameters as query strings, and some as request bodies. Since they have these similarities, we can create one class that will handle all API requests.
In the Model
folder, add a new class and call it WebRequest
.
We also need a few private
variables, as follows:
private const string JsonContentTypeHeader = "application/json"; private static readonly JsonSerializerSettings _settings = new JsonSerializerSettings { DateFormatHandling = DateFormatHandling.IsoDateFormat, NullValueHandling = NullValueHandling.Ignore, ContractResolver = new CamelCasePropertyNamesContractResolver() }; private HttpClient _httpClient; private string _endpoint;
The constant, JsonContentTypeHeader
, defines the content type that we want to use for all API calls. The _settings
phrase is a JsonSerializerSettings
object, which specifies how we want JSON data to be (de)serialized.
The _httpClient
is the object that will be used to make our API requests. The last member, _endpoint
, will hold the API endpoint.
As shown in the following code, our constructor will accept two parameters: one string for the URI, and one string for the API key:
public WebRequest(string uri, string apiKey) { _endpoint = uri; _httpClient = new HttpClient(); _httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", apiKey); }
We assign the uri
to the corresponding member. Next, we create a new object of a HttpClient
type and add one request header. This is the header that contains the given apiKey
.
The class will contain one function, MakeRequest
. This should have the return type of Task<TResponse>
, meaning a type that we specify when calling the function. As you can see in the following code, it should accept three parameters: a HttpMethod
, a query string
, and a TRequest
, (which is a request body that we specify in the call). The function should be asynchronous:
public async Task <TResponse> MakeRequest <TRequest, TResponse (HttpMethod method, string queryString, TRequest requestBody = default(TRequest))
The preceding lines show the complete function signature. Note how we do not need to specify a request body, as there are some cases where it may be empty. We will cover what TRequest
and TResponse
may be in a bit.
We enter a try
clause, as shown in the following code:
try { string url = $"{_endpoint}{queryString}"; var request = new HttpRequestMessage(method, url); if (requestBody != null) request.Content = new StringContent (JsonConvert.SerializeObject(requestBody, _settings), Encoding.UTF8, JsonContentTypeHeader); HttpResponseMessage response = await _httpClient.SendAsync(request);
First, we create a url
, consisting of our _endpoint
and the queryString
. Using this and the specified method
, we create a HttpRequestMessage
object.
If we have a requestBody
, we add Content
to the request
object by serializing the requestBody
.
With the request in order, we make an asynchronous call to SendAsync
on the _httpClient
object. This will call the API endpoint, returning a HttpResponseMessage
containing the response.
If the response
is successful, we want to get the Content
as a string. This is done as follows:
ReadAsStringAsync
. This will return a string.TResponse
object.In the case that there is no data in responseContent
, we return a default TResponse
. This will contain default values for all properties, as shown in the following code:
if (response.IsSuccessStatusCode) { string responseContent = null; if (response.Content != null) responseContent = await response.Content.ReadAsStringAsync(); if (!string.IsNullOrWhiteSpace(responseContent)) return JsonConvert.DeserializeObject<TResponse>(responseContent,_settings); return default(TResponse); }
If the API response contains any error code, then we try to get the error message as a string (errorObjectString
). In a typical application, you would want to deserialize this and propagate it to the user. However, as this is a simple example application, we will choose to output it to the Debug
console window, as shown in the following code:
else { if (response.Content != null && response.Content.Headers.ContentType.MediaType.Contains (JsonContentTypeHeader)) { var errorObjectString = await response.Content.ReadAsStringAsync(); Debug.WriteLine(errorObjectString); } }
Make sure you add the corresponding catch
clause and output any exceptions to the Debug
console window. Also, make sure that you return a default TResponse
if any exceptions occur.
As we need to (de)serialize JSON data as a part of the requests and responses to the APIs, we need to create data contracts. These will act as the TResponse
and TRequest
objects, used in the WebRequest
class.
Add a new folder called Contracts
to the project. A typical data contract may look like the following:
[DataContract] public class TextErrors { [DataMember] public string id { get; set; } [DataMember] public string message { get; set; } }
This correlates to errors in the text analytics API. As you can see, it has two string properties for id
and message
. Both may appear in an API response.
When discussing each API, we will see all request and response parameters in either table form or JSON format. We will not look at how each of these translates into a data contract, but it will take a similar form to that previously shown. It is then up to you to create the contracts needed.
The most important thing to note is that the property names must be identical to the corresponding JSON property.
Make sure that the code compiles and that you can run the application before continuing.