为什么【FromBody】会导致 React 和 ASP.NET Core 8 Web API 之间出现 CORS 和 AmbigouslyMatchException 异常?

问题描述 投票:0回答:1

我有一个 Web 应用程序,以 React 作为前端,以 .NET core 8 作为后端。 该应用程序有一个带有

Meals
按钮的导航。单击它后,会出现带有两个以上按钮的子导航。其中一个是
All meals
,另一个是
Add meal
。一切都工作得很好,直到我决定前端应该在获取所有餐点时发送当前日期。然后,当我添加后端控制器
[FromBody] string date
时,它开始抛出两个尚未抛出的错误,并且直到我删除
[FromBody] string date
并重建项目后,它才会得到修复。

错误如下,第一个是后端的,第二个是前端的: 1 - `Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1] 执行请求时发生未处理的异常。 Microsoft.AspNetCore.Routing.Matching.AmbigouslyMatchException:请求匹配多个终结点。比赛:

  Fitness_Tracker.Controllers.MealController.AllMeals (Fitness-Tracker)
  Fitness_Tracker.Controllers.MealController.AllMeals (Fitness-Tracker)
     at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ReportAmbiguity(Span1 candidateState)
     at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ProcessFinalCandidates(HttpContext httpContext, Span1 candidateState)
     at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.Select(HttpContext httpContext, Span1 candidateState)
     at Microsoft.AspNetCore.Routing.Matching.DfaMatcher.MatchAsync(HttpContext httpContext)
     at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext)
     at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)`

2-

Access to fetch at 'https://localhost:7009/api/meal/all' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

import React, { useEffect, useState } from 'react';
import '../../css/AllMealsPage.css';

const AllMealsPage = () => {
    const [meals, setMeals] = useState([]);
    const [calories, setCalories] = useState(0);
    const [errorMessage, setErrorMessage] = useState('');
    const [selectedDate, setSelectedDate] = useState(new Date());

    useEffect(() => {
        const fetchMeals = async () => {
            try {
                const response = await fetch('https://localhost:7009/api/meal/all', {
                    method: 'POST',
                    credentials: 'include',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ date: selectedDate.toISOString() })
                });

                if (!response.ok) {
                    throw new Error('Displaying meals failed');
                }

                const data = await response.json();
                setMeals(data);
                setErrorMessage('');
            } catch (err) {
                setErrorMessage(err.message);
            }
        };

        fetchMeals();
    }, [selectedDate]);

    useEffect(() => {
        const fetchCalories = async () => {
            try {
                const response = await fetch('https://localhost:7009/api/meal/calories', {
                    method: 'POST',
                    credentials: 'include'
                });

                if (!response.ok) {
                    throw new Error('Displaying calories failed');
                }

                const data = await response.json();
                setCalories(data);
                setErrorMessage('');
            } catch (err) {
                setErrorMessage(err.message);
            }
        };

        fetchCalories();
    }, [selectedDate]);

    const handlePreviousDay = () => {
        setSelectedDate(prevDate => {
            const newDate = new Date(prevDate);
            newDate.setDate(prevDate.getDate() - 1);
            return newDate;
        });
    };

    const handleNextDay = () => {
        setSelectedDate(prevDate => {
            const newDate = new Date(prevDate);
            const today = new Date();
            if (newDate.toDateString() !== today.toDateString()) {
                newDate.setDate(prevDate.getDate() + 1);
            }
            return newDate;
        });
    };

    return (
        <div className="all-meals-container">
            {errorMessage && <p className="error-message">{errorMessage}</p>}

            <div className="date-navigation">
                <button onClick={handlePreviousDay}>←</button>
                <span>{selectedDate.toDateString()}</span>
                <button onClick={handleNextDay} disabled={selectedDate.toDateString() === new Date().toDateString()}>→</button>
            </div>

            <div className="table-wrapper">
                <table className="meals-table">
                    <thead>
                        <tr>
                            <th>Meal Name</th>
                            <th>Meal of the Day</th>
                            <th>Calories</th>
                        </tr>
                    </thead>
                    <tbody>
                        {meals.map((meal) => (
                            <tr key={meal.id}>
                                <td>{meal.name}</td>
                                <td>{MealOfTheDayLabel(meal.mealOfTheDay)}</td>
                                <td>{meal.calories}</td>
                            </tr>
                        ))}
                    </tbody>
                </table>
            </div>

            <h1>Total calories: {calories}</h1>
        </div>
    );
};

const MealOfTheDayLabel = (mealOfTheDay) => {
    switch (mealOfTheDay) {
        case 0:
            return 'Breakfast';
        case 1:
            return 'Lunch';
        case 2:
            return 'Dinner';
        case 3:
            return 'Snack';
        default:
            return 'Unknown';
    }
};

export default AllMealsPage;

这是我的后端控制器(不工作时):

namespace Fitness_Tracker.Controllers
{
    using Fitness_Tracker.Data.Models;
    using Fitness_Tracker.Models.Meals;
    using Fitness_Tracker.Services.Meals;
    using Microsoft.AspNetCore.Mvc;
    using System.Security.Claims;

    public class MealController : BaseApiController
    {
        private readonly IMealService _mealService;

        public MealController(IMealService mealService)
        {
            this._mealService = mealService;
        }

        [HttpPost("add")]
        public async Task<IActionResult> AddMeal([FromBody] AddMealModel model)
        {
            if(!ModelState.IsValid)
            {
                return BadRequest();
            }

            string userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

            if(userId == null) { 
                return BadRequest();
            }

            await _mealService.CreateMealAsync(userId, model);

            return Ok();
        }

        [HttpPost("all")]
        public async Task<IActionResult> AllMeals(string date)// Remove [FromBody] string date and it works
        {
            string userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

            if(userId == null)
            {
                return BadRequest();
            }

            List<Meal> result = await _mealService.GetAllUserMealsAsync(userId);

            return Ok(result);
        }

        [HttpPost("calories")]
        public async Task<IActionResult> AllMealsCalories()
        {
            string userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

            if (userId == null)
            {
                return BadRequest();
            }

            int result = await _mealService.GetTotalUserMealCaloriesAsync(userId);

            return Ok(result);
        }
    }
}

我玩弄了它,基本上无论我添加什么作为参数来捕获,例如

[FromQuery] string date
,简单的
string date
等等 - 都会发生错误。 我似乎无法弄清楚问题是什么,因为我收到的错误与实际问题无关,因为否则一切都很好。

我原本希望简单地执行

[FromBody] string date
并捕获从前端发送的日期,但它进入了噩梦模式。

c# reactjs asp.net-core-webapi asp.net-core-8
1个回答
0
投票

我建议为“所有”操作创建一个模型,如下所示:

    [HttpPost("all")]
    public async Task<IActionResult> AllMeals(AllMealsRequest allMealsRequest)// Remove [FromBody] string date and it works
    {
       [...]
    }

    // With a class similar to this: 
    public class AllMealsRequest
    {
       public string Date { get; set; }
    }

原因是 json 已成为事实上的标准,并且当您发送有效负载时,api 默认情况下需要 json。创建一个对象只是让它与 api 配合得很好。此外,以后可以更轻松地向请求正文添加属性,而不会破坏将来的 api。

© www.soinside.com 2019 - 2024. All rights reserved.