Skip to content

Commit 0fe33ee

Browse files
committed
Implement basic 301 Redirect when SSR is used.
1 parent 499bfe3 commit 0fe33ee

2 files changed

Lines changed: 42 additions & 3 deletions

File tree

src/app/core/data/dso-redirect.service.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ describe('DsoRedirectService', () => {
2727
const requestUUIDURL = `https://rest.api/rest/api/pid/find?id=${dsoUUID}`;
2828
const requestUUID = '34cfed7c-f597-49ef-9cbe-ea351f0023c2';
2929
const objectCache = {} as ObjectCacheService;
30+
const mockResponse = jasmine.createSpyObj(['redirect']);
31+
const mockPlatformBrowser = 'browser';
32+
const mockPlatformServer = 'server';
3033

3134
beforeEach(() => {
3235
scheduler = getTestScheduler();
@@ -58,6 +61,8 @@ describe('DsoRedirectService', () => {
5861
objectCache,
5962
halService,
6063
router,
64+
mockResponse,
65+
mockPlatformBrowser // default to CSR except where explicitly SSR below
6166
);
6267
});
6368

@@ -141,6 +146,25 @@ describe('DsoRedirectService', () => {
141146
scheduler.flush();
142147
expect(router.navigate).toHaveBeenCalledWith(['/communities/' + remoteData.payload.uuid]);
143148
});
149+
150+
it('should return 301 redirect when SSR is used', () => {
151+
service = new DsoRedirectService(
152+
requestService,
153+
rdbService,
154+
objectCache,
155+
halService,
156+
router,
157+
mockResponse,
158+
mockPlatformServer // explicitly SSR mode
159+
);
160+
remoteData.payload.type = 'item';
161+
const redir = service.findByIdAndIDType(dsoHandle, IdentifierType.HANDLE);
162+
// The framework would normally subscribe but do it here so we can test navigation.
163+
redir.subscribe();
164+
scheduler.schedule(() => redir);
165+
scheduler.flush();
166+
expect(mockResponse.redirect).toHaveBeenCalledWith(301, '/items/' + remoteData.payload.uuid);
167+
});
144168
});
145169

146170
describe('DataService', () => { // todo: should only test the id/uuid interpolation thingy

src/app/core/data/dso-redirect.service.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* http://www.dspace.org/license/
77
*/
88
/* eslint-disable max-classes-per-file */
9-
import { Injectable } from '@angular/core';
9+
import { Inject, Injectable, Optional, PLATFORM_ID } from '@angular/core';
1010
import { Router } from '@angular/router';
1111
import { Observable } from 'rxjs';
1212
import { tap } from 'rxjs/operators';
@@ -21,6 +21,8 @@ import { getFirstCompletedRemoteData } from '../shared/operators';
2121
import { DSpaceObject } from '../shared/dspace-object.model';
2222
import { IdentifiableDataService } from './base/identifiable-data.service';
2323
import { getDSORoute } from '../../app-routing-paths';
24+
import { RESPONSE } from '@nguniversal/express-engine/tokens';
25+
import { isPlatformServer } from '@angular/common';
2426

2527
const ID_ENDPOINT = 'pid';
2628
const UUID_ENDPOINT = 'dso';
@@ -75,12 +77,20 @@ export class DsoRedirectService {
7577
protected objectCache: ObjectCacheService,
7678
protected halService: HALEndpointService,
7779
private router: Router,
80+
@Optional() @Inject(RESPONSE) private response: any,
81+
@Inject(PLATFORM_ID) private platformId: any
7882
) {
7983
this.dataService = new DsoByIdOrUUIDDataService(requestService, rdbService, objectCache, halService);
8084
}
8185

8286
/**
83-
* Retrieve a DSpaceObject by
87+
* Redirect to a DSpaceObject's path using the given identifier type and ID.
88+
* This is used to redirect paths like "/handle/[prefix]/[suffix]" to the object's path (e.g. /items/[uuid]).
89+
* See LookupGuard for more examples.
90+
*
91+
* If this is called server side (via SSR), it performs a 301 Redirect.
92+
* If this is called client side (via CSR), it simply uses the Angular router to do the redirect.
93+
*
8494
* @param id the identifier of the object to retrieve
8595
* @param identifierType the type of the given identifier (defaults to UUID)
8696
*/
@@ -94,7 +104,12 @@ export class DsoRedirectService {
94104
if (hasValue(dso.uuid)) {
95105
let newRoute = getDSORoute(dso);
96106
if (hasValue(newRoute)) {
97-
this.router.navigate([newRoute]);
107+
// If running via SSR, perform a "301 Moved Permanently" redirect for SEO purposes.
108+
if (isPlatformServer(this.platformId)) {
109+
this.response.redirect(301, newRoute);
110+
} else {
111+
this.router.navigate([newRoute]);
112+
}
98113
}
99114
}
100115
}

0 commit comments

Comments
 (0)