Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ASP.NET Core and SPAs in .NET 6 #27887

Closed
Tracked by #27883
LadyNaggaga opened this issue Nov 16, 2020 · 70 comments
Closed
Tracked by #27883

ASP.NET Core and SPAs in .NET 6 #27887

LadyNaggaga opened this issue Nov 16, 2020 · 70 comments
Assignees
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates Done This issue has been fixed enhancement This issue represents an ask for new feature or an enhancement to an existing one feature-spa Priority:1 Work that is critical for the release, but we could probably ship without User Story A single user-facing feature. Can be grouped under an epic.
Milestone

Comments

@LadyNaggaga
Copy link

LadyNaggaga commented Nov 16, 2020

ASP.NET Core and SPAs in. NET 6

Summary

We will be introducing a new experience for SPA templates in .NET 6 timeframe. This experience is going to focus on decoupling the frontend from the backend into two separate projects.

Motivations and goals

Motivation: Easily enable users to update their Angular and Reacts versions as well as keep up with the latest SPA template.
Goal: Providing flexibility to our community to bring in new templates or update the existing SPA template without having to rely on .NET Core SDK.

This would open the following:

  • Bring your own SPA(BYOSPA) | Simplifying using ASP.NET Core with more SPA frameworks. The JavaScript SPA frameworks ecosystem is growing, and we want to enable the community easily to use the SPA template of their choice.

  • Up-to-date templates: Keep up with the latest versions for your preferred SPA framework.

  • Community templates: enable and encourage community participation to update and bring their own templates to the ecosystem.

What to expect

  • Proxy through the SPA frameworks dev server: Leverage the webpack dev server proxy approach available Preview 4.
    With new proxy work when you ran an app it will start the ASP.NET Process and the process will check if the JS SPA dev server running and start it if it’s not and your app will run.
    image
    spa

  • A new template repo: We will be moving all the JS SPA Frameworks to a new repo in Preview 5 and updates the React and Angular templates.

  • New tooling for JavaScript/TypeScript in Visual Studio: unit testing, a GUI npm management, CLI based templates and more to be announced. This work is being done by the Microsoft TypeScript and JavaScript team; current date of availability is June 15.

  • Separating the Client App and Server App into separate projects in one solution. Below is prototype of experience we are working towards. Date for this work to be completed by June.

MicrosoftTeams-image (2)

@LadyNaggaga LadyNaggaga self-assigned this Nov 16, 2020
@mkArtakMSFT mkArtakMSFT added area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates feature-spa labels Nov 16, 2020
@mkArtakMSFT mkArtakMSFT added this to the Next sprint planning milestone Nov 16, 2020
@ghost
Copy link

ghost commented Nov 16, 2020

Thanks for contacting us.
We're moving this issue to the Next sprint planning milestone for future evaluation / consideration. We will evaluate the request when we are planning the work for the next milestone. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

@mkArtakMSFT mkArtakMSFT added the Priority:1 Work that is critical for the release, but we could probably ship without label Jan 29, 2021
@mkArtakMSFT mkArtakMSFT added this to Committed in .NET 6.0 Jan 29, 2021
@mkArtakMSFT mkArtakMSFT added the User Story A single user-facing feature. Can be grouped under an epic. label Jan 29, 2021
@erikrenaud
Copy link

erikrenaud commented Feb 23, 2021

Not sure if this is the right place for these ideas, but seems on topic...

1 - I find the whole auth story is broken.. We want to build SPAs that are modern. ASP.NETCore has authentication, but not the bits required to interact with the SPA (we need identityserver or openiddict). These two technologies do well in the templates, but if you want to start doing something fancy or go to production, it requires a lot of thinking. I.e. I currently use OpenIDDIct in one project which is great, but i require soooo much code to have my own interfaces to allow admins to manage roles, to create applications so that i can test using postman, my SPA... I think MS should take ownership of OpenIDDict and integrate that into the templates. Then provide some sort of basic UI that can be scaffolded to manage things (like asp.net identity does). Rethinking how to configure OpenIDDict would be nice (basic scenario from appsettings.json that doesn`t require hoops to go to prod).

2 - It would be nice if the SPA services framework did the swagger thing and then generated typescripts artifacts for the SPA. For small projects, having to maintain the typescript projections for the request and replies when they are expressed within the same project in another language is time consuming.

3 - In order to speedup to inner loop, it would be nice if the webpack run would be done in another process instead of a child process, that way restarting the dotnet part wouldn`t cause webpack to recompile everything if nothing had changed. I understand i can run the spa server myself but then i have to "run" two things...

Cheers, Erik.

@micka190
Copy link

Strongly agree with Erik regarding the SPA authentication situation.

Looking up how to write a simple JWT setup without Identity Server provides no real insight outside of outdated (and not necessarily secure) third-party blog posts and YouTube videos.

Identity Server is great, but it's way overkill for smaller projects.

As it stands, we've simply been avoiding writing APIs in ASP.NET Core, since it's easier to get working in other languages.
It'd be nice to be able to use ASP.NET Core for APIs with SPA support natively (i.e. through ASP.NET Core's own Identity instead of through third-party packages, or having to write our own).

