POST-запросы страницы .Net Core Razor дают 400 неверных запросов

У меня есть веб-приложение и проекты веб-API, встроенные в .net 5. Я обновил их обоих до .NET 8.0 и обновил все пакеты в проекте. После обновления до .NET 8 в веб-приложении любой почтовый запрос выдает 400 неверных запросов без использования метода OnPostAsync, он даже не входит в метод. Срабатывает метод OnGet и извлекает данные из базы данных. Каково решение этой проблемы? Я попытался отключить защиту от подделки, и запрос на публикацию перенаправил меня на ту же страницу без публикации данных. Эта проблема возникает только на рабочем сервере: когда я запускаю ее локально на своем устройстве, она работает нормально, я также пытался опубликовать ее на своем локальном сервере IIS, и все работало нормально. Я также попытался запустить его на рабочем сервере с помощью CMD. Я успешно прошел метод post. Я использую протокол HTTP, а не HTTPS.

РЕДАКТИРОВАТЬ

Мой файл web.config:

<?xml version = "1.0" encoding = "utf-8"?>
<configuration>
  <location path = "." inheritInChildApplications = "false">
    <system.webServer>
      <handlers>
        <add name = "aspNetCore" path = "*" verb = "*" modules = "AspNetCoreModuleV2" resourceType = "Unspecified" />
        <remove name = "OPTIONSVerbHandler" />
<add name = "OPTIONSVerbHandler" path = "*" verb = "GET,HEAD,POST,OPTIONS" modules = "ProtocolSupportModule" resourceType = "Unspecified" requireAccess = "None" />
      </handlers>
      <aspNetCore processPath = "dotnet" arguments = ".\myapplication.dll" stdoutLogEnabled = "false" stdoutLogFile = ".\logs\stdout" hostingModel = "inprocess" />
    </system.webServer>
  </location>
</configuration>
<!--ProjectGuid: b2ce28c8-f007-4de5-b3aa-c44c6e651ec5-->

мой файл stratup.cs после удаления program.cs:

using AutoMapper;
using MyApplication.Admin.Data;
using MyApplication.Admin.Middlewares;
using MyApplication.Admin.Model;
using MyApplication.Library.MySQL.DbModels;
using MyApplication.Library.MySQL.Repositories;
using MyApplication.Library.MySQL.Services;
using MyApplication.Library.MySQL.Utility;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using System;
using System.IO;


var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options =>
{
    options.ListenAnyIP(44341);
    options.Limits.MaxRequestBodySize = 52428800;
});

var services = builder.Services;
var configuration = builder.Configuration;

services.AddDbContext<Context>(options =>
    //options.UseSqlServer(
    //    Configuration.GetConnectionString("ContextConnection"))
    //);
    options.UseMySQL(
         configuration.GetConnectionString("ContextConnection"))
     );

services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");

services.AddHttpClient();

services.AddDbContext<MyContext>();
services.AddScoped<IUnitofWork, UnitofWork>();
services.AddDefaultIdentity<IdentityUser>(options =>
{
    options.SignIn.RequireConfirmedAccount = true;

    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromDays(1);
    options.Lockout.MaxFailedAccessAttempts = 3;

    options.Password.RequireDigit = true;
    options.Password.RequireLowercase = true;
    options.Password.RequireUppercase = true;
    options.Password.RequireNonAlphanumeric = true;
    options.Password.RequiredUniqueChars = 1;
    options.Password.RequiredLength = 8;


})
.AddRoles<ApplicationRole>()
.AddEntityFrameworkStores<Context>();

services.Configure<SecurityStampValidatorOptions>(options =>
{
    options.ValidationInterval = TimeSpan.Zero;
});

services.AddRazorPages(options =>
{
    //options.Conventions.AuthorizeFolder("/");
}).AddRazorRuntimeCompilation();

services.Configure<RazorViewEngineOptions>(o =>
{
    o.ViewLocationFormats.Add("/Pages/Shared/Components/{1}/{0}" + RazorViewEngine.ViewExtension);
});

var mappingConfig = new MapperConfiguration(mc =>
{
    mc.AddProfile(new AutoMapperProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);

services.AddMemoryCache();

services.AddCors(options =>
{
    options.AddDefaultPolicy(builder =>
    {
        builder.AllowAnyOrigin()
               .AllowAnyMethod()
               .AllowAnyHeader();
    });
});

services.AddRazorPages();

var app = builder.Build();

//Insecure cookie setting: missing Secure flag
app.UseCookiePolicy(
   new CookiePolicyOptions
   {
       Secure = CookieSecurePolicy.Always
   });


////Permissions-Policy
//app.Use(async (context, next) =>
//{
//    context.Response.Headers.Add("Permissions-Policy", "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()");
//    await next();
//});

//Missing security header: X - XSS - Protection
app.Use(async (context, next) =>
{
    context.Response.Headers.Add("X-Xss-Protection", "1");
    await next();
});


//Missing security header: X-Content-Type-Options
app.Use(async (context, next) =>
{
    context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
    await next();
});

app.UseCors();

////Missing security header: X-Content-Type-Options
//app.Use(async (context, next) =>
//{
//    context.Response.Headers.Add("Referrer-Policy", "no-referrer");
//    await next();
//});

///////New////
//Missing security header: X - Frame - Options
app.Use(async (context, next) =>
{
    context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
    await next();
});


if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    // 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.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
                    Path.Combine(configuration.GetSection("MediaSettings:MEDIAFILESURL").Value, "Media", "Default")),
    RequestPath = "/Media/Default"
});

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

// app.UsePermissions();

app.UseAntiforgery();

// global error handler
app.UseMiddleware<ErrorHandler>();

app.MapRazorPages();

app.Run();

моя страница входа в систему.cshtml:

                        <form id = "account" method = "post">

                            <div class = "input-group">
                                <input asp-for = "Input.Email" class = "form-control" placeholder = "Username" />
                                <span asp-validation-for = "Input.Email" class = "text-danger" style = "margin-top: 2rem;"></span>
                            </div>
                            <div class = "input-group">
                                <input asp-for = "Input.Password" class = "form-control" placeholder = "Password" />
                                <span asp-validation-for = "Input.Password" class = "text-danger" style = "margin-top: 2rem;"></span>
                            </div>
                            <div class = "row kt-login__extra">
                                <div class = "col">
                                    <label class = "kt-checkbox" asp-for = "Input.RememberMe">
                                        <input asp-for = "Input.RememberMe" />
                                        @Html.DisplayNameFor(m => m.Input.RememberMe)
                                        <span></span>
                                    </label>
                                </div>


                                <div class = "col kt-align-right">
   <p class = "text-danger">@ViewBag.Message</p>
</div> 
                            </div>
                            <div class = "kt-login__actions">
                                <button type = "submit" class = "btn btn-brand btn-elevate kt-login__btn-primary">Log in</button>
                            </div>
                        </form>

Файл Login.cshtml.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Http;
using System.Net.Http;
using System.Security.Claims;
using MyApplication.Library.MySQL.Utility;
using MyApplication.Library.MySQL.Services;

namespace MyApplication.Admin.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LoginModel : PageModel
    {
        private readonly UserManager<IdentityUser> _userManager;
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LoginModel> _logger;
        private readonly IConfiguration _configuration;
        private readonly IUnitofWork _repositories;

        public LoginModel(IUnitofWork repositories, SignInManager<IdentityUser> signInManager, ILogger<LoginModel> logger, UserManager<IdentityUser> userManager, IConfiguration configuration)
        {
            _userManager = userManager;
            _signInManager = signInManager;
            _logger = logger;
            _configuration = configuration;
            _repositories = repositories;
        }

        [BindProperty]
        public InputModel Input { get; set; }

        public IList<AuthenticationScheme> ExternalLogins { get; set; }

        public string ReturnUrl { get; set; }

        [TempData]
        public string ErrorMessage { get; set; }
        public class InputModel
        {
            [Required]
            [EmailAddress]
            public string Email { get; set; }

            [Required]
            [DataType(DataType.Password)]
            public string Password { get; set; }

            [Display(Name = "Remember me?")]
            public bool RememberMe { get; set; }
        }

        public async Task OnGetAsync(string returnUrl = null)
        {
            if (User.Identity.IsAuthenticated)
            {
                Response.Redirect("/Index");
            }

            if (!string.IsNullOrEmpty(ErrorMessage))
            {
                ModelState.AddModelError(string.Empty, ErrorMessage);
            }

            returnUrl ??= Url.Content("~/");

            ViewData["AppName"] = ! string.IsNullOrWhiteSpace( _configuration["AppName"]) ? "/" + _configuration["AppName"] : "";

            ViewData["Message"] = "";

            // Clear the existing external cookie to ensure a clean login process
            await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);

            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();

            ReturnUrl = returnUrl;
        }

        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {

            ViewData["AppName"] = !string.IsNullOrWhiteSpace(_configuration["AppName"]) ? "/" + _configuration["AppName"] : "";

            ViewData["Message"] = "";

            returnUrl ??= Url.Content("~/");

            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            try
            {

                if (ModelState.IsValid)
                {
                    // This doesn't count login failures towards account lockout
                    // To enable password failures to trigger account lockout, set lockoutOnFailure: true


                    var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);

                    if (result.Succeeded)
                    {
                        var userInfo = await _userManager.FindByEmailAsync(Input.Email);

                        // Create Cookie and Generate new Token
                        CookieOptions option = new CookieOptions();
                        //option.Expires = DateTime.Now.AddYears(1);
                        option.Expires = DateTime.Now.AddDays(10);
                       // option.Secure = true;
                        string tokenValue = GenerateNewTokenAsync(userInfo.Id, Input.Email);
 
                        Response.Cookies.Append("MyApplication_Token
", tokenValue, option);
                      

                        _logger.LogInformation("User logged in.");
                        return LocalRedirect(returnUrl);
                    }
                    if (result.RequiresTwoFactor)
                    {
                        return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
                    }
                    if (result.IsLockedOut)
                    {
                        _logger.LogWarning("User account locked out.");
                        return RedirectToPage("./Lockout");
                    }
                    else
                    {
                        ViewData["Message"] = "Invalid login attempt."; 
                        ModelState.AddModelError(string.Empty, "Invalid login attempt.");
                        await _signInManager.SignOutAsync();
                        return Page();
                    }
                }

            }
            catch (Exception ex) { }
            // If we got this far, something failed, redisplay form
            return Page();
        }

        private bool ValidateUserAsync(string UserEmail, string Password)
        {
            string Result = "";
            try
            {
                var ApiUrl = _configuration["AppSettings:APIURL"];
                HttpClientHandler clientHandler = new HttpClientHandler();
                clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };


                using (HttpClient httpClient = new HttpClient(clientHandler))
                {
                    HttpResponseMessage response = new HttpResponseMessage();
                    response = httpClient.GetAsync(ApiUrl + "Api/Authenticate/ValidateUser?UserEmail = " + UserEmail + "&Password = " + Password).Result;

                    if (response.StatusCode == System.Net.HttpStatusCode.OK)
                    {
                        Result = response.Content.ReadAsStringAsync().Result;
                    }

                    if (Result == "true")
                        return true;
                    else
                        return false;
                }
            }
            catch (Exception e)
            {
                return false;
            }
        }

        private string GenerateNewTokenAsync(string UserId, string UserEmail)
        {
            string Token = "";
            try
            {
                var ApiUrl = _configuration["AppSettings:APIURL"];

                HttpClientHandler clientHandler = new HttpClientHandler();
                clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };


                using (HttpClient httpClient = new HttpClient(clientHandler))
                {
                    HttpResponseMessage response = new HttpResponseMessage();
                    response = httpClient.GetAsync(ApiUrl + "Api/Authenticate/GenerateNewToken?UserId = " + UserId + "&UserEmail = " + UserEmail).Result;

                    if (response.StatusCode == System.Net.HttpStatusCode.OK)
                    {
                        Token = response.Content.ReadAsStringAsync().Result;
                    }
                }
            }
            catch (Exception e)
            {
                return "";
            }

            return Token;
        }

        public async Task<JsonResult> OnPostResetPassword(string email)
        {
            email = email.Trim();
            var emails = (await _repositories.AspNetUsers.GetAll()).Select(u => u.Email).ToList();
            if (!emails.Contains(email))
            {
                return new JsonResult(new { status = false, msg = "email not found" });
            }

            string secret = _configuration["AppSettings:ResetSecret"];
            string resetCode = ResetPasswordUtility.GenerateResetPasswordCode();
            string resetJwt = ResetPasswordUtility.GenerateResetJwtToken(email, resetCode, secret);

            string resetUrl = $"https://localhost:44341/Identity/Account/ResetPassword?ResetToken = {resetJwt}&UserEmail = {email}";

            string emailSubject = "MyApplication Reset Password";
            string emailBody = $@"
                <p>follow this link to reset you password it is valid for 30 minutes only</p>
                <br>
                <a href = ""{resetUrl}"">{resetUrl}</a>
            ";

            var user = (await _repositories.AspNetUsers.GetAll()).FirstOrDefault(u => u.Email == email);
            user.ResetPasswordCode = resetCode;
            var saveRes = await _repositories.Save(Guid.Empty);

            if (saveRes.Item1 == false)
            {
                Console.WriteLine(saveRes.Item2);
                return new JsonResult(new { status = false, msg = "failed to send email, please try again later" });
            }

            try
            {
                EmailUtility.SendEmail(email, emailSubject, emailBody);
                return new JsonResult(new { status = true, msg = "please check your email." });
            }
            catch (System.Exception)
            {
                return new JsonResult(new { status = false, msg = "failed to send email, please try again later" });
            }

        }
    }
}

Какие пакеты? Какой код? можете ли вы показать минимальный воспроизводимый пример ?

Pac0 07.08.2024 12:49

можете ли вы показать свой запрос на вызов ajax?

n_dev 07.08.2024 13:04

@n_dev Я не делаю никаких ajax-запросов на этой странице, я только вызываю метод OnPostAsync, используя форму.

Laith Sa'd Al-Deen 07.08.2024 13:05

проверьте проверку свойств модели, затем отправляете ли вы обязательные поля

n_dev 07.08.2024 13:09

в модели, которую вы передаете

n_dev 07.08.2024 13:09

@n_dev В моей локальной среде (режим разработки) все работает нормально, эта ошибка возникает только тогда, когда я размещаю ее в IIS.

Laith Sa'd Al-Deen 07.08.2024 13:10

вы проверили свой пул приложений и разрешения в соответствии с iis?

n_dev 07.08.2024 13:19

@n_dev Настройки пула приложений следующие: .Net CLR: 4.0 Управляемый конвейер: интегрированный А о каких разрешениях вы говорите?

Laith Sa'd Al-Deen 07.08.2024 13:22

вы используете хостинг Plesk?

n_dev 07.08.2024 13:23

@n_dev у меня есть свой сервер

Laith Sa'd Al-Deen 07.08.2024 13:24
<system.webServer> <handlers> <remove name = "WebDAV" /> <remove name = "ExtensionlessUrlHandler-Integrated-4.0" /> <remove name = "OPTIONSVerbHandler" /> <remove name = "TRACEVerbHandler" /> <add name = "ExtensionlessUrlHandler-Integrated-4.0" path = "*." verb = "*" type = "System.Web.Handlers.TransferRequestHandler" preCondition = "integratedMode,runtimeVersionv4.0" /> </handlers> <modules> <remove name = "WebDAVModule" /> </modules> </system.webServer>
n_dev 07.08.2024 13:26

пожалуйста, попробуйте этот раздел конфигурации для обработки запросов

n_dev 07.08.2024 13:27

о боже мой, ты украшаешь свой метод публикации с помощью [HttpPost]

n_dev 07.08.2024 13:30

как контроль определяет, что это почтовый запрос

n_dev 07.08.2024 13:31

Я попробовал конфигурацию, которую вы рассказали мне выше, и она выдала мне 404, когда я посещаю веб-сайт, для действия публикации это метод OnPostAsync, и я использую бритвенные страницы, элемент управления определит это, когда я добавлю OnPost или OnGet перед именем обработчика. В дополнение к тому, что этот пример работает нормально, как я уже говорил ранее, в моей локальной среде, я сталкиваюсь с проблемами только при публикации его на рабочем сервере IIS.

Laith Sa'd Al-Deen 07.08.2024 13:38

Я предлагаю вам сначала следовать этой статье, чтобы включить отслеживание неудачных запросов внутри IIS, затем вам следует проанализировать трассировку IIS, чтобы увидеть, обрабатывает ли модуль IIS запрос на публикацию и перенаправляет на страницу по умолчанию или сначала.

Brando Zhang 08.08.2024 08:39
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
16
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я понял, в чем проблема, Я использую app.UseCookiePolicy, инициализирую класс CookiePolicyOptions и делаю Secure to CookieSecurePolicy. Всегда следующим образом:

app.UseCookiePolicy(
   new CookiePolicyOptions
   {
       Secure = CookieSecurePolicy.Always
   });

Для свойства Secure установлено значение CookieSecurePolicy.Always, что означает, что все файлы cookie, созданные приложением, будут отмечены флагом Secure.

Что делает флаг безопасности: Флаг Secure в файле cookie гарантирует, что файл cookie отправляется только через HTTPS (защищенные) соединения. Это предотвращает передачу файла cookie по незашифрованным HTTP-соединениям, что может привести к его перехвату злоумышленниками.

Хотя я запускаю свое приложение по протоколу HTTP «Не защищено», это создает проблему при публикации формы. Если только он не работал нормально в версии .Net 5 «до обновления до 8». Комментарий решил мою проблему.

Другие вопросы по теме