gpt4 book ai didi

c# - 从 Core 2.0 更新到 3.1.1 后响应正文为空

转载 作者:行者123 更新时间:2023-12-04 00:21:55 24 4
gpt4 key购买 nike

UPDATE: If I change return new OkObjectResult(token); to return Ok("test"); then it's not empty.

要使用 API,您必须调用 AuthenticateAsync 来创建并返回一个 token 。

    [AllowAnonymous]
[HttpPost]
public async Task<IActionResult> AuthenticateAsync([FromBody]string apiKey)
{
return await StopwatchLogger.LogExecutionTime($"Authenticate user: {apiKey}", async () =>
{
EnsureValidLoginRequest(apiKey);

//Validate user credentials
AuthenticationInfo authenticationInfo = await _authenticationService.AuthenticateUser(apiKey);
if (!authenticationInfo.IsAuthenticated)
{
throw new RestApiExceptionNoLogging(ErrorCode.AuthenticationInvalidApiKey, "");
}

//Login current user and get the created token with claims identifying current user
Token token = _authenticationService.LoginApiUser(HttpContext, authenticationInfo.SystemUser);

return new OkObjectResult(token);
});
}

token 已正确创建,据我所知,一切都和以前一样,除了响应正文变为空。不会抛出任何错误。

编辑:添加startup.cs

public class Startup
{
private const string LogText = "An error occurred during startup.";

private readonly List<Exception> _exceptions = new List<Exception>();

private IWebHostEnvironment _HostingEnvironment;
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
_HostingEnvironment = env;
}

/// <summary>
/// This method gets called by the runtime. Use this method to add services to the container.
/// </summary>
/// <param name="services"></param>
/// <remarks>Is called before Configure</remarks>
public void ConfigureServices(IServiceCollection services)
{
try
{
if (_exceptions.Any())
{
return;
}
// Add framework services. See configuration class (implementing IConfigureOptions<MvcOptions>) for specific implementation details.
services.AddApiVersioning(o =>
{
o.ReportApiVersions = true;
o.AssumeDefaultVersionWhenUnspecified = true;
o.DefaultApiVersion = new ApiVersion(1, 0);
});

// Adds services required for using options.
services.AddOptions();
services.AddLogging(logging =>
{
logging.AddConsole();
logging.AddDebug();
});

//Add scoped services (New service for every request)
services.AddScoped<IJwtBearerService, JwtBearerService>();
services.AddScoped<IApiAuthService, ApiAuthService>();
services.AddScoped<ILoggingService, LoggingService>();


services.AddScoped<IResponseDatabaseLogger, ResponseDatabaseLogger>();
services.AddScoped<IRequestDatabaseLogger, RequestDatabaseLogger>();

services.AddScoped<IAuthService, AuthService>();
services.AddScoped<IOrderService, OrderService>();
services.AddScoped<IApiOrderService, ApiOrderService>();
services.AddScoped<IHaikomService, HaikomService>();

services.AddDbContext<LoggingContext>(options => options.UseSqlServer(connection));
services.AddDbContext<OrderContext>(options => options.UseSqlServer(connection));
services.AddDbContext<AuthContext>(options => options.UseSqlServer(connection));

////Add application setting as options to enable dependency injection for settings
services.Configure<Mediacreator.RestApi.Models.AuthenticationModel.Options.AuthenticationOptions>(options =>
{
// Get the setting from environment variable or user secret.
// For more info: https://blogcarlosperez.com/2016/05/24/user-secrets/ eller http://asp.net-hacker.rocks/2016/07/11/user-secrets-in-aspnetcore.html
options.JwtEncryptionSigningSecret = Configuration["Tokens:Key"];
options.Audience = Configuration["Tokens:Issuer"];
options.Issuer = Configuration["Tokens:Issuer"];
});


services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IResponseFileLogger, ResponseFileLogger>();
services.AddSingleton<IRequestFileLogger, RequestFileLogger>();

var sp = services.BuildServiceProvider();

// Resolve the services from the service provider
var jwtBearerService = sp.GetService<IJwtBearerService>();

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.TokenValidationParameters = jwtBearerService.CreateTokenValidationParameters(Configuration["Tokens:Key"]);
});

services.Configure<IISOptions>(options =>
{
options.ForwardClientCertificate = false;
options.AutomaticAuthentication = false;
});

services.AddMvc(options =>
{
//The services should only support JSON as output format for avoiding supporting for example XML
//By specifying this property, the client will get a 406 error message if the accept header is for example "Accept: application/xml".
options.ReturnHttpNotAcceptable = true;

options.EnableEndpointRouting = false;

//Enable events executed before, and after an action has been executed.
options.Filters.Add(typeof(ActionFilter));

//Make all controllers be protected by authorization
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();

options.Filters.Add(new AuthorizeFilter(policy));
});
}
catch (Exception ex)
{
_exceptions.Add(ex);
}
}

/// <summary>
/// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
/// </summary>
/// <param name="app"></param>
/// <param name="env"></param>
/// <param name="loggerFactory"></param>
/// <remarks>Is called after ConfigureServices</remarks>
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
ILogger logger = null;

try
{
NLog.LogManager.LoadConfiguration("NLog.config");

loggerFactory.EnsureApiLoggersExists();
logger = loggerFactory.CreateLogger(this.GetType());
logger.LogInformation($"Current environment: {env.EnvironmentName}");

//Check if any exceptions occurred during startup
if (_exceptions.Any())
{
//Terminate the pipeline and return a response to the client
app.Run(async httpContext =>
{
await ExceptionHandler.HandleExceptionWriteErrorResponse(logger, httpContext, _exceptions.First(), LogText);
});
}

//Important to add this middleware first as exceptions in other middleware are also to be caught
app.UseCustomExceptionHandler();

//Important to add this middleware before UseMvc is added as the middleware component otherwise may not be called for all requests.
app.UseRequestLogger();
app.UseResponseLogger();
app.UseAuthentication();

app.UseMvcWithDefaultRoute();

}
catch (Exception ex)
{
//Terminate the pipeline and return a response to the client
app.Run(async httpContext =>
{
await ExceptionHandler.HandleExceptionWriteErrorResponse(logger, httpContext, ex, LogText);
});
}
}


}

我不得不将 IHostingEnvironment 更改为 IWebHostEnvironment。日志记录发生了一些变化,我不得不将 options.EnableEndpointRouting = false 添加到 MVC 选项中。还有一些事情我一时想不起来了。

编辑 2,添加响应记录:

public class ResponseLogger : MiddlewareBase
{
private readonly IResponseFileLogger _IResponseLogger;
public ResponseLogger(RequestDelegate nextRequestDelegate, ILoggerFactory loggerFactory, IResponseFileLogger respLogger) : base(nextRequestDelegate, loggerFactory)
{
_IResponseLogger = respLogger;
}
public async Task InvokeAsync(HttpContext context)
{
//Adds an identifier to the response headers. The identifier is used for connecting a response to log posts for corresponding requests and responses.
context.AddTraceIdentifierToResponseHeaders();

//The original response body needs to be stored locally as it can not be rewind after reading and logging it.
//http://stackoverflow.com/questions/37855384/log-httpresponse-body-for-asp-net-core-1-0-rest-api/38275942#38275942

using (var responseBodyBufferStream = new MemoryStream())
{
//Store the original body stream in a local variable
var responseBodyStream = context.Response.Body;

try
{
//Replace the context response with the newly created buffer as the original stream is not readable.
context.Response.Body = responseBodyBufferStream;

//Invoke the rest of the pipeline
await InvokeNextDelegate(context);

//Reset the buffer so the content can be read
responseBodyBufferStream.Seek(0, SeekOrigin.Begin);

//Create a stream reader to be able to read the response
using (var bufferStreamReader = new StreamReader(responseBodyBufferStream))
{
//Read the body from the stream
string responseBody = await bufferStreamReader.ReadToEndAsync();

//Reset the buffer
responseBodyBufferStream.Seek(0, SeekOrigin.Begin);

//Copy the content to the original stream and put it back
await responseBodyBufferStream.CopyToAsync(responseBodyStream);

//var responseFileLogger = new ResponseFileLogger(LoggerFactory, context, responseBody);
_IResponseLogger.Log(context, responseBody);
}
}
finally
{
//Ensure original body stream is is written back to the response body even if an exception occurs in another middleware.
context.Response.Body = responseBodyStream;
}
}
}
}

我尝试注释掉响应记录的代码以查看这是否是问题的一部分,但似乎与我所知道的没有区别。

编辑 3,从请求中添加输出:

info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 POST http://localhost:5000/api/v1.0/authentication application/json; charset=utf-8 41
info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[3]
Route matched with {action = "Authenticate", controller = "Authentication"}. Executing controller action with signature System.Threading.Tasks.Task`1[Microsoft.AspNetCore.Mvc.IActionResult] AuthenticateAsync(System.String) on controller Mediacreator.RestApi.Controllers.AuthenticationController (CoreRestApi).
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 3.1.1 initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: None
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (70ms) [Parameters=[@p0='?' (Size = 4000), @p1='?' (Size = 4000), @p2='?' (Size = 4000), @p3='?' (DbType = DateTime2), @p4='?' (DbType = DateTime2), @p5='?' (DbType = Int32), @p6='?' (Size = 4000), @p7='?' (DbType = Int64), @p8='?' (Size = 4000), @p9='?' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
INSERT INTO [ApiLogs] ([Action], [RemoteAddress], [RequestData], [RequestDateUtc], [ResponseDateUtc], [ResponseStatusCode], [Status], [SystemUserId], [TraceIdentifier], [Url])
VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9);
SELECT [Id]
FROM [ApiLogs]
WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 3.1.1 initialized 'AuthContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: None
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (22ms) [Parameters=[@__apiKey_0='?' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SELECT [t].[Id], [t].[ApiKey], [t].[Deleted], [t].[Description], [t].[IsActive], [t].[Password], [t].[Permissions], [t].[Username], [t0].[SystemUserId], [t0].[UserRoleId], [t0].[Id], [t0].[Description], [t0].[Name]
FROM (
SELECT TOP(2) [s].[Id], [s].[ApiKey], [s].[Deleted], [s].[Description], [s].[IsActive], [s].[Password], [s].[Permissions], [s].[Username]
FROM [SystemUsers] AS [s]
WHERE [s].[ApiKey] = @__apiKey_0
) AS [t]
LEFT JOIN (
SELECT [s0].[SystemUserId], [s0].[UserRoleId], [u].[Id], [u].[Description], [u].[Name]
FROM [SystemUserRole] AS [s0]
INNER JOIN [UserRoles] AS [u] ON [s0].[UserRoleId] = [u].[Id]
) AS [t0] ON [t].[Id] = [t0].[SystemUserId]
ORDER BY [t].[Id], [t0].[SystemUserId], [t0].[UserRoleId], [t0].[Id]
info: Mediacreator.RestApi.Services.Authentication.ApiAuthService[0]
User with ApiKey <xxx> authenticated
info: Mediacreator.RestApi.Services.Authentication.ApiAuthService[0]
Token created for user '<yyy>' (5). Token is valid '3600' seconds
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (20ms) [Parameters=[@__logId_0='?' (DbType = Int64)], CommandType='Text', CommandTimeout='30']
SELECT TOP(2) [a].[Id], [a].[Action], [a].[RemoteAddress], [a].[RequestData], [a].[RequestDateUtc], [a].[ResponseDateUtc], [a].[ResponseStatusCode], [a].[Status], [a].[SystemUserId], [a].[TraceIdentifier], [a].[Url]
FROM [ApiLogs] AS [a]
WHERE [a].[Id] = @__logId_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (20ms) [Parameters=[@p3='?' (DbType = Int64), @p0='?' (DbType = DateTime2), @p1='?' (DbType = Int32), @p2='?' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [ApiLogs] SET [ResponseDateUtc] = @p0, [ResponseStatusCode] = @p1, [Status] = @p2
WHERE [Id] = @p3;
SELECT @@ROWCOUNT;
info: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor[1]
Executing ObjectResult, writing value of type 'Mediacreator.RestApi.Models.AuthenticationModel.Entities.Token'.
info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2]
Executed action Mediacreator.RestApi.Controllers.AuthenticationController.AuthenticateAsync (CoreRestApi) in 8305.5034ms
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 11069.4992ms 200 application/json; charset=utf-8

编辑 4:添加 Token 类

public class Token
{
public readonly string access_token;
public readonly string token_type = Constants.TokenTypeBearer;
public readonly int expires_in;

public Token(string encodedJsonWebToken, int expiresInSeconds)
{
access_token = encodedJsonWebToken;
expires_in = expiresInSeconds;
}
}

最佳答案

它不起作用的原因是@Ian Kemp 建议我的 Token 类的序列化不起作用。我读到他们在 Core 3.0 中添加了一个带有 System.Text.Json 的新序列化,但出于某种原因,它无法与

一起使用
public class Token
{
public readonly string access_token;
public readonly string token_type = Constants.TokenTypeBearer;
public readonly int expires_in;

public Token(string encodedJsonWebToken, int expiresInSeconds)
{
access_token = encodedJsonWebToken;
expires_in = expiresInSeconds;
}
}

所以我决定试用 Newtonsoft,现在它像以前一样工作了。

解决方案是将其添加到 Startup.cs 中的 ConfigureServices

services.AddControllers().AddNewtonsoftJson();

关于c# - 从 Core 2.0 更新到 3.1.1 后响应正文为空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59932578/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com