@pdevito3
Copy link

pdevito3 commented Feb 24, 2021

On the subject of auth, the hard part I've come to find with SPAs is that they need a code grant flow that issues HTTP only cookies to be 'properly' secured. Every other method of token storing on client side has major security holes. The thing is though, in order to do this, you need your API on the same server as your SPA, or a BFF gateway on the SPA server to handle this exchange for you. The only solutions for this that come to mind for me are really good docs and examples of setting something like this up for prod and possible a bundled service to take care of some heavy lifting.

Regardless, and to the others points, I agree that the docs need a major update to properly detail best practices for authorization and authentication in .NET. Including swagger setup and setting up integration tests. Very common needs that I had to jump around various sources for months to properly get my head around this.

On the auth vein, but not unique to SPAs, I'll also echo Erik in that having a first class authentication server capability (now that IS4 is moving to a paid Duende model) with good documentation would be a big value add. Azure AD gets us part of the way there, but isn't as robust (e.g. no social or custom login) and requires Azure instead of working in .NET Core.

Edit: one more thing I thought of would be adding in an option for an es build tool like snowpack instead of webpack.

@Drabenstein
Copy link

I had been doing a project for my university degree 4 months ago and I certainly agree that current authorization setup is terrible when you use SPA. If you use Razor pages you're good to go with ASP.NET Core Identity and multiple examples. On the other hand in case of SPA you're left to using ID4, building custom solution or using other 3rd party services and there are only few up-to-date examples out there.

ID4 seemed like an overkill for my small-medium app when it would really require at least a week or two to understand it (not to mention the overhead connected to maintaining and deploying another web service for ID4). I then moved to Auth0 which with some of their examples got me up and running in a few days but I would certainly appreciate Razor-like experience for authorization with SPA.

@kevinchalet
Copy link
Contributor

kevinchalet commented Mar 8, 2021

I find the whole auth story is broken.. We want to build SPAs that are modern.

@Tuath FWIW, I don't think it's in such a bad state: the low-level authentication infrastructure is excellent. I had the pleasure of helping the ASP.NET team redesign it for 2.0 and while there are things I don't like (like the virtual scheme thing that leaks from the AuthenticationSchemeOptions base class), its flexibility is insanely good (way better than what you can find on other platforms).

Regarding the higher-level story... yeah, I agree it's less pleasant. E.g we still don't have built-in FIDO/WebAuthN support in Identity (which is a serious limitation in 2021) and the Identity UI doesn't offer any localization hook (which means you basically have to re-scaffold everything when building a non-English website).

These two technologies do well in the templates, but if you want to start doing something fancy or go to production, it requires a lot of thinking. I.e. I currently use OpenIDDIct in one project which is great, but i require soooo much code to have my own interfaces to allow admins to manage roles, to create applications so that i can test using postman, my SPA...

Not sure if you saw that, but Thomas Duft started working on a UI for OpenIddict: https://github.com/thomasduft/openiddict-ui. There's also OrchardCore's OpenID admin UI, that you can easily re-use with vanilla OpenIddict deployments with a few lines of code: https://kevinchalet.com/2020/10/03/using-the-orchardcore-openid-management-feature-with-an-existing-openiddict-deployment/

I think MS should take ownership of OpenIDDict and integrate that into the templates. Then provide some sort of basic UI that can be scaffolded to manage things (like asp.net identity does).

I'd love to see OpenIddict adopted in ASP.NET Core and I'd certainly do my best to make that happen if the ASP.NET team was interested, but - of course, it's not my call. I'm sure this would allow some very nice synergy between the two projects.

I suspect the "IdSrv moving to a commercial model after being officially adopted by MSFT" story left a bad taste in their mouths, so betting on a third-party project a second time may not be an easy decision.

Rethinking how to configure OpenIDDict would be nice (basic scenario from appsettings.json that doesn`t require hoops to go to prod).

Native IConfiguration support sounds interesting. Feel free to open a ticket in the OI repo if you're still interested.

@brockallen
Copy link

We made an announcement today about a new OSS library that implements the BFF pattern to provide security for SPA apps calling APIs: https://blog.duendesoftware.com/posts/20210326_bff/.

@Xyncgas
Copy link

Xyncgas commented Apr 19, 2021

who are you

@leastprivilege
Copy link
Contributor

#32494

@poke
Copy link
Contributor

poke commented May 8, 2021

@LadyNaggaga

With new proxy work when you ran an app it will start the ASP.NET Process and the process will check if the JS SPA dev server running and start it if it’s not and your app will run.

Since this isn’t shown in your example screen recording: If you now terminate the ASP.NET Core application, will the npm process still be kept around? And if you then restart the backend, will it “reconnect” to the still running frontend dev server?

If so, then that’s really great news. The biggest issue I had with the old templates was that the default behavior of launching the npm dev server from your server application meant that you would have to restart it on every server-side change. So I almost always immediately changed it so that it would just proxy to an dev server instead, requiring me to run that separately.

@LadyNaggaga
Copy link
Author

@poke Please find my response inline

Since this isn’t shown in your example screen recording: If you now terminate the ASP.NET Core application, will the npm process still be kept around? And if you then restart the backend, will it “reconnect” to the still running frontend dev server?

spa2

In summary, if you terminate the ASP.NET Core application the npm process will be terminated as well. Thank you for bringing your experience to my attention. It's very useful user feedback.

@poke
Copy link
Contributor

poke commented May 10, 2021

@LadyNaggaga Thanks for the clarifications.

So it’s mostly the same as it is now (e.g. with the UseReactDevelopmentServer()) just that there is a separate window now.

To be completely honest, I don’t really see much benefit there. Building a client-side application is usually very slow on first launch (especially for Angular projects). So what you usually want to do in a development scenario is to run both the server and the client separately so changes on one end will not force a recompilation of the other. That way, when you make changes on the client-side, the running Webpack watcher is able to compile just the changed files very quickly. But when the client-side build pipeline is coupled to the server backend, then that means that with every backend change you will have to restart the slow client-side development process too.

I think this would already be solved if the backend would just not terminate the spawned client-side dev server process. That way, the backend is able to handle both situations (when a dev server is already running, and when it isn’t), and you get huge speed benefits when restarting the backend.

@Drabenstein
Copy link

I think this would already be solved if the backend would just not terminate the spawned client-side dev server process. That way, the backend is able to handle both situations (when a dev server is already running, and when it isn’t), and you get huge speed benefits when restarting the backend.

@poke, I'm not sure but how then would it determine when to kill that external npm process? As of now it's just simple run npm process if dotnet run is executed or run npm on your own (then you control the "lifetime" of the npm process yourself). I suppose always running externally npm would cause ghost process that you would be eventually forced to kill manually trough Task Manager.

As to overall SPA integration, personally I use proxy all the time and it is quite easy to update for newer Angular versions than the template one or out-of-the-box-unsupported frameworks like Vue.js

@poke
Copy link
Contributor

poke commented May 11, 2021

@Drabenstein I wouldn’t expect the backend to kill the npm process at all. Since the npm command is spawned in a separate window (as seen in the GIFs), that window can just stay open when the server process stops or restarts. It would actually feel weird to me if there is a separate window with the npm process and that process gets killed automatically when the server exits.

But yes, if the server process is able to detect and connect to an npm development server that is launched separately, then that would be fine with me too. As long as I don’t need to make code changes to support both proxying to an existing dev server and launching that separately if you just start the server, then I am totally fine with that.

I just think that it would still be better if the default behavior would be improved, and I think that only works if the npm process stays alive after the backend server stopped.

@Drabenstein
Copy link

@poke Oh, ok, I get the point now . Sure, +1 for such smart management and detection of npm window.

@ernestopye
Copy link

@berhir Appreciate your response. Apologies for the confusion on SpaProxy on our end. We're using UseProxyToSpaDevelopmentServer, which gives us the separate backend/frontend behavior already - it doesn't start any node process (just expects it to be there), and the dotnet process acts as the proxy.

I saw your package and we're definitely considering it as an option - thanks for your work on that!

We are currently getting setup using the .NET 6 preview and are hoping it'll work for us.

@POFerro
Copy link

POFerro commented Oct 1, 2021

Hi guys,

This is terrible news for the development cycle for those, like me, who organize their team's work by functionality ( or feature ).

In my development cycle the team develops FE and BE tasks in a row and one feature is developed by one team member ( the full feature, end to end ). Visual Studio angular template was the only development tool that allowed me to enclose the full code to support a user interface fully organized in a single folder as viewed in this image:
image

One of the main reasons for me to defend .NET development and Visual Studio was the ability for the team member to focus on functionality and not on the technicalities underneath, also gave the ability to develop micro front-ends and micro-services according to this vision.

Am I and people like me going to have an alternative? Dividing the project into FE->BE projects makes people loose the notion of what depends on what and what is self contained functionality and what is reused therefore, bug correction, for example will lead to fear of changing something that is "not only used by my feature, so I'll leave it there and create a new version on the side".

Also the deployment practice of separating FE from BE ( different IIS aps or different servers as I've seen sometimes ) brings more pain than gain and is something that I've been struggling against on all customers, this change will lead more teams to make that mistake.

One app ( being it SPA, MPA or exe ) is one app, glued together by it's functional nature, if it's to be split is in self contained functionality to avoid dependencies, the more we move away from this real world fact the more technology will fight functional evolution of our systems, this is something I learned along the many years I've been in the industry ;).

After the long talk, the question :).
Is there going to be an alternative to keep FE and BE together as used to be? Any template alternative to allow this kind of development cycle?

Thanks and best regards.
POFerro

@javiercn
Copy link
Member

javiercn commented Oct 1, 2021

Also the deployment practice of separating FE from BE ( different IIS aps or different servers as I've seen sometimes ) brings more pain than gain and is something that I've been struggling against on all customers, this change will lead more teams to make that mistake.

Even if the two are in separate projects, it won't change the deployment. At publish time the files for the SPA are copied into the wwwroot folder of the server.

In my development cycle the team develops FE and BE tasks in a row and one feature is developed by one team member ( the full feature, end to end ). Visual Studio angular template was the only development tool that allowed me to enclose the full code to support a user interface fully organized in a single folder as viewed in this image:

@POFerro I'm not sure how you were modifying the template (I'm assuming you removed the ClientApp folder and put everything in the root folder)?

Everything is currently on the same project, the only thing that is different is that we launch the SPA proxy in front of the backend (while in the past it was the opposite way, but to be absolutely clear, there's always been a frontend dev server running during development, we just changed the order) instead of the backend proxying to the front end.

In the future, we might move to a multi-project approach since it's harder to provide an improved JS experience on the same project type. One of the goals of having a separate JS project is that it can define what it means to restore, build, publish, test and run, and offer a better experience for running and debugging tests within VS and many other aspects of the dev experience.

Potentially having a separate project type is more about tooling than anything else, the underlying source of truth for how your client pieces build is going to continue to be webpack, rollup, or whatever tool you are using to build your client assets.

Hope this helps clarify things.

@javiercn
Copy link
Member

javiercn commented Oct 1, 2021

Could someone please confirm my understanding of the new experience?

  1. Currently, with SpaProxy, requests go: dotnet -> proxy -> node. With the new changes, it's backwards, so node -> proxy -> dotnet. Meaning any requests while in development have to go through node first, not dotnet. Is that correct?
  2. For us, currently (with SpaProxy), we have to start dotnet/node separately, which is really nice. It sounds like with these new changes, starting the dotnet app will start node by itself, which I can see having some benefits (we have developers who forget to start node sometimes). The part that worries me is the node process being terminated when dotnet is terminated. Does that still happen if I start the node process myself, rather than letting dotnet do that for me?

So for us, the main change will be to make sure developers hit the correct URL when developing locally, but our final product shouldn't change much.

As a side note, it always struck me as weird that these are called SPA proxies. We use this proxy for MPAs, and it does a great job allowing us to have a build process for JS/CSS, while still being served in a more traditional way. Particularly important for us as we allow dotnet to do as much server-side rendering as possible, and we just let the client pick it up later. Any thoughts on that? Just curious if this sort of setup is on your radar.

@ernestopye here is how it works. You have two optioons

  • Start the frontend manually: We don't launch the frontend ourselves and use the existing instance instead.
    • When the dotnet process stops, we don't do anything.
  • dotnet starts the frontend: If we don't detect a SPA proxy instance running, we launch our own.
    • When the dotnet process stops, we clean up the proxy instance. This is consistent with the existing behavior in previous versions of ASP.NET Core.

In summary, if you want the proxy to survive across dotnet refresh cycles, launch it manually. Otherwise, we launch it for you, but we will clean it afterwards.

@javiercn
Copy link
Member

javiercn commented Oct 1, 2021

@mcardoalm auth works because the SPA frontend transparently forwards the request to the backend on the appropriate urls. If you instantiate the angular template with auth for example, you can navigate to /Identity/Login and you'll see that its serving the content from the backend in the origin for the proxy.

The proxy forwards headers to ASP.NET Core and ASP.NET Core uses that information transparently to emit cookies and other things correctly.

@ernestopye
Copy link

Could someone please confirm my understanding of the new experience?

  1. Currently, with SpaProxy, requests go: dotnet -> proxy -> node. With the new changes, it's backwards, so node -> proxy -> dotnet. Meaning any requests while in development have to go through node first, not dotnet. Is that correct?
  2. For us, currently (with SpaProxy), we have to start dotnet/node separately, which is really nice. It sounds like with these new changes, starting the dotnet app will start node by itself, which I can see having some benefits (we have developers who forget to start node sometimes). The part that worries me is the node process being terminated when dotnet is terminated. Does that still happen if I start the node process myself, rather than letting dotnet do that for me?

So for us, the main change will be to make sure developers hit the correct URL when developing locally, but our final product shouldn't change much.
As a side note, it always struck me as weird that these are called SPA proxies. We use this proxy for MPAs, and it does a great job allowing us to have a build process for JS/CSS, while still being served in a more traditional way. Particularly important for us as we allow dotnet to do as much server-side rendering as possible, and we just let the client pick it up later. Any thoughts on that? Just curious if this sort of setup is on your radar.

@ernestopye here is how it works. You have two optioons

  • Start the frontend manually: We don't launch the frontend ourselves and use the existing instance instead.

    • When the dotnet process stops, we don't do anything.
  • dotnet starts the frontend: If we don't detect a SPA proxy instance running, we launch our own.

    • When the dotnet process stops, we clean up the proxy instance. This is consistent with the existing behavior in previous versions of ASP.NET Core.

In summary, if you want the proxy to survive across dotnet refresh cycles, launch it manually. Otherwise, we launch it for you, but we will clean it afterwards.

Appreciate the clarification. This should work for us. We'll be migrating to .NET 6 early 2022, so we'll provide feedback along the way. Thank you very much.

@javiercn
Copy link
Member

javiercn commented Oct 1, 2021

@poke

  • This requires the use of a client-side development server that supports proxying. This might not be an issue currently where almost everything (unfortunately) relies on Webpack but this might change in the future and proxy capabilities might not always be given. So this is almost feels like a vendor lock-in for Webpack here.

This requires the client-side technology to have a dev-server (which all do) and it's not tied to webpack. They all use http-proxy-middleware under the hood.

  • Complex authentication situations, like Windows authentication or certificate-based authentication, are likely not supported now at all. This would at least break my current project.

Haven't done much research, but I believe there's likely a solution for these.

  • Advanced Kestrel features might not be supported at all with a Node-based proxy in front of it. Things like HTTP/2 or even the newer HTTP protocols come to mind here. This might affect things like Web gRPC.

Node has http/2 and http/3 support. I suspect is doable to make it work.

Overall, I don’t really trust a Node development server to be really capable enough for advanced situations. Sure, it likely works for the simple case but I will have to test out if this approach would work for my situations. It does put a lot of dependency on the SPA development server though, which is a move I don’t really like.

It works for Google and Facebook, so I think its likely going to work for everyone.

In the end I would say the following. Angular, React and other SPA framework developers likely have the same needs that many .NET customers do, so I don't think its unreasonable to think that there are solutions for them in the same way they exist in .NET.

In general the templates leverage the approach decided by the given SPA framework, which helps tap into their vast knowledge base and doesn't require a .NET specific solution.

You get to customize how you setup your front-end as much as you want. If you take it to the extreme, it's possible for you use a completely customized dev-server by putting together the pieces the ecosystem provides (node, express, http-proxy-middleware). So there is no vendor locking here unless the underlying SPA framework forces you to it.

Hope this helps.

@drdamour
Copy link

drdamour commented Oct 1, 2021

Also the deployment practice of separating FE from BE ( different IIS aps or different servers as I've seen sometimes ) brings more pain than gain and is something that I've been struggling against on all customers, this change will lead more teams to make that mistake.

One app ( being it SPA, MPA or exe ) is one app, glued together by it's functional nature, if it's to be split is in self contained functionality to avoid dependencies, the more we move away from this real world fact the more technology will fight functional evolution of our systems, this is something I learned along the many years I've been in the industry ;).

Yes, agree 100% and this seems like a change to accommodate this broken model thats swept our industry. I want it to work locally like it does in production with the request coming thru dotnet first. If i ran in prod with the request hitting node first, then id be all for this. Locally node has to know what it should pass thru to dotnet, in prod .net has to know what it should pass thru. Its likely no problem when your whole app url scheme leads to SPA pages but every app ive built has mvc razor views right next to SPA pages.

@javiercn
Copy link
Member

javiercn commented Oct 1, 2021

@Daniel15

Why is the reverse proxy needed? To me it seems like it'd be fine to run the backend and frontend servers on separate ports, and just perform requests to the correct port (ie you load the app from the frontend server, then it performs API calls to the backend server on a different port). I guess that might have issues if you also serve controllers from the backend server You'd have to set the correct CORS headers, but ASP.NET has middleware for that so it'd be fine, and you might need to do that in production anyways - some sites run the backend server on an api. subdomain, while the HTML+JS+CSS can be served from a CDN.

The reverse proxy offers the illusion of everything being served from the same origin. Using CORS in development introduces a lot of complexity. If you are deploying your FE and your BE to separate origins, it might make sense, but this is not the recommended starting point.

@javiercn
Copy link
Member

javiercn commented Oct 1, 2021

@drdamour

Yes, agree 100% and this seems like a change to accommodate this broken model thats swept our industry. I want it to work locally like it does in production with the request coming thru dotnet first. If i ran in prod with the request hitting node first, then id be all for this. Locally node has to know what it should pass thru to dotnet, in prod .net has to know what it should pass thru. Its likely no problem when your whole app url scheme leads to SPA pages but every app ive built has mvc razor views right next to SPA pages.

ASP.NET will give preference to existing .NET endpoints over the SPA fallback, so if you have a route that matches a page or other endpoint that will be chosen over the default SPA.

@drdamour
Copy link

drdamour commented Oct 1, 2021

ASP.NET will give preference to existing .NET endpoints over the SPA fallback, so if you have a route that matches a page or other endpoint that will be chosen over the default SPA.

In prod it will, but as i understand it locally node is in charge and can swallow routes destined for asp.net

@javiercn
Copy link
Member

javiercn commented Oct 1, 2021

In prod it will, but as i understand it locally node is in charge and can swallow routes destined for asp.net

That's fair, you do need to configure the backend routes in the proxy config. However, its potentially doable to keep this in sync automagically (at least for controllers/pages that use endpoint routing) by listing the routes on the server, placing them in a json file, and having the proxy.conf.js consume the routes from there.

We haven't pursued this as part of the default template experience, but it's not too far fetched to do on your own.

@poke
Copy link
Contributor

poke commented Oct 1, 2021

@javiercn

This requires the client-side technology to have a dev-server (which all do)

All is a very strong claim. It may be true for the current generation of client-side frameworks but may not be the case for all of them. And just any dev server isn’t enough, it needs to be a dev server that is able to properly proxy the requests to the backend, which again raises the bar for “compatible” frameworks you are allowing with this approach.

Compare that to ASP.NET Core/Kestrel which is already a full fledged production web server that has a very predictable feature set and exactly matches the production environment the application will eventually be hosted on.


Having the actual web server be the web server would also help in mixed content situations where the SPA (and API) maybe is not the only thing that is being hosted. For example, in an application I inherited, the ASP.NET Core application serves both a React SPA and some Razor pages that take a higher precedence in the routing. I’m not saying that this is a good idea or even a common thing but this worked in the proxy setup where the ASP.NET Core application did the proxying.

And for me personally, the Windows authentication story is very critical. In the .NET space, Windows authentication is still commonly used for internal business applications. And I feel like there should be a solution outlined by MS if the “new way to do it“ has the proxy roles reversed. Because from my experience, Node does not work well with Windows authentication.


One question I think that hasn’t been brought up yet: What can we expect with the previous UseProxyToSpaDevelopmentServer from AspNetCore.SpaServices.Extensions? I haven’t looked at the .NET 6 release/code yet but will this still remain supported for the future and should we expect it to be pulled eventually? Are there any plans yet?

@POFerro
Copy link

POFerro commented Oct 1, 2021

Hi @javiercn ,

Thanks for the answer.

Even if the two are in separate projects, it won't change the deployment. At publish time the files for the SPA are copied into the wwwroot folder of the server.

This is good news, the easy way for teams to go should ( in my opinion ) be to deploy all together, people always go for the default behavior of Visual Studio ;).

I'm not sure how you were modifying the template (I'm assuming you removed the ClientApp folder and put everything in the root folder)?

I don't modify anything, just put BE controllers on the same folder as angular components ( inside ClientApp folder ), everything works perfectly since CSharp compilation runs for every cs file in the project, inside or outside ClientApp.

Everything is currently on the same project, the only thing that is different is that we launch the SPA proxy in front of the backend (while in the past it was the opposite way

I'm not sure what you mean by "currently everything is on the same project", currently is on the 5.x template or the current state of the proposed 6.x template? What I see in the proposal is one project for FE and another for BE, meaning that if a developer is developing a FE UI component it has to go to one folder to develop the front UI and then move to another separate folder to develop the supporting BE api, there is no physical correspondence between the two ( even if it is just a visual illusion ).

, but to be absolutely clear, there's always been a frontend dev server running during development, we just changed the order) instead of the backend proxying to the front end.

Yes, I totally understand that underneath things are separate, but that's where it should be, hidden from developers that are focused on the functionality they are developing, that was the beauty of current templates and tooling, they allowed for this abstraction ;).

In the future, we might move to a multi-project approach since it's harder to provide an improved JS experience on the same project type. One of the goals of having a separate JS project is that it can define what it means to restore, build, publish, test and run, and offer a better experience for running and debugging tests within VS and many other aspects of the dev experience.

Potentially having a separate project type is more about tooling than anything else, the underlying source of truth for how your client pieces build is going to continue to be webpack, rollup, or whatever tool you are using to build your client assets.

This is my point we're letting tooling development problems interfere with functionality development, what you mention are the technicalities around developing a SPA tooling set, I understand the difficulties but would rather listen something like: "We as tooling developers are committed to hide the technical challenges of current development stacks and deployment so that developers can focus on the functional problems they have in hands and don't have to worry about how things are deployed or tested underneath" -> That would make me really happy ;).

Thanks and best regards
POFerro

@javiercn
Copy link
Member

javiercn commented Oct 1, 2021

@poke

All is a very strong claim. It may be true for the current generation of client-side frameworks but may not be the case for all of them. And just any dev server isn’t enough, it needs to be a dev server that is able to properly proxy the requests to the backend, which again raises the bar for “compatible” frameworks you are allowing with this approach.

I mean, how do you develop without a dev server? JS frameworks have these because they add features like HMR and so on. If you are (let's take it to the extreme) writing a vanilla JS SPA and you don't need any HMR or things like that, then you can serve the files directly from the ASP.NET Core app without a proxy at all.

No proxy is required for development nor production, the proxy is there because it adds convenient functionality to apps during development. If you don't need it, you are not forced to use it and can update the template in a few steps to do so.

Compare that to ASP.NET Core/Kestrel which is already a full fledged production web server that has a very predictable feature set and exactly matches the production environment the application will eventually be hosted on.

It's not clear what you are advocating for here. To bypass the given frameworks experiences and provide our own custom experience for all frameworks?

Having the actual web server be the web server would also help in mixed content situations where the SPA (and API) maybe is not the only thing that is being hosted. For example, in an application I inherited, the ASP.NET Core application serves both a React SPA and some Razor pages that take a higher precedence in the routing. I’m not saying that this is a good idea or even a common thing but this worked in the proxy setup where the ASP.NET Core application did the proxying.

The main issue here is that .NET does not know the routes defined in JS and JS doesn't know the routes defined in .NET. It is potentially possible to keep these in sync at least for things that use endpoint routing, while the other way around is not possible.

And for me personally, the Windows authentication story is very critical. In the .NET space, Windows authentication is still commonly used for internal business applications. And I feel like there should be a solution outlined by MS if the “new way to do it“ has the proxy roles reversed. Because from my experience, Node does not work well with Windows authentication.

We can add a doc request for it @Rick-Anderson.

One question I think that hasn’t been brought up yet: What can we expect with the previous UseProxyToSpaDevelopmentServer from AspNetCore.SpaServices.Extensions? I haven’t looked at the .NET 6 release/code yet but will this still remain supported for the future and should we expect it to be pulled eventually? Are there any plans yet?

We have no plans to keep updating that approach. As for the future, we don't have concrete plans yet.

@javiercn
Copy link
Member

javiercn commented Oct 1, 2021

I'm not sure what you mean by "currently everything is on the same project", currently is on the 5.x template or the current state of the proposed 6.x template? What I see in the proposal is one project for FE and another for BE, meaning that if a developer is developing a FE UI component it has to go to one folder to develop the front UI and then move to another separate folder to develop the supporting BE api, there is no physical correspondence between the two ( even if it is just a visual illusion ).

In 5.0 and in 6.0 there is only a single project and all files reside in the ClientApp folder. In the future, that might change and the client pieces might be separated into their own project.

That said, even if our templates do that in the future (which is TBD) you are free to change it for your current approach. Templates are only a starting point.

This is my point we're letting tooling development problems interfere with functionality development, what you mention are the technicalities around developing a SPA tooling set, I understand the difficulties but would rather listen something like: "We as tooling developers are committed to hide the technical challenges of current development stacks and deployment so that developers can focus on the functional problems they have in hands and don't have to worry about how things are deployed or tested underneath" -> That would make me really happy ;).

I don't think this is the case. Runtime and tooling work together in many cases to achieve a productive end to end experience. It's pretty common that "toolability" influences the design of runtime features, and it's a good thing, because productivity comes from the synergy between both.

hope this helps clarify some questions.

@drdamour
Copy link

drdamour commented Oct 1, 2021

In prod it will, but as i understand it locally node is in charge and can swallow routes destined for asp.net

That's fair, you do need to configure the backend routes in the proxy config. However, its potentially doable to keep this in sync automagically (at least for controllers/pages that use endpoint routing) by listing the routes on the server, placing them in a json file, and having the proxy.conf.js consume the routes from there.

We haven't pursued this as part of the default template experience, but it's not too far fetched to do on your own.

I see that as a workaround for a design flawed by its limited viewpoint and sidesteps the main concern that local is now fundamentally different than prod.

theres no 1 right answer to this problem, forcing us one way or the other with the ootb sdk and docs is wrong. Ive watched projects mature from .net is only /api stuff so frontend should be in front to the flipped model many times, ive seen em go the other way to..though more infrequent. Its too bad this change isnt a new option, instead of just being “the way”

@poke
Copy link
Contributor

poke commented Oct 1, 2021

@javiercn

It's not clear what you are advocating for here. To bypass the given frameworks experiences and provide our own custom experience for all frameworks?

My point is that the “ASP.NET Core application serves the API and the SPA” is the production setup, where the ASP.NET Core server responds to the requests. By swapping this around during development, you are creating a higher separation between development and production hosting modes, while relying on the SPA development server to support all the server functionality that you need (and have) during production.
That adds a lot of dependencies on the development server which may not be designed to support proxying, especially with things like websockets or Windows authentication, or even just not operate at the performance that the Kestrel server is able to push.

The main issue here is that .NET does not know the routes defined in JS and JS doesn't know the routes defined in .NET. It is potentially possible to keep these in sync at least for things that use endpoint routing, while the other way around is not possible.

Yes, the problem is pretty clear. But there is a simple solution that matches the production experience: .NET endpoints go first, and then there is a single fallback endpoint that forwards all remaining things to the SPA so it’s able to do client-side routing.
That’s always how this works since the server responds to the request first, before client-side routing has a chance to do something.

But with the proxies reversed, this is no longer the case, so the SPA development server (a third system) will need to be able to distinguish between what’s a server-side route and what’s a client-side route. That adds complexity that you simply didn’t need to care about before (and don’t care about during production either).

We have no plans to keep updating that approach. As for the future, we don't have concrete plans yet.

Okay. As long as you don’t pull the plug on that for now, I can accept this change even though I don’t agree with the decisions being made here. I guess you will properly announce this as a breaking change if/when you decide to obsolete the SpaServices.Extensions?

@mkArtakMSFT mkArtakMSFT added the enhancement This issue represents an ask for new feature or an enhancement to an existing one label Oct 18, 2021
@Akridian
Copy link

Akridian commented Nov 3, 2021

Hello. Really don't understand how to manage my app to work with a new approach.

Currently I have app.Use(/* some condition-based authentication here */) before my SPA to challenge OIDC provider.

And now SPA handles requests before my middleware so all front end request start falling with 401 code.

How should I register my middleware before SPA dev-server magic?

@cantti
Copy link

cantti commented Nov 11, 2021

Lots of changes. There is not enough documentation for the old Microsoft.AspNetCore.SpaServices.Extensions and the new Microsoft.AspNetCore.SpaProxy. To add SpaProxy to an existing project, you have to watch how the template works. Hope it gets fixed.

@erictor
Copy link

erictor commented Nov 22, 2021

The forced proxy for running .NET 6 Angular gives me a problem in that I have my WebAPI in a separate project. I have validated that while running the app (non-production through Visual Studio 2022), the WebAPI project port is set at the original port # -- the port# before it gets changed by the proxy (for example from :7001 to :44401). So calls that worked to the API in .NET 5 now all return 404 (Not Found). When I try to change api URI string port# back to :7001 to call the WebAPI, I guess CORS (?) not being set is preventing that? Can't get the app to work yet; confusing.

@erictor
Copy link

erictor commented Nov 26, 2021

OK the solution for what I was confused about in my previous post was just to add /api in proxy.config.js -- no CORS settings needed -- and they don't seem to help with this anyway (at least I haven't had luck). So I have almost everything that used to work in my (previously .NET 5, now .NET 6, based on the MS .NET + Angular template) working. Great! There's only one thing I still can't get working and that is SignalR. I've learned I needed to add the /hubURL to proxy.config.js to get rid of some issues, but the automatically-imposed proxy thing with the different localhost:ports still seems to be preventing SignalR from working. The error I'm getting is:

"Error: Failed to start the transport 'WebSockets': Error: WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled."

If I add completely-opened-up CORS settings in Program.cs it does not stop this error.

To get past this, do I have to give up the approach that is the default of the .NET 6 Angular template (where there's one Program.cs / one project by default), and avoid the whole proxy thing by manually starting the SPA part separately from the server part? Folks seem to be advocating that approach. Everything I'm trying to do is working except SignalR using the approach where there's just one click and everything starts up.

@miguellira
Copy link

@erictor Did you try adding enabling web sockets in the Angular app's proxy.config?

const PROXY_CONFIG = [
  {
    context: [
    ],
    target: "https://localhost:5001",
    secure: false,
    ws: true
  }
]

module.exports = PROXY_CONFIG;

This worked for me.

I successfully migrated a similar Angular/.NET app targeting netcoreapp3.1 to net6 while it was in beta. However, when upgrading to major framework changes - whether .NET or Angular - I often take the approach of starting from scratch using the CLI of each and copying over my old files. It's a pain for both frameworks, but helps me keep the codebase up to date with each framework's latest approach or guidance.

Good luck!

@erictor
Copy link

erictor commented Nov 29, 2021

@miguellira Thank you for that "ws: true" setting; I needed to learn about that. I also ended up needing to resort to a new sample-app showing SignalR, as my 'legacy' SignalR stuff was not connecting to the hub, it was hanging in the 'Connecting' state. But stuff is working now.

I found that everything I needed to work, worked, without needing to set up CORS ( builder.Services.AddCors( ... ) / app.UseCors() ).

So, the learning experiences I had getting an existing .NET 5 Angular SPA with WebAPI and SignalR to work under VS2022 and .NET 6:

o Needed to learn the updated coding paradigm in Program.cs
o Needed to get used to the proxy starting and terminal windows that pop up outside of VS.
o Needed to learn that VS doesn't necessarily auto-run npm when package.json is updated within VS, like it did in VS2019. Within VS one can open up a Terminal window on ClientApp and run npm commands.
o Needed to deal with an issue that I had with the .NET 6 Angular template where wwwroot didn't work as the root for the web app (maybe it was just me, maybe it's yet something else I need to learn about running from a proxy, but I had to work around this by putting things like favicon.ico in my assets folder).
o Needed to learn how to appropriately edit proxy.conf.js to support WebAPI (if the api routing is prefixed, for example, with /api) and to support SignalR.
o Needed to update my SignalR code, learning by examining a new SignalR sample.

@blogcraft
Copy link

Did spa template (angular) supports hot reload? I can't make it work using VS Code or VS Studio.

@erictor
Copy link

erictor commented Nov 29, 2021

@blogcraft Yes. I can vouch that the new .NET 6 Angular 12 Template is doing a good job with Hot Reload. (For me, the wwwroot folder didn't work as I expected as a / root folder - it did on their previous template. You can also "scaffold"-in authentication if desired using right-click on the project, and SignalR can work with the template too.) They do have another "standalone" Angular template - it's possible there's no Hot Reload with that one (not sure).

@blogcraft
Copy link

How strange, the C# code won't hot reload on save. Only the angular code which is standard since the beginning.

@dotnet dotnet locked as resolved and limited conversation to collaborators Dec 30, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates Done This issue has been fixed enhancement This issue represents an ask for new feature or an enhancement to an existing one feature-spa Priority:1 Work that is critical for the release, but we could probably ship without User Story A single user-facing feature. Can be grouped under an epic.
Projects
.NET 6.0
  
Completed
Development

No branches or pull requests