Gelöst: API Controller wird bei React nicht erreicht
Problem: bei einer neu erstellen React Anwendung mit ASP.Net Controller unter Visual Studio 2022 und .Net6 wird der Daten Controller nie erreicht.
Die Anwendung meldet OK 305 zurĂŒck
Lösung: man muss unter der React -Datei setupProxy.js die Erweiterung fĂŒr den Controller eintragen
Solution
is:
setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware'); const { env } = require('process');
const target = env.ASPNETCORE_HTTPS_PORT ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}` : env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'http://localhost:46007';
const context = [ "/api", "/weatherforecast", "/_configuration", "/.well-known", "/Identity", "/connect", "/ApplyDatabaseMigrations", "/_framework" ];
module.exports = function(app) { const appProxy = createProxyMiddleware(context, { target: target, secure: false, headers: { Connection: 'Keep-Alive' } });
app.use(appProxy); };
|
With added â/apiâ it works
Asp.net6: Program.cs
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.UI; using Microsoft.EntityFrameworkCore; using Rue25.Data; using Rue25.Models;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container. var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString)); builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
//*AntiforgeryToken
// Angular's default header name for sending the XSRF token.
builder.Services.AddAntiforgery(options => { options.HeaderName = "X-XSRF-TOKEN"; //options.Cookie.Name = "X-XSRF-TOKEN"; //"X-CSRF-TOKEN-OurAppName"; // Set Cookie properties using CookieBuilder propertiesâ . //options.FormFieldName = "AntiforgeryFieldname"; //options.HeaderName = "X-CSRF-TOKEN-HEADERNAME"; //options.SuppressXFrameOptionsHeader = false; });
builder.Services.AddAuthentication() .AddIdentityServerJwt();
builder.Services.AddControllersWithViews(); builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseMigrationsEndPoint(); } else { // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); }
app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting();
app.UseAuthentication(); app.UseIdentityServer(); app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "api", pattern: "/{controller}/{action=Index}/{id?}"); endpoints.MapRazorPages(); });
app.MapFallbackToFile("index.html"); ;
app.Run();
//--< AntiForgery >--
//app.us ////*add in Configure(.. , IAntiforgery antiforgery) // app.Use(next => context => // { // string path = context.Request.Path.Value; // string[] urlAreas = { "/api", "/swagger", "articles" }; // if ( // string.Equals(path, "/", StringComparison.OrdinalIgnoreCase) || // string.Equals(path, "/index.html", StringComparison.OrdinalIgnoreCase) || // urlAreas.Any(urlAreas => path.StartsWith(urlAreas)) // ) // { // // The request token can be sent as a JavaScript-readable cookie, // // and Angular uses it by default. // var tokens = antiforgery.GetAndStoreTokens(context); // context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, // new CookieOptions() // { // HttpOnly = false, // Secure = false, // IsEssential = true, // SameSite = SameSiteMode.Strict // }); // }
// return next(context); // }); ////--</ AntiForgery >-- |
React Articles.js
import React, { Component } from 'react';
export class Articles extends Component { //--------< component: Articles >--------
//====< compontent >==== static displayName = Articles.name;
constructor(props) { //-< react: parameter > super(props); //-</ react: parameter >
//--< react: variables >-- this.state = { articles: [], loading: true }; //--</ react: variables >- }
//--< component.events >-- componentDidMount() { this.populateData(); } //--</ component.events >--
//====</ compontent >====
//========< Fill HTML >======
static renderTable(articles) { return ( <table className='table table-striped' aria-labelledby="tabelLabel"> <thead> <tr> <th>title</th>
</tr> </thead> <tbody> {articles.map(article => <tr key={article.IDArticle}> <td>{article.title}</td> </tr> )} </tbody> </table> ); } //========</ Fill HTML >====== //====< react: render >==== //*set root hmtl, load data, fill data render() { //----< prepare document >--- let contents = this.state.loading ? <p><em>Loading...</em></p> : Articles.renderTable(this.state.articles); //----</ prepare document >---
//----< fill html >---- return ( <div> <h1 id="tableLabel" >Articles</h1> <p>This component demonstrates fetching data from the server.</p> {contents} </div> ); //----</ fill html >----
} //====</ react: render >==== //====< functions >==== //*load data from web api async populateData() { //--------< populateData() >-------- const response = await fetch('api/articles/list'); //*get web_data from web api as json-string const data = await response.json(); //*convert json to data this.setState({ articles: data, loading: false }); //*refresh state of arcticles //--------</ populateData() >-------- } //====</ functions >====
//--------</ component: Articles >-------- }
|
ArticlesController.cs
Using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Rue25.Models; using Rue25.Data;
namespace Controllers { //------------< Namespace >------------ [Produces ("application/json")] [Route("api/articles")] [ApiController] public class ArticlesController : ControllerBase { //--------< Controller: ArticlesController >-------- #region init private readonly ApplicationDbContext _dbContext;
public ArticlesController(ApplicationDbContext dbcontext) { _dbContext = dbcontext; //*get database context } #endregion /init
// GET: api/Articles [HttpGet("list")] public async Task<ActionResult<IEnumerable<ArticleModel>>> List() { //-------------< Liste >------------- var data = _dbContext.tbl_Articles.Take(100); return await data.ToListAsync(); //-------------</ Liste >------------- } } }
|
appRoutes.js
import ApiAuthorzationRoutes from './components/api-authorization/ApiAuthorizationRoutes'; import { Counter } from "./components/Counter"; import { FetchData } from "./components/FetchData"; import { Home } from "./components/Home"; import { Articles } from "./components/Articles";
const AppRoutes = [
{ index: true, element: <Home /> }, { path: '/counter', element: <Counter /> }, { path: '/fetch-data', requireAuth: true, element: <FetchData /> }, { path: '/articles', element: <Articles/> },
...ApiAuthorzationRoutes ];
export default AppRoutes;
|
App.js
import React, { Component } from 'react'; import { Route, Routes } from 'react-router-dom'; import AppRoutes from './AppRoutes'; import AuthorizeRoute from './components/api-authorization/AuthorizeRoute'; import { Layout } from './components/Layout'; import './custom.css';
export default class App extends Component { static displayName = App.name;
render() { return ( <Layout> <Routes> {AppRoutes.map((route, index) => { const { element, requireAuth, ...rest } = route; return <Route key={index} {...rest} element={requireAuth ? <AuthorizeRoute {...rest} element={element} /> : element} />; })} </Routes> </Layout> ); } } |