Angular proxy generator ignoring [Produces] attribute
Is there an existing issue for this?
- [x] I have searched the existing issues
Description
Right now, the Angular proxy generator checking only response type, but not response connect type, in case if type is string. This cause weird behavior.
Reproduction Steps
Create simple service:
[Produces("application/json")]
public Task<string> GetStatus()
{
return Task.FromResult("Open")
}
The problem is in the generated code:
getStatus = (config?: Partial<Rest.Config>) =>
this.restService.request<any, string>({
method: 'GET',
responseType: 'string', // <<< WRONG
url: '/api/app/testService/status',
},
{ apiName: this.apiName,...config });
responseType: 'string' is wrong in this case, it must be 'json'
It related to this change: https://github.com/abpframework/abp/pull/6352
Expected behavior
var status = this.testService.getStatus(); //
console.log("status")
Should be value Open
Actual behavior
var status = this.testService.getStatus(); //
console.log("status")
Returned "Open" (note to double brackets)
Regression?
No response
Known Workarounds
No response
Version
8.1.1
User Interface
Angular
Database Provider
EF Core (Default)
Tiered or separate authentication server
None (Default)
Operation System
Windows (Default)
Other information
No response
hi
What is the JSON output of the api/abp/api-definition endpoint?
Thanks
@maliming
"GetStatus": {
"uniqueName": "GetStatus",
"name": "GetStatus",
"httpMethod": "GET",
"url": "api/app/test-service/status",
"supportedVersions": [],
"parametersOnMethod": [],
"parameters": [],
"returnValue": {
"type": "System.String",
"typeSimple": "string"
},
"allowAnonymous": null,
"implementFrom": "My.TestService"
}
hi @antonGritsenko
Produces will convert the return value to JSON.
The status string to JSON will be "status"
A string is a sequence of zero or more Unicode characters, wrapped in double quotes, using backslash escapes. A character is represented as a single character string. A string is very much like a C or Java string.
[Produces("application/json")]
public Task<string> GetStatus()
Why do you use Produces?
Thanks.
Why do you use Produces
@maliming
Because I thought that you are using it to generate correct dataType (which is expected), but then I did check the code and this is not the case. The actual problem that even without [Produces("application/json")] ABP generates JSON response (with double brackets) like "Ready".
ok, what is the Content-Type of the GetStatus HTTP request?
@maliming you know, I think problem is deeper. I have found another sample where is works strange (and I pretty sure it works in v6 of ABP in another way, because this app was migrated from v6). I have endpoint defined like that:
public async Task<IRemoteStreamContent> GetScreenshot(string channelId, [FromQuery] DateTime? time)
{
var stream = await _someService.GetScreenshoot(channelId, time);
return new RemoteStreamContent(stream, fileName: "screenhoot.jpg", contentType: "image/jpeg");
}
Corresponding TS code generated by the proxy:
getScreenshotByChannelIdAndTime = (channelId: string, time: string, config?: Partial<Rest.Config>) =>
this.restService.request<any, Blob>({
method: 'GET',
responseType: 'blob',
url: `/api/app/sec-service/screenshot/${channelId}`,
params: { time },
},
{ apiName: this.apiName,...config });
Looks good so far. The actual problem that request sent by this call is totally wrong:
:method GET :path /api/app/sec-service/screenshot/Ia7HtFlm_cVhDG5pu?time=2025-09-17T19:41:16.218Z :scheme https accept application/json, text/plain, /
With that request I'm getting following in the body:
{"fileName":"screenhoot.jpg","contentType":"image/jpeg","contentLength":786763}
So not an image, but just JSON, but this kind expected - server reply exactly what we've requested with application/json: the JSON object.
RestService sending application/json first, but must sent application/octet-steam. I did check with the Postman, if Accept set to application/octet-steam then actual image is returned.
If I do call like this:
this.localHttpClient.get(`${environment.apis.default.url}/api/app/sec-service/screenshot/${this.selectedRow.channelId}`,{
responseType: 'blob',
headers: {
'Accept': 'application/octet-stream',
'Authorization': `Bearer ${this.oAuthService.getAccessToken()}` ,
},
params: { time: time.toISOString() }
}
).subscribe(r => blob => {
let objectURL = URL.createObjectURL(blob);
this.screenShotUrl = this.sanitizer.bypassSecurityTrustUrl(objectURL);
});
then all works as expected.
Looks like Angular's HttpClient always sending application/json.
hi
Let's go back to the original: what is the Content-Type of the GetStatus HTTP request?
application/json, as requested by the client
Remove [Produces("application/json")] and check the Content-Type again
I'm doing all tests now without [Produces("application/json")], with pure ApplicationService class.
This is correct behavior of the backend from my PoV: it's returning exactly what requested. The Accept set by RestService:
The first in Accept is application/json, if backed support it then it must return it by the standard and ignore all other.
IMHO the proxy generator must add 'Accept' header in case if responseType != 'json'.
One more huge issue with this: if response: 'text' then all errors are ignored by standard ABP error handler in the Angular, and error is hidden.