-
Notifications
You must be signed in to change notification settings - Fork 504
Description
Describe the bug
When running the DSpace 10 Angular UI after the recent upgrade to Angular 20, the UI fails to bootstrap against a standard DSpace 10 REST backend with the error:
No _links section found at http://localhost:8080/server/api
The DSpace REST API root endpoint returns HAL JSON correctly when requested explicitly with Accept: application/hal+json, but the UI’s browser requests default to Accept: application/json, text/plain, */*, which triggers a non-HAL response (Content-Type: application/json) without _links. This breaks the UI because it requires HAL links from the API root for discovery/navigation.
This appears to be a regression introduced during the Angular 20 upgrade because the current DspaceRestInterceptor only rewrites SSR base URLs and does not set the required HAL Accept header for REST requests.
Observed in:
- DSpace Angular UI:
dspace-angular@10.0.0-next(Angular 20.x) - DSpace REST API: DSpace 10 REST backend (local install)
- Browser: Chrome (also reproducible in other Chromium-based browsers)
To Reproduce
Steps to reproduce the behavior:
- Set up a local DSpace 10 REST API, running on:
http://localhost:8080/server/api
- Set up and build the DSpace Angular UI from the current Angular 20 branch:
npm installnpm run build:prod- start SSR (for example via
npm run serve:ssror via PM2 usingdist/server/main.js)
- Open the UI in a browser (e.g. Chrome).
- Observe the UI fails to bootstrap and logs:
No _links section found at http://localhost:8080/server/api
- Inspect the network request for
GET http://localhost:8080/server/api:- Request header
Acceptis:application/json, text/plain, */* - Response header
Content-Typeis:application/json;charset=UTF-8 - Response body does not contain
_links
- Request header
- Compare with calling the same endpoint explicitly requesting HAL:
curl -i -H "Accept: application/hal+json" http://localhost:8080/server/api- Response is
200 OK Content-Type: application/hal+json;charset=UTF-8- Response contains
_links
- Response is
Expected behavior
The Angular UI should request HAL responses from the REST API root and other REST endpoints, i.e. it should send an Accept: application/hal+json header (at least for /server/api and likely for all REST calls) so that the API returns a HAL response containing _links.
This would prevent the UI from failing during startup and maintain the expected HAL-driven API discovery mechanism.
Suggested fix (code snippet)
One potential fix is to ensure REST requests explicitly request HAL responses by setting Accept: application/hal+json for requests targeting the REST API base URL, while not overriding any explicitly set Accept header (e.g. auth requests that may set application/json intentionally).
Example patch to src/app/core/dspace-rest/dspace-rest.interceptor.ts:
import { isPlatformBrowser } from '@angular/common';
import {
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
} from '@angular/common/http';
import {
Inject,
Injectable,
PLATFORM_ID,
} from '@angular/core';
import {
APP_CONFIG,
AppConfig,
} from '@dspace/config/app-config.interface';
import { isEmpty } from '@dspace/shared/utils/empty.util';
import { Observable } from 'rxjs';
@Injectable()
export class DspaceRestInterceptor implements HttpInterceptor {
protected baseUrl: string;
protected ssrBaseUrl: string;
constructor(
@Inject(APP_CONFIG) protected appConfig: AppConfig,
@Inject(PLATFORM_ID) private platformId: string,
) {
this.baseUrl = this.appConfig.rest.baseUrl;
this.ssrBaseUrl = this.appConfig.rest.ssrBaseUrl;
}
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
let newRequest = request;
// If this is a REST API request and no Accept header was explicitly set, request HAL
const isRestRequest =
request.url.startsWith(this.baseUrl) ||
(!isEmpty(this.ssrBaseUrl) && request.url.startsWith(this.ssrBaseUrl));
if (isRestRequest && !request.headers.has('Accept')) {
newRequest = newRequest.clone({
setHeaders: {
Accept: 'application/hal+json',
},
});
}
// Existing SSR URL rewriting logic
if (!isPlatformBrowser(this.platformId) && !isEmpty(this.ssrBaseUrl) && this.baseUrl !== this.ssrBaseUrl) {
const url = newRequest.url.replace(this.baseUrl, this.ssrBaseUrl);
newRequest = newRequest.clone({ url });
}
return next.handle(newRequest);
}
}Metadata
Metadata
Assignees
Labels
Type
Projects
Status