-
Notifications
You must be signed in to change notification settings - Fork 519
Angular Server Side Rendering issue with ORIGIN_URL #989
Comments
You need to @Inject() it and prepend your Http calls with ORIGIN_URL as servers need absolute urls. There should be an example of injecting it in one of the components. |
Hi @MarkPieszak. I'm pretty sure I've got it in place the correct way. This is how I'm doing it:
originUrl ends up being undefined, which is why the path ends up being
|
Perhaps try opening the file at Views/Shared/_Layout.cshtml and changing the base href to the following:
See: |
@ADefWebserver Thanks for the suggestion. Just tried it, no luck. Same issue. |
You definitely have this line in app.browser? |
@MarkPieszak Yes, it works client side. The problem is when I go to a specific page deeper in the system and then try to make it do a server side refresh which involves making a server side API call. Do I need a line similar to that in the app.module.server.ts file? I see there are providers specified in boot-server.ts. |
Are you trying to use It looks like you have it in some get (id: number, @Inject('ORIGIN_URL') originUrl: string): Observable<Case> {
return this.httpService.get<Case>(`${originUrl}/api/Cases/${id}`);
} This needs to be in the constructor( /* ...whatever-other-dependencies... */ , @Inject('ORIGIN_URL') originUrl: string ) { }
// now when you do your get method, you can grab it by this.originUrl
get (id: number): Observable<Case> {
return this.httpService.get<Case>(`${ this.originUrl }/api/Cases/${id}`);
} |
@MarkPieszak I tried it in a constructor originally. When I put it in the constructor, I was unable to access it in the method. It complained about it not being accessible. That's when I tried the method. I did change it back to the constructor, but I had to set a private variable in the class using the constructor to a private variable inside of the class. All of this is being done inside of a service, not a component. I'm not sure if that makes a difference. |
It's accessible in the method via One thing I just realized I forgot, and this is probably why it wasn't accessible in your method, make sure you add a private/public accessor infront of the variable name for it. That should do the trick. constructor( @Inject('ORIGIN_URL') private originUrl: string ) { } // note the "private" |
@MarkPieszak It's working now! I honestly am confused as to what is different now versus what I had tried in the past, but I'm glad it's working. Thanks for your help. One other question regarding SSR (and I'll create a separate issue or post on StackOverflow if that's best): our API endpoints are decorated with the Authorize attribute, and we store the token in sessionStorage. Of course, sessionStorage isn't available server side so when SSR occurs, it shows our login page for a few seconds before properly authenticating the user on the client side again with the token and then sending them back to the original page. |
That's a dependency issue issue you'll be dealing with, similar to how you did this here with ORIGIN_URL, you'll have to make abstract out the concept of "Storage" into an abstract class, that you can dependency inject into the client, doing a useClass: BrowserStorage, and the same but different class name and implementation for the server, where it doesn't look in a Storage, by just in some object you passed during the SSR. I'm hopefully releasing some articles in the next week or two on some of these deeper SSR & DI problems as they aren't concepts everyone is as used to! |
Thanks @MarkPieszak. Looking forward to reading them. I understand the concept of what needs to happen. I was doing some more research and see how others have worked through a similar thing, but I'm not sure if there is some sort of best practice at this point, or if it's too early for there to even be a best practice. 😄 |
I had the same question as @AmrineA about the best/recommended way to pass auth tokens to the SSR context. What you wrote makes sense, but I had a follow-up question about this statement: "the same but different class name and implementation for the server, where it doesn't look in a Storage, by just in some object you passed during the SSR." What is the best way to pass the "some object" between the client and the server? I'd like an already-authenticated user to be able to hit "F5" and be able to see the prerendered page with authenticated content display smoothly with no flashing. Do I have to store the auth tokens in cookies to enable this? And if so, should my server side api controllers just be using cookie authorization to begin with (instead of bearer auth)? That would be suboptimal in my case because ideally I'd like my server APIs to work smoothly for mobile clients as well, and bearer auth seems preferable in that context. @MarkPieszak - I can't wait to read the articles you mentioned above. Do you think they will address this question? |
@astegmaier Using cookies is the only way that I've seen used at this point. It's probably the most common thing that traverses both client and server. We too use bearer tokens, which means potentially storing the token in two different places in order for it to work. Not really an ideal scenario, but might be the only option at this point. |
@astegmaier I'm hoping to tap into many common but advanced issues with SSR, these are cross-framework as well, as everyone has the same problems. Hopefully release at least part 1 sometime soon. |
@MarkPieszak I use |
@BillyQin https://github.com/aspnet/JavaScriptServices/blob/dev/templates/AngularSpa/ClientApp/boot-server.ts#L14:
|
@MarkPieszak - did you have a chance to write that article? I wasn't sure where to look for it--medium? twitter? (I'm still spinning my wheels trying to create an optimal auth system that works great both client- and server-side, supports social auth and local accounts together, and (ideally) doesn't involve rolling the whole thing myself. Learning the frameworks while trying to forge a new path is tough, and I was hoping to learn from the masters :-) I appreciate all the work you've done to help us newbies out!) |
When using latest Angular SPATemplate it's BASE_URL now instead of ORIGIN_URL |
I noticed in the out of the box templates, I'm able to go to the Fetch Data page and refresh the browser and it properly does the call out to the server as you would expect. However, in my application, I get a completely different result.
Exception: Call to Node module failed with error: Error: URLs requested via Http on the server must be absolute. URL: undefined/api/Cases/1
I'm importing ORIGIN_URL into the method that needs the value, but it is coming back as undefined for some reason. I've also got it in the boot-server.ts file:
Where does params.origin get its value from? Does that need set somewhere that I'm missing?
The text was updated successfully, but these errors were encountered: