Introduction
When posting from Dart to Golang’s API, I got stuck on how to handle date-type values for nullable DB columns, so I summarized it as a memorandum.
If you don’t allow NULLs, simply define it as time.Time type
If the database column does not allow NULLs, simply time.Time
define the field of the Golang structure as a type, and from the Dart side, convert DateTime
the object into a string using toString()
a method (or toIso8601String()
in ISO 8601 format using ) and send it. Just do it.
Golang can parse ISO 8601 format strings into objects because Golang packages internally understand this format and provide the functionality to parse it correctly. In particular, functions can be used to convert a specified date/time format string into a type value.time.Time
time
time.Parse
time.Time
When performing HTTP communication, data must be in text format, and DateTime
objects cannot be sent as is.
When you send data using Dart’s DateTime
default methods for objects, the resulting string typically contains the object’s complete date and time information, but this format may differ from the ISO 8601 format. For example, may or may not include time zone information, and its format may be inconsistent. Therefore, if you send data using this method, problems may occur when Golang receives and analyzes the data. It is recommended to use.toString()
DateTime
toString()
toIso8601String()
Go
type RequestBody struct {
Date time.Time `json:"date"` // NULLを許容しない場合
}
Dart
import 'package:http/http.dart' as http;
void postData(DateTime date) async {
final response = await http.post(
Uri.parse('http://example.com/api/post'),
headers: {
'Content-Type': 'application/json'
},
body: jsonEncode({
'date': date.toIso8601String(), // DateTimeをISO 8601形式の文字列に変換
}),
);
if (response.statusCode == 200) {
print("Data posted successfully");
} else {
print("Failed to post data");
}
}
Now, I will introduce three ways to use Golang to process date-type values for DB columns that allow nulls.
Allow nulls in pointers
Features :
- On the Dart side, use the nullable DateTime? type, and on the Golang side, use the pointer type *time.Time.
- If it is null on the Dart side, the pointer will be nil on the Golang side.
- Null can be determined based on whether the pointer is nil, so there is no need for a conditional branch to determine null on the Dart side. If you do not want to store empty strings in the DB, separate processing is required, such as storing empty strings as null values in the DB or configuring settings to ignore empty strings and not store them in the DB (characters from (as it is assumed that it points to a memory address)
Dart
import 'package:http/http.dart' as http;
DateTime? date; // null許容のDateTime型
// APIにPOSTする処理
void postData() async {
final response = await http.post(
Uri.parse('http://example.com/api/post'),
body: {'date': date?.toIso8601String()}, // DateTime型をISO 8601形式の文字列に変換して送信
);
}
Go
package main
import (
"fmt"
"net/http"
"time"
)
type RequestBody struct {
Date *time.Time `json:"date,omitempty"` // ポインタ型でnullを許可
}
func handlePost(w http.ResponseWriter, r *http.Request) {
var requestBody RequestBody
// リクエストボディをパースしてRequestBody構造体に格納
// ...
if requestBody.Date != nil {
fmt.Println("Received date:", requestBody.Date)
} else {
fmt.Println("Received null date")
}
}
func main() {
http.HandleFunc("/api/post", handlePost)
http.ListenAndServe(":8080", nil)
}
Use sql.NullTime
When sending from Dart, if it is null, it will be sent as null, and if there is a valid date, it will be sent as a time type value. On the Golang side, use the sql.NullTime type to receive data.
Features :
- Use sql.NullTime type on Golang side to represent nullable time type
- Determine null through the Valid field on the Golang side.
- You need to import the SQL package.
- Conditional branching is required via the Valid field to determine null. Indicates null when Valid is false.
Dart
import 'package:http/http.dart' as http;
DateTime? date; // null許容のDateTime型
// APIにPOSTする処理
void postData() async {
final response = await http.post(
Uri.parse('http://example.com/api/post'),
body: {'date': date != null ? date.toIso8601String() : null}, // nullの場合はnullを送信
);
}
Go
package main
import (
"database/sql"
"encoding/json"
"fmt"
"net/http"
"time"
)
type RequestBody struct {
Date sql.NullTime `json:"date"` // sql.NullTime型を使用
}
func handlePost(w http.ResponseWriter, r *http.Request) {
var requestBody RequestBody
// リクエストボディをパースしてRequestBody構造体に格納
// ...
if requestBody.Date.Valid {
fmt.Println("Received date:", requestBody.Date.Time)
} else {
fmt.Println("Received null date")
}
}
func main() {
http.HandleFunc("/api/post", handlePost)
http.ListenAndServe(":8080", nil)
}
sql.NullTime becomes a dictionary type when serialized to JSON
sql.NullTimeand
also holds Timewhether becomes an object containing fields of and. Therefore, if you try to simply treat this object as a string on the Dart side, the following error will occur.Valid
Time
Valid
sql.NullTime
JSON response ( fields) sent from the Golang server side
JSON
{
"date": {
"Time": "2024-05-06T14:00:00Z",
"Valid": true
}
}
ShellScript
flutter: type '_Map<String, dynamic>' is not a subtype of type 'String' in type cast
In order to properly process this data on the Dart/Flutter side, it is necessary to refer to a specific key in the map and extract the value instead of directly casting it to a string from the map.
Dart
var response = await api.getUser();
var dateMap = response['date'] as Map<String, dynamic>;
if (dateMap['Valid'] == true) {
var dateString = dateMap['Time'] as String; // 正しい日付の文字列を取得
}
Use []uint8
Features :
- Used to represent binary data or string data.
- Even if it is null, the value is sent as an empty string (or an empty byte slice), so a conditional branch is required on the Golang side to distinguish between an empty string and a null.
Byte slice → string → time.Time object
Convert byte slice to string :
requestBody.Date
is a byte slice of type and converts[]uint8
it to a string.string(requestBody.Date)
As a result, the string data originally sent from Dart (for example, a format like “2024-05-01T12:00:00Z”) is converted back to string format.
Parse string date data time.Time
into an object :
- Next, convert
time.Parse(time.RFC3339, string(requestBody.Date))
the date data in string format to a Go object using. defines a date and time format, and strings that follow this format can be converted to objects appropriately.time.Time
time.RFC3339
time.Time
Store in database :
- Finally,
time.Time
use this object to store date data in a TIMESTAMP-type column in the database. This ensures that time information is correctly stored in the database and allows query operations related to date and time.
Dart
import 'package:http/http.dart' as http;
DateTime? date; // null許容のDateTime型
// APIにPOSTする処理
void postData() async {
final response = await http.post(
Uri.parse('http://example.com/api/post'),
body: {'date': date != null ? date.toIso8601String() : null}, // nullの場合はnullを送信
);
}
Go
package main
import (
"fmt"
"net/http"
"time"
)
type RequestBody struct {
Date []uint8 `json:"date"` // []uint8型を使用
}
func handlePost(w http.ResponseWriter, r *http.Request) {
var requestBody RequestBody
// リクエストボディをパースしてRequestBody構造体に格納
// ...
if len(requestBody.Date) > 0 {
date, err := time.Parse(time.RFC3339, string(requestBody.Date))
if err != nil {
fmt.Println("Error parsing date:", err)
return
}
fmt.Println("Received date:", date)
} else {
fmt.Println("Received null date")
}
}
func main() {
http.HandleFunc("/api/post", handlePost)
http.ListenAndServe(":8080", nil)
}
コメント