-
Notifications
You must be signed in to change notification settings - Fork 10.1k
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
Comments
Thanks for contacting us. |
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. |
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. |
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. |
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. |
@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 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).
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'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.
Native |
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/. |
who are you |
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. |
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. |
@LadyNaggaga Thanks for the clarifications. So it’s mostly the same as it is now (e.g. with the 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. |
@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 |
@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. |
@poke Oh, ok, I get the point now . Sure, +1 for such smart management and detection of npm window. |
@ernestopye I am curious what With the new templates, the .NET host checks first if the SPA dev server is already running. If yes, it doesn't start a new one and when you stop the .NET host, the SPA dev server continues to run. But if the SPA dev server is not running, it gets started and stopped together with the .NET host. I created the AspNetCore.SpaYarp package that works more like the old templates (dotnet -> proxy -> node). But like with the new templates, it's possible to start and stop the SPA dev server independently of the .NET host. |
@berhir Appreciate your response. Apologies for the confusion on 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. |
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.
@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. |
@ernestopye here is how it works. You have two optioons
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. |
@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 The proxy forwards headers to ASP.NET Core and ASP.NET Core uses that information transparently to emit cookies and other things correctly. |
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. |
This requires the client-side technology to have a
Haven't done much research, but I believe there's likely a solution for these.
Node has http/2 and http/3 support. I suspect is doable to make it work.
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. |
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. |
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. |
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 |
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. |
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 |
Hi @javiercn , Thanks for the answer.
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 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.
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 ).
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 ;).
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 |
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.
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?
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.
We can add a doc request for it @Rick-Anderson.
We have no plans to keep updating that approach. As for the future, we don't have concrete plans yet. |
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.
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. |
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” |
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.
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. 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).
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? |
Hello. Really don't understand how to manage my app to work with a new approach. Currently I have 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? |
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. |
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. |
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. |
@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 Good luck! |
@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 |
Did spa template (angular) supports hot reload? I can't make it work using VS Code or VS Studio. |
@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). |
How strange, the C# code won't hot reload on save. Only the angular code which is standard since the beginning. |
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.
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.
The text was updated successfully, but these errors were encountered: