I'm trying to run the access the following service in my code.
Following code segment illustrates the service:
import ballerina/http;
type Album readonly & record {|
string title;
string artist;
|};
type IdNotFound record {|
*http:NotFound;
record {
string message;
} body;
|};
table<Album> key(title) albums = table [
{title: "Blue Train", artist: "John Coltrane"},
{title: "Jeru", artist: "Gerry Mulligan"}
];
service / on new http:Listener(8022) {
resource function get album/[string id]() returns Album|IdNotFound {
if (!albums.hasKey(id)) {
return {body: {message: "No matching resource found."}};
}
return albums.get(id);
}
}
Following is my code in main.bal file
import ballerina/http;
import ballerina/io;
type Album readonly & record {
string title;
string artist;
};
type IdNotFound record {|
*http:NotFound;
record {
string message;
} body;
|};
public function main() returns error? {
// Creates a new client with the Basic REST service URL.
http:Client albumClient = check new ("localhost:8022");
string id = "Blue Train";
Album|IdNotFound album = check albumClient->/album/[id];
io:println("GET request:" + album.toJsonString());
}
When tried to run this code, I'm getting the following error in line Album|IdNotFound album = check albumClient->/album/[id];
line
incompatible type for parameter 'targetType' with inferred typedesc value: expected 'typedesc<(ballerina/http:2.8.0:Response|anydata)>', found 'typedesc<(wso2healthcare/abc:0.2.3:Album|wso2healthcare/abc:0.2.3:IdNotFound)>'(BCE3931)
Can I know what is wrong with my code?
You can only bind the response payload (which is expected to be a subtype of anydata). The error is because you are also using IdNotFound (which is not anydata) to try and bind to it.
You can take a similar approach to the following.
Album|http:ClientError album = albumClient->/album/[id];
if album is Album {
string title = album.title;
io:println("Title: ", title);
return;
}
if album !is http:ClientRequestError {
return album;
}
// album is http:ClientRequestError here
// 4xx errors
record {
string message;
} body = check album.detail().body.ensureType();
io:println("Message: ", body.message);
If you have a requirement to wrap all errors using a spec-defined error message structure, you can use ResponseErrorInterceptor
to change the error structure like this:
type ErrorDetails record {|
string timestamp;
string message;
|};
// This can handle all the interceptable errors
service class ErrorInterceptor {
*http:ResponseErrorInterceptor;
// We can access error and the already built response wrt to the error type
remote function interceptResponseError(error err, http:Response errResponse) returns error {
// Changing all the errors
// Construct the spec defined error body
ErrorDetails errDetails = {
timestamp: time:utcToString(time:utcNow()),
message: err.message()
};
// Return a default status code error with the error body
// Set the cause to the original error to maintain the status code
return error httpscerr:DefaultStatusCodeError("default error", err, body = errDetails);
}
}
service http:InterceptableService / on new http:Listener(8022) {
public function createInterceptors() returns ErrorInterceptor {
return new ErrorInterceptor();
}
...
}