概述
我们正在将数据库访问转移到 API 调用,并且我需要从数据库返回使用该 API 找到的搜索结果。计划是使用 IAsyncEnumerable,但我在处理 HttpClient 缓冲区时遇到了麻烦(至少我认为)。该调用由 WPF 客户端上的按钮单击触发,该按钮将搜索词发送到客户端 API 服务。该服务发出 API 请求,然后使用存储库来完成工作。我使用的是最小的 API,客户端和 API 都使用 .Net 6。
问题
我遇到的问题是,虽然 IAsyncEnumerable 部分在 API 端工作,每次返回一个条目,但整个结果仍然作为单个响应传回,而不是一次返回一个响应。
信息与问题
此时,我已经将其简化,只是为了尝试让这个概念发挥作用。当不使用 HttpClient 时,IAsyncEnumerable 部分在客户端 ApiService 本身内工作得很好,但一旦引入它,我就开始遇到问题。您可以在 GetTestStreamAsync 方法的区域中看到我尝试过的各种解决方案。
所以我想主要问题之一是,IAsyncEnumerable 是否可以做到这一点?如果没有,解决方案是什么?
客户端代码
MainWindow.xaml.cs
private async void btnSearch_Click(object sender, RoutedEventArgs e)
{
// This works
//var result = apiService.GetTestAsync();
// This doesn't
var result = apiService.GetTestStreamAsync(txtSearchTerms.Text);
await foreach(var item in result!)
{
lstResults.Items.Add(item);
}
}
Run Code Online (Sandbox Code Playgroud)
主窗口.xaml
<Grid>
<TextBox x:Name="txtSearchTerms"
HorizontalAlignment="Left"
Margin="86,92,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="340"
TabIndex="1" …Run Code Online (Sandbox Code Playgroud) 我正在尝试将我的 api 重构为最小的 api。以前我一直使用 ControllerBase.HttpContext 来获取用户,如下所示:
var emial = HttpContext.User.FindFirstValue(ClaimTypes.Email);
Run Code Online (Sandbox Code Playgroud)
我想要用于端点映射的方法应该是这样的:
public static void MapSurveyEndpoints(this WebApplication app) {
app.MapPost("/api/Surveys", AddSurveysAsync);
}
public static async Task<Survey> AddSurveysAsync(ISurveyRepository repo, Survey survey) {
var email = ...; //get current user email
survey.UserEmail = email;
return await repo.AddSurveysAsync(survey);
}
Run Code Online (Sandbox Code Playgroud)
在不使用控制器的情况下吸引用户的另一种方法是什么?
我正在使用 Net 6 Minimal API:
application.MapGet("countries", async (IService service) => {
var countries = await mediator.Send(request);
return Results.Ok(countries);
});
Run Code Online (Sandbox Code Playgroud)
是否可以将 AspNet Api 版本控制与 Net 6 Minimal API 一起使用?如何?
我见过很多类似的问题,但似乎没有一个能准确代表我面临的问题。当我创建文件夹结构如下:
./
./src
./src/test
Run Code Online (Sandbox Code Playgroud)
然后导航到./src/test并运行dotnet new webapi -lang c#,这将创建一个可以正常工作的最小 API。我也可以dotnet publish -c RELEASE -o out /p:Version=1.0.0毫无问题地运行。
然后,当我尝试在根级别创建一个包含以下内容的 docker 文件时:
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
ARG BUILDCONFIG=RELEASE
ARG VERSION=1.0.0
COPY ./src/test/test.csproj ./src/test/
RUN dotnet restore ./src/test/test.csproj
COPY ./src/ ./
WORKDIR ./src/test/
RUN dotnet publish -c $BUILDCONFIG -o out /p:Version=$VERSION
FROM mcr.microsoft.com/dotnet/sdk:6.0
WORKDIR /app
COPY --from=build /src/test/out ./
EXPOSE 5000
ENTRYPOINT ["dotnet", "test.dll"]
Run Code Online (Sandbox Code Playgroud)
我得到以下输出:
=> ERROR [build 6/6] RUN dotnet publish -c RELEASE -o out /p:Version=1.0.0 2.5s …Run Code Online (Sandbox Code Playgroud) 我确信这个问题的答案一定是显而易见的,因为这是一件显而易见的事情,但我似乎找不到任何指导。
我有一个 ASP.NET Core 最小 API,并且想要添加身份验证。我已经在不同的项目中设置了 Identity,并且希望对 API 使用相同的数据库(即相同的用户)。
我看到这篇博客文章,它看起来很有希望,直到我意识到那里的代码以纯文本形式检查用户名和密码(admin在示例中都使用)...
if (credentials[0] == "admin" && credentials[1] == "admin")
Run Code Online (Sandbox Code Playgroud)
问题在于(值得庆幸的是),身份不会以纯文本形式存储密码,它们是经过哈希处理的,所以我无法进行简单的比较。
我尝试对传入的密码进行哈希处理,如本答案所示,但这不起作用,因为每次调用时哈希值都不同_userManager.PasswordHasher.HashPassword。
我尝试使用 ASP.NET Core 的SignInManager.CanSignInAsync方法来检查是否可以使用凭据登录,但这需要我将以下内容添加到Program...
builder.Services.AddIdentity<User, IdentityRole>(options => {
// options removed for clarity
})
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<AppDbContext>();
Run Code Online (Sandbox Code Playgroud)
然而,一旦我这样做了,对 API 的任何请求都会尝试重定向到登录页面,这显然在从代码调用 API 时不起作用。
我在 Microsoft 网站上只能找到这篇文章,但这假设您使用的是 Azure。目前,我仍在本地计算机上开发它,我还不知道项目所有者是否想要部署到 Azure 还是他们自己的托管服务器,因此那里的代码对我没有帮助。
任何人都能够向我解释我如何完成看似如此明显且简单的任务?如果我需要提供更多信息,请告诉我。谢谢
c# authentication asp.net-identity asp.net-core minimal-apis
我使用 Microsoft https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-6.0的链接来创建我的 Web api。
之后,我像这样添加数据库:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<Context>(opt => opt.UseSqlServer(
builder.Configuration.GetConnectionString("Default")
));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
Run Code Online (Sandbox Code Playgroud)
我的功能是这样的:
app.MapGet("/SomeRoute/{data}", async (long data, Context appDb) =>
{
var tabladata = await appDb.Table1
.Where(some code here)
.ToListAsync();
return Results.Ok(tabladata);
});
Run Code Online (Sandbox Code Playgroud)
我发现我可以使用以下代码获取 IP 客户端: 在 var app = builder.Build(); 之前添加此代码
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
Run Code Online (Sandbox Code Playgroud)
之后:
app.UseForwardedHeaders();
Run Code Online (Sandbox Code Playgroud)
最后看起来像这样:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<Context>(opt => opt.UseSqlServer(
builder.Configuration.GetConnectionString("Default")
));
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; …Run Code Online (Sandbox Code Playgroud) 我正在使用最小 API 来重新创建旧的 REST 服务,因此我需要尽可能模仿以前服务的功能。旧服务中的信息返回帕斯卡大小写,我知道有一些旧客户端区分大小写。开箱即用的 .NET 6 在网络上序列化为驼峰式大小写,但可以使用以下代码覆盖:
builder.Services.Configure<JsonOptions>(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
Run Code Online (Sandbox Code Playgroud)
如果设置了断点,我可以看到策略最初设置为驼峰式大小写,然后重置为空。如果我随后使用JsonSerializer.Serialize它,它会按预期工作。唯一的问题是,如果我使用Results.OK(colection)它,就会回到驼峰式大小写,就好像它忽略了选项设置一样。我可以在模型类上使用声明性属性设置所有属性名称,并且按预期工作,因此内联声明可以工作,但在服务级别上的设置却不能。那么,“Results.OK”响应管道正在查看哪些选项?
编辑:更正的方法名称
我正在为 ASP.NET Core 7 最小 API 项目编写一些中间件。
在这个阶段,我只希望能够读取正在发送的请求正文,然后读取正在发送的响应正文,并将它们转储到控制台。
阅读请求似乎很容易。我的中间件包含以下内容...
public async Task InvokeAsync(HttpContext httpContext) {
try {
httpContext.Request.EnableBuffering();
string requestBody = await new StreamReader(httpContext.Request.Body, Encoding.UTF8).ReadToEndAsync();
httpContext.Request.Body.Position = 0;
Console.WriteLine($"Request body: {requestBody}");
} catch (Exception ex) {
Console.WriteLine($"Exception reading request: {ex.Message}");
}
await _next(httpContext);
}
Run Code Online (Sandbox Code Playgroud)
这很好用。但是,当我尝试读取响应正文时,出现异常。我尝试插入以下内容(类似于在许多 SO 答案和博客文章中找到的内容)...
try {
using StreamReader reader = new(httpContext.Response.Body);
httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
string responseBody = await reader.ReadToEndAsync();
httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
Console.WriteLine($"Response body: {responseBody}");
} catch (Exception ex) {
Console.WriteLine($"Exception reading response: {ex.Message}");
}
Run Code Online (Sandbox Code Playgroud)
但是,这会产生异常System.ArgumentException:Stream 不可读。
让我困惑的一件事是,根据 …
使用用 C# 编写的 ASP.NET Core 6 Web API 和最少的 API,我想返回数据流,而无需先将数据加载到内存中。就我而言,这是由 Apache Spark 编写的 JSONL(JSON 行)数据。JSONL 是一种基于文本的格式。
下面的代码设置了这Content-Type: application/json对于我的用例来说是不正确的。设置此类型然后用数组包装整个内容,并在所有引号的地方添加转义反斜杠字符。
相反,它应该设置Content-type: text/plain保留行的原始格式,并允许该端点的使用者一次流式传输和处理一行,而无需将整个响应正文加载到客户端的内存中。
是否可以content-type在保留流的同时更改此设置Transfer-Encoding: chunked,并且不解析或修改我从 .jsonl 文件中读取的行内容?
app.MapGet("/stream/data", () =>
{
async IAsyncEnumerable<string> Stream()
{
using (StreamReader file = new StreamReader(filePath))
{
while (!file.EndOfStream)
{
yield return await file.ReadLineAsync() ?? string.Empty;
}
}
}
return Stream();
});
Run Code Online (Sandbox Code Playgroud) 我正在使用 .NET 最小 API,并且遇到了枚举的 JSON 序列化/反序列化行为的问题。尽管我尝试更改使用字符串而不是整数的行为,但 Swashbuckle.AspNetCore 的 Swagger 文档仍然将枚举表示为整数。
using System.Text.Json.Serialization;
var builder = WebApplication.CreateBuilder(args);
// Docs: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-7.0#configure-json-deserialization-options-globally
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapPost("/api/v1/test", (TestRequest request) => Results.Ok(request));
app.Run();
public enum GridType
{
Arithmetic,
Geometric
}
public class TestRequest
{
public required string Name { get; init; }
public required GridType Type { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
但是当 Swagger UI 打开时,我仍然得到枚举的整数:
{
"name": "string",
"type": …Run Code Online (Sandbox Code Playgroud) minimal-apis ×10
c# ×8
asp.net-core ×4
.net-6.0 ×3
api ×1
asp.net ×1
dockerfile ×1
jsonlines ×1
stream ×1
swagger ×1
swashbuckle ×1
wpf ×1