Minimum API uygulamalarında Parametre Bağlama
Uyarı
ASP.NET Core'un bu sürümü artık desteklenmiyor. Daha fazla bilgi için bkz . .NET ve .NET Core Destek İlkesi. Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.
Parametre bağlama, istek verilerini yol işleyicileri tarafından ifade edilen kesin olarak belirlenmiş parametrelere dönüştürme işlemidir. Bağlama kaynağı, parametrelerin nereye bağlandığını belirler. Bağlama kaynakları HTTP yöntemine ve parametre türüne göre açık veya çıkarılabilir.
Desteklenen bağlama kaynakları:
- Yol değerleri
- Sorgu dizesi
- Üst bilgi
- Gövde (JSON olarak)
- Form değerleri
- Bağımlılık ekleme tarafından sağlanan hizmetler
- Özel
Aşağıdaki GET
yol işleyicisi bu parametre bağlama kaynaklarından bazılarını kullanır:
var builder = WebApplication.CreateBuilder(args);
// Added as service
builder.Services.AddSingleton<Service>();
var app = builder.Build();
app.MapGet("/{id}", (int id,
int page,
[FromHeader(Name = "X-CUSTOM-HEADER")] string customHeader,
Service service) => { });
class Service { }
Aşağıdaki tabloda, önceki örnekte kullanılan parametrelerle ilişkili bağlama kaynakları arasındaki ilişki gösterilmektedir.
Parametre | Bağlama Kaynağı |
---|---|
id |
yol değeri |
page |
sorgu dizesi |
customHeader |
üst bilgi |
service |
Bağımlılık ekleme tarafından sağlanır |
, , HEAD
OPTIONS
ve DELETE
HTTP yöntemleri GET
gövdeden örtük olarak bağlanmaz. Bu HTTP yöntemleri için gövdeden (JSON olarak) bağlanmak için ile [FromBody]
açıkça bağlayın veya dosyasından HttpRequestokuyun.
Aşağıdaki örnek POST yol işleyicisi, parametresi için person
bir bağlama gövdesi kaynağı (JSON olarak) kullanır:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/", (Person person) => { });
record Person(string Name, int Age);
Yukarıdaki örneklerde yer alan parametrelerin tümü istek verilerinden otomatik olarak bağlıdır. Parametre bağlamanın sağladığı kolaylığı göstermek için aşağıdaki yol işleyicileri istek verilerini doğrudan istekten nasıl okuyacaklarını gösterir:
app.MapGet("/{id}", (HttpRequest request) =>
{
var id = request.RouteValues["id"];
var page = request.Query["page"];
var customHeader = request.Headers["X-CUSTOM-HEADER"];
// ...
});
app.MapPost("/", async (HttpRequest request) =>
{
var person = await request.ReadFromJsonAsync<Person>();
// ...
});
Açık Parametre Bağlama
Öznitelikler, parametrelerin nereye bağlı olduğunu açıkça bildirmek için kullanılabilir.
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
// Added as service
builder.Services.AddSingleton<Service>();
var app = builder.Build();
app.MapGet("/{id}", ([FromRoute] int id,
[FromQuery(Name = "p")] int page,
[FromServices] Service service,
[FromHeader(Name = "Content-Type")] string contentType)
=> {});
class Service { }
record Person(string Name, int Age);
Parametre | Bağlama Kaynağı |
---|---|
id |
adıyla yol değeri id |
page |
adlı sorgu dizesi "p" |
service |
Bağımlılık ekleme tarafından sağlanır |
contentType |
adıyla üst bilgi "Content-Type" |
Form değerlerinden açık bağlama
[FromForm]
özniteliği form değerlerini bağlar:
app.MapPost("/todos", async ([FromForm] string name,
[FromForm] Visibility visibility, IFormFile? attachment, TodoDb db) =>
{
var todo = new Todo
{
Name = name,
Visibility = visibility
};
if (attachment is not null)
{
var attachmentName = Path.GetRandomFileName();
using var stream = File.Create(Path.Combine("wwwroot", attachmentName));
await attachment.CopyToAsync(stream);
}
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Ok();
});
// Remaining code removed for brevity.
Bunun alternatifi, özniteliğini [AsParameters]
ile ek açıklamalı özelliklere sahip özel bir türle kullanmaktır [FromForm]
. Örneğin, aşağıdaki kod form değerlerinden kayıt yapısının özelliklerine NewTodoRequest
bağlanır:
app.MapPost("/ap/todos", async ([AsParameters] NewTodoRequest request, TodoDb db) =>
{
var todo = new Todo
{
Name = request.Name,
Visibility = request.Visibility
};
if (request.Attachment is not null)
{
var attachmentName = Path.GetRandomFileName();
using var stream = File.Create(Path.Combine("wwwroot", attachmentName));
await request.Attachment.CopyToAsync(stream);
todo.Attachment = attachmentName;
}
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Ok();
});
// Remaining code removed for brevity.
public record struct NewTodoRequest([FromForm] string Name,
[FromForm] Visibility Visibility, IFormFile? Attachment);
Daha fazla bilgi için bu makalenin devamında yer alan AsParameters bölümüne bakın.
Örnek kodun tamamı AspNetCore.Docs.Samples deposundadır.
IFormFile ve IFormFileCollection'dan güvenli bağlama
Karmaşık form bağlama, kullanılarak IFormFile ve IFormFileCollection kullanılarak [FromForm]
desteklenir:
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
// Generate a form with an anti-forgery token and an /upload endpoint.
app.MapGet("/", (HttpContext context, IAntiforgery antiforgery) =>
{
var token = antiforgery.GetAndStoreTokens(context);
var html = MyUtils.GenerateHtmlForm(token.FormFieldName, token.RequestToken!);
return Results.Content(html, "text/html");
});
app.MapPost("/upload", async Task<Results<Ok<string>, BadRequest<string>>>
([FromForm] FileUploadForm fileUploadForm, HttpContext context,
IAntiforgery antiforgery) =>
{
await MyUtils.SaveFileWithName(fileUploadForm.FileDocument!,
fileUploadForm.Name!, app.Environment.ContentRootPath);
return TypedResults.Ok($"Your file with the description:" +
$" {fileUploadForm.Description} has been uploaded successfully");
});
app.Run();
ile [FromForm]
isteğe bağlı parametreler, bir sahte belirteç içerir. İstek işlendiğinde, kötü amaçlı yazılımdan koruma belirteci doğrulanır. Daha fazla bilgi için bkz . Minimal API'lerle sahteciliğe karşı koruma.
Daha fazla bilgi için bkz. Minimum API'lerde form bağlama.
Örnek kodun tamamı AspNetCore.Docs.Samples deposundadır.
Bağımlılık ekleme ile parametre bağlama
En düşük API'ler için parametre bağlama, tür hizmet olarak yapılandırıldığında bağımlılık ekleme yoluyla parametreleri bağlar. Özniteliğin bir parametreye açıkça uygulanması [FromServices]
gerekmez. Aşağıdaki kodda, her iki eylem de saati döndürür:
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IDateTime, SystemDateTime>();
var app = builder.Build();
app.MapGet("/", ( IDateTime dateTime) => dateTime.Now);
app.MapGet("/fs", ([FromServices] IDateTime dateTime) => dateTime.Now);
app.Run();
İsteğe bağlı parametreler
Yol işleyicilerinde bildirilen parametreler gerekli olarak değerlendirilir:
- Bir istek yolla eşleşiyorsa, yol işleyicisi yalnızca istekte tüm gerekli parametreler sağlandığında çalışır.
- Tüm gerekli parametrelerin sağlanamaması hatayla sonuçlanır.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int pageNumber) => $"Requesting page {pageNumber}");
app.Run();
URI | result |
---|---|
/products?pageNumber=3 |
3 döndürüldü |
/products |
BadHttpRequestException : Sorgu dizesinden gerekli "int pageNumber" parametresi sağlanmadı. |
/products/1 |
HTTP 404 hatası, eşleşen yol yok |
İsteğe bağlı yapmak pageNumber
için türü isteğe bağlı olarak tanımlayın veya varsayılan bir değer sağlayın:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");
string ListProducts(int pageNumber = 1) => $"Requesting page {pageNumber}";
app.MapGet("/products2", ListProducts);
app.Run();
URI | result |
---|---|
/products?pageNumber=3 |
3 döndürüldü |
/products |
1 döndürüldü |
/products2 |
1 döndürüldü |
Yukarıdaki null atanabilir ve varsayılan değer tüm kaynaklar için geçerlidir:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/products", (Product? product) => { });
app.Run();
Yukarıdaki kod, istek gövdesi gönderilmezse null bir ürünle yöntemini çağırır.
NOT: Geçersiz veri sağlanırsa ve parametre null atanabilirse, yol işleyicisi çalıştırılmaz .
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");
app.Run();
URI | result |
---|---|
/products?pageNumber=3 |
3 Döndürülen |
/products |
1 Döndürülen |
/products?pageNumber=two |
BadHttpRequestException : "two" parametresi "Nullable<int> pageNumber" bağlanamadı. |
/products/two |
HTTP 404 hatası, eşleşen yol yok |
Daha fazla bilgi için Bağlama Hataları bölümüne bakın.
Özel türler
Aşağıdaki türler açık öznitelikler olmadan bağlanır:
HttpContext: Geçerli HTTP isteği veya yanıtı hakkındaki tüm bilgileri barındıran bağlam:
app.MapGet("/", (HttpContext context) => context.Response.WriteAsync("Hello World"));
HttpRequest ve HttpResponse: HTTP isteği ve HTTP yanıtı:
app.MapGet("/", (HttpRequest request, HttpResponse response) => response.WriteAsync($"Hello World {request.Query["name"]}"));
CancellationToken: Geçerli HTTP isteğiyle ilişkili iptal belirteci:
app.MapGet("/", async (CancellationToken cancellationToken) => await MakeLongRunningRequestAsync(cancellationToken));
ClaimsPrincipal: İstekle ilişkili kullanıcı, ile HttpContext.Userilişkili:
app.MapGet("/", (ClaimsPrincipal user) => user.Identity.Name);
İstek gövdesini veya Stream
olarak bağlama PipeReader
İstek gövdesi, kullanıcının verileri işlemesi gereken senaryoları verimli bir Stream
şekilde desteklemek için veya PipeReader
olarak bağlanabilir ve:
- Verileri blob depolamaya depolayın veya bir kuyruk sağlayıcısına sıralayın.
- Depolanan verileri bir çalışan işlemi veya bulut işleviyle işleme.
Örneğin, veriler Azure Kuyruk depolama alanına sıralanabilir veya Azure Blob depolamada depolanabilir.
Aşağıdaki kod bir arka plan kuyruğu uygular:
using System.Text.Json;
using System.Threading.Channels;
namespace BackgroundQueueService;
class BackgroundQueue : BackgroundService
{
private readonly Channel<ReadOnlyMemory<byte>> _queue;
private readonly ILogger<BackgroundQueue> _logger;
public BackgroundQueue(Channel<ReadOnlyMemory<byte>> queue,
ILogger<BackgroundQueue> logger)
{
_queue = queue;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await foreach (var dataStream in _queue.Reader.ReadAllAsync(stoppingToken))
{
try
{
var person = JsonSerializer.Deserialize<Person>(dataStream.Span)!;
_logger.LogInformation($"{person.Name} is {person.Age} " +
$"years and from {person.Country}");
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
}
}
}
}
class Person
{
public string Name { get; set; } = String.Empty;
public int Age { get; set; }
public string Country { get; set; } = String.Empty;
}
Aşağıdaki kod istek gövdesini bir Stream
ile bağlar:
app.MapPost("/register", async (HttpRequest req, Stream body,
Channel<ReadOnlyMemory<byte>> queue) =>
{
if (req.ContentLength is not null && req.ContentLength > maxMessageSize)
{
return Results.BadRequest();
}
// We're not above the message size and we have a content length, or
// we're a chunked request and we're going to read up to the maxMessageSize + 1.
// We add one to the message size so that we can detect when a chunked request body
// is bigger than our configured max.
var readSize = (int?)req.ContentLength ?? (maxMessageSize + 1);
var buffer = new byte[readSize];
// Read at least that many bytes from the body.
var read = await body.ReadAtLeastAsync(buffer, readSize, throwOnEndOfStream: false);
// We read more than the max, so this is a bad request.
if (read > maxMessageSize)
{
return Results.BadRequest();
}
// Attempt to send the buffer to the background queue.
if (queue.Writer.TryWrite(buffer.AsMemory(0..read)))
{
return Results.Accepted();
}
// We couldn't accept the message since we're overloaded.
return Results.StatusCode(StatusCodes.Status429TooManyRequests);
});
Aşağıdaki kod dosyanın tamamını Program.cs
gösterir:
using System.Threading.Channels;
using BackgroundQueueService;
var builder = WebApplication.CreateBuilder(args);
// The max memory to use for the upload endpoint on this instance.
var maxMemory = 500 * 1024 * 1024;
// The max size of a single message, staying below the default LOH size of 85K.
var maxMessageSize = 80 * 1024;
// The max size of the queue based on those restrictions
var maxQueueSize = maxMemory / maxMessageSize;
// Create a channel to send data to the background queue.
builder.Services.AddSingleton<Channel<ReadOnlyMemory<byte>>>((_) =>
Channel.CreateBounded<ReadOnlyMemory<byte>>(maxQueueSize));
// Create a background queue service.
builder.Services.AddHostedService<BackgroundQueue>();
var app = builder.Build();
// curl --request POST 'https://localhost:<port>/register' --header 'Content-Type: application/json' --data-raw '{ "Name":"Samson", "Age": 23, "Country":"Nigeria" }'
// curl --request POST "https://localhost:<port>/register" --header "Content-Type: application/json" --data-raw "{ \"Name\":\"Samson\", \"Age\": 23, \"Country\":\"Nigeria\" }"
app.MapPost("/register", async (HttpRequest req, Stream body,
Channel<ReadOnlyMemory<byte>> queue) =>
{
if (req.ContentLength is not null && req.ContentLength > maxMessageSize)
{
return Results.BadRequest();
}
// We're not above the message size and we have a content length, or
// we're a chunked request and we're going to read up to the maxMessageSize + 1.
// We add one to the message size so that we can detect when a chunked request body
// is bigger than our configured max.
var readSize = (int?)req.ContentLength ?? (maxMessageSize + 1);
var buffer = new byte[readSize];
// Read at least that many bytes from the body.
var read = await body.ReadAtLeastAsync(buffer, readSize, throwOnEndOfStream: false);
// We read more than the max, so this is a bad request.
if (read > maxMessageSize)
{
return Results.BadRequest();
}
// Attempt to send the buffer to the background queue.
if (queue.Writer.TryWrite(buffer.AsMemory(0..read)))
{
return Results.Accepted();
}
// We couldn't accept the message since we're overloaded.
return Results.StatusCode(StatusCodes.Status429TooManyRequests);
});
app.Run();
- Verileri okurken,
Stream
ile aynı nesnedirHttpRequest.Body
. - İstek gövdesi varsayılan olarak arabelleğe alınmıyor. Ceset okunduktan sonra geri alınamaz. Akış birden çok kez okunamaz.
Stream
PipeReader
ve, temel alınan arabellekler atılacağı veya yeniden kullanılacağı için en düşük eylem işleyicisi dışında kullanılamaz.
IFormFile ve IFormFileCollection kullanılarak dosya yüklemeleri
Aşağıdaki kod dosyayı karşıya yüklemek için ve IFormFileCollection kullanırIFormFile:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.MapPost("/upload", async (IFormFile file) =>
{
var tempFile = Path.GetTempFileName();
app.Logger.LogInformation(tempFile);
using var stream = File.OpenWrite(tempFile);
await file.CopyToAsync(stream);
});
app.MapPost("/upload_many", async (IFormFileCollection myFiles) =>
{
foreach (var file in myFiles)
{
var tempFile = Path.GetTempFileName();
app.Logger.LogInformation(tempFile);
using var stream = File.OpenWrite(tempFile);
await file.CopyToAsync(stream);
}
});
app.Run();
Kimliği doğrulanmış dosya yükleme istekleri Yetkilendirme üst bilgisi, istemci sertifikası veya cookie üst bilgi kullanılarak desteklenir.
IFormCollection, IFormFile ve IFormFileCollection ile formlara bağlama
, IFormFileve IFormFileCollection kullanarak IFormCollectionform tabanlı parametrelerden bağlama desteklenir. Swagger kullanıcı arabirimiyle tümleştirmeyi desteklemek üzere form parametreleri için OpenAPI meta verileri çıkarılır.
Aşağıdaki kod, türdeki IFormFile
çıkarımlı bağlamayı kullanarak dosyaları karşıya yükler:
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http.HttpResults;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
string GetOrCreateFilePath(string fileName, string filesDirectory = "uploadFiles")
{
var directoryPath = Path.Combine(app.Environment.ContentRootPath, filesDirectory);
Directory.CreateDirectory(directoryPath);
return Path.Combine(directoryPath, fileName);
}
async Task UploadFileWithName(IFormFile file, string fileSaveName)
{
var filePath = GetOrCreateFilePath(fileSaveName);
await using var fileStream = new FileStream(filePath, FileMode.Create);
await file.CopyToAsync(fileStream);
}
app.MapGet("/", (HttpContext context, IAntiforgery antiforgery) =>
{
var token = antiforgery.GetAndStoreTokens(context);
var html = $"""
<html>
<body>
<form action="/upload" method="POST" enctype="multipart/form-data">
<input name="{token.FormFieldName}" type="hidden" value="{token.RequestToken}"/>
<input type="file" name="file" placeholder="Upload an image..." accept=".jpg,
.jpeg, .png" />
<input type="submit" />
</form>
</body>
</html>
""";
return Results.Content(html, "text/html");
});
app.MapPost("/upload", async Task<Results<Ok<string>,
BadRequest<string>>> (IFormFile file, HttpContext context, IAntiforgery antiforgery) =>
{
var fileSaveName = Guid.NewGuid().ToString("N") + Path.GetExtension(file.FileName);
await UploadFileWithName(file, fileSaveName);
return TypedResults.Ok("File uploaded successfully!");
});
app.Run();
Uyarı: Formlar uygulanırken uygulamanın Siteler Arası İstek Sahteciliği (XSRF/CSRF) saldırılarını engellemesi gerekir. Yukarıdaki kodda IAntiforgery hizmet, bir sahte belirteç oluşturup doğrulayarak XSRF saldırılarını önlemek için kullanılır:
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http.HttpResults;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
string GetOrCreateFilePath(string fileName, string filesDirectory = "uploadFiles")
{
var directoryPath = Path.Combine(app.Environment.ContentRootPath, filesDirectory);
Directory.CreateDirectory(directoryPath);
return Path.Combine(directoryPath, fileName);
}
async Task UploadFileWithName(IFormFile file, string fileSaveName)
{
var filePath = GetOrCreateFilePath(fileSaveName);
await using var fileStream = new FileStream(filePath, FileMode.Create);
await file.CopyToAsync(fileStream);
}
app.MapGet("/", (HttpContext context, IAntiforgery antiforgery) =>
{
var token = antiforgery.GetAndStoreTokens(context);
var html = $"""
<html>
<body>
<form action="/upload" method="POST" enctype="multipart/form-data">
<input name="{token.FormFieldName}" type="hidden" value="{token.RequestToken}"/>
<input type="file" name="file" placeholder="Upload an image..." accept=".jpg,
.jpeg, .png" />
<input type="submit" />
</form>
</body>
</html>
""";
return Results.Content(html, "text/html");
});
app.MapPost("/upload", async Task<Results<Ok<string>,
BadRequest<string>>> (IFormFile file, HttpContext context, IAntiforgery antiforgery) =>
{
var fileSaveName = Guid.NewGuid().ToString("N") + Path.GetExtension(file.FileName);
await UploadFileWithName(file, fileSaveName);
return TypedResults.Ok("File uploaded successfully!");
});
app.Run();
XSRF saldırıları hakkında daha fazla bilgi için bkz . Minimal API'lerle antiforgery
Daha fazla bilgi için bkz. Minimum API'lerde form bağlama;
Formlardan koleksiyonlara ve karmaşık türlere bağlama
Bağlama aşağıdakiler için desteklenir:
Aşağıdaki kod şunları gösterir:
- Çok parçalı form girişini karmaşık bir nesneye bağlayan en düşük uç nokta.
- Antiforgery belirteçlerinin oluşturulmasını ve doğrulanması için antiforgery hizmetlerini kullanma.
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
app.MapGet("/", (HttpContext context, IAntiforgery antiforgery) =>
{
var token = antiforgery.GetAndStoreTokens(context);
var html = $"""
<html><body>
<form action="/todo" method="POST" enctype="multipart/form-data">
<input name="{token.FormFieldName}"
type="hidden" value="{token.RequestToken}" />
<input type="text" name="name" />
<input type="date" name="dueDate" />
<input type="checkbox" name="isCompleted" value="true" />
<input type="submit" />
<input name="isCompleted" type="hidden" value="false" />
</form>
</body></html>
""";
return Results.Content(html, "text/html");
});
app.MapPost("/todo", async Task<Results<Ok<Todo>, BadRequest<string>>>
([FromForm] Todo todo, HttpContext context, IAntiforgery antiforgery) =>
{
try
{
await antiforgery.ValidateRequestAsync(context);
return TypedResults.Ok(todo);
}
catch (AntiforgeryValidationException e)
{
return TypedResults.BadRequest("Invalid antiforgery token");
}
});
app.Run();
class Todo
{
public string Name { get; set; } = string.Empty;
public bool IsCompleted { get; set; } = false;
public DateTime DueDate { get; set; } = DateTime.Now.Add(TimeSpan.FromDays(1));
}
Önceki kodda:
- JSON gövdesinden okunması gereken parametrelerden ayırt etmek için hedef parametreye özniteliğiyle
[FromForm]
açıklama eklenmelidir. - İstek Temsilcisi Oluşturucu ile derlenen en düşük API'ler için karmaşık veya koleksiyon türlerinden bağlama desteklenmez .
- İşaretlemeyi adı ve değeri
false
ile ek birisCompleted
gizli giriş gösterir.isCompleted
Form gönderildiğinde onay kutusu işaretlenirse, hem değerlertrue
false
hem de değer olarak gönderilir. Onay kutusunun işareti kaldırılırsa, yalnızca gizli giriş değerifalse
gönderilir. ASP.NET Core model bağlama işlemi, birbool
değere bağlanırken yalnızca ilk değeri okur ve bu da işaretli onay kutularına vefalse
işaretsiz onay kutularına nedentrue
olur.
Önceki uç noktaya gönderilen form verilerine bir örnek aşağıdaki gibi görünür:
__RequestVerificationToken: CfDJ8Bveip67DklJm5vI2PF2VOUZ594RC8kcGWpTnVV17zCLZi1yrs-CSz426ZRRrQnEJ0gybB0AD7hTU-0EGJXDU-OaJaktgAtWLIaaEWMOWCkoxYYm-9U9eLV7INSUrQ6yBHqdMEE_aJpD4AI72gYiCqc
name: Walk the dog
dueDate: 2024-04-06
isCompleted: true
isCompleted: false
Üst bilgilerden ve sorgu dizelerinden dizileri ve dize değerlerini bağlama
Aşağıdaki kod, sorgu dizelerini ilkel türler, dize dizileri ve StringValues dizilerine bağlamayı gösterir:
// Bind query string values to a primitive type array.
// GET /tags?q=1&q=2&q=3
app.MapGet("/tags", (int[] q) =>
$"tag1: {q[0]} , tag2: {q[1]}, tag3: {q[2]}");
// Bind to a string array.
// GET /tags2?names=john&names=jack&names=jane
app.MapGet("/tags2", (string[] names) =>
$"tag1: {names[0]} , tag2: {names[1]}, tag3: {names[2]}");
// Bind to StringValues.
// GET /tags3?names=john&names=jack&names=jane
app.MapGet("/tags3", (StringValues names) =>
$"tag1: {names[0]} , tag2: {names[1]}, tag3: {names[2]}");
Sorgu dizelerini veya üst bilgi değerlerini karmaşık türlerden oluşan bir diziye bağlama, tür TryParse
uygulandığında desteklenir. Aşağıdaki kod bir dize dizisine bağlanır ve belirtilen etiketlere sahip tüm öğeleri döndürür:
// GET /todoitems/tags?tags=home&tags=work
app.MapGet("/todoitems/tags", async (Tag[] tags, TodoDb db) =>
{
return await db.Todos
.Where(t => tags.Select(i => i.Name).Contains(t.Tag.Name))
.ToListAsync();
});
Aşağıdaki kod modeli ve gerekli TryParse
uygulamayı gösterir:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
// This is an owned entity.
public Tag Tag { get; set; } = new();
}
[Owned]
public class Tag
{
public string? Name { get; set; } = "n/a";
public static bool TryParse(string? name, out Tag tag)
{
if (name is null)
{
tag = default!;
return false;
}
tag = new Tag { Name = name };
return true;
}
}
Aşağıdaki kod bir int
diziye bağlanır:
// GET /todoitems/query-string-ids?ids=1&ids=3
app.MapGet("/todoitems/query-string-ids", async (int[] ids, TodoDb db) =>
{
return await db.Todos
.Where(t => ids.Contains(t.Id))
.ToListAsync();
});
Yukarıdaki kodu test etmek için aşağıdaki uç noktayı ekleyerek veritabanını öğelerle Todo
doldurun:
// POST /todoitems/batch
app.MapPost("/todoitems/batch", async (Todo[] todos, TodoDb db) =>
{
await db.Todos.AddRangeAsync(todos);
await db.SaveChangesAsync();
return Results.Ok(todos);
});
Aşağıdaki verileri önceki uç noktaya geçirmek için gibi HttpRepl
bir araç kullanın:
[
{
"id": 1,
"name": "Have Breakfast",
"isComplete": true,
"tag": {
"name": "home"
}
},
{
"id": 2,
"name": "Have Lunch",
"isComplete": true,
"tag": {
"name": "work"
}
},
{
"id": 3,
"name": "Have Supper",
"isComplete": true,
"tag": {
"name": "home"
}
},
{
"id": 4,
"name": "Have Snacks",
"isComplete": true,
"tag": {
"name": "N/A"
}
}
]
Aşağıdaki kod üst bilgi anahtarına X-Todo-Id
bağlanır ve eşleşen Id
değerlere Todo
sahip öğeleri döndürür:
// GET /todoitems/header-ids
// The keys of the headers should all be X-Todo-Id with different values
app.MapGet("/todoitems/header-ids", async ([FromHeader(Name = "X-Todo-Id")] int[] ids, TodoDb db) =>
{
return await db.Todos
.Where(t => ids.Contains(t.Id))
.ToListAsync();
});
Not
Sorgu dizesinden bir string[]
bağlama yapılırken eşleşen sorgu dizesi değerinin olmaması, null değer yerine boş bir diziyle sonuçlanır.
[AsParameters] ile bağımsız değişken listeleri için parametre bağlama
AsParametersAttribute karmaşık veya özyinelemeli model bağlamasına değil, türlere basit parametre bağlamasını etkinleştirir.
Aşağıdaki kodu inceleyin:
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
var app = builder.Build();
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.Select(x => new TodoItemDTO(x)).ToListAsync());
app.MapGet("/todoitems/{id}",
async (int Id, TodoDb Db) =>
await Db.Todos.FindAsync(Id)
is Todo todo
? Results.Ok(new TodoItemDTO(todo))
: Results.NotFound());
// Remaining code removed for brevity.
Aşağıdaki GET
uç noktayı göz önünde bulundurun:
app.MapGet("/todoitems/{id}",
async (int Id, TodoDb Db) =>
await Db.Todos.FindAsync(Id)
is Todo todo
? Results.Ok(new TodoItemDTO(todo))
: Results.NotFound());
struct
Aşağıdakiler, önceki vurgulanan parametreleri değiştirmek için kullanılabilir:
struct TodoItemRequest
{
public int Id { get; set; }
public TodoDb Db { get; set; }
}
Yeniden düzenlenmiş GET
uç nokta, AsParameters özniteliğiyle öncekini struct
kullanır:
app.MapGet("/ap/todoitems/{id}",
async ([AsParameters] TodoItemRequest request) =>
await request.Db.Todos.FindAsync(request.Id)
is Todo todo
? Results.Ok(new TodoItemDTO(todo))
: Results.NotFound());
Aşağıdaki kod, uygulamadaki ek uç noktaları gösterir:
app.MapPost("/todoitems", async (TodoItemDTO Dto, TodoDb Db) =>
{
var todoItem = new Todo
{
IsComplete = Dto.IsComplete,
Name = Dto.Name
};
Db.Todos.Add(todoItem);
await Db.SaveChangesAsync();
return Results.Created($"/todoitems/{todoItem.Id}", new TodoItemDTO(todoItem));
});
app.MapPut("/todoitems/{id}", async (int Id, TodoItemDTO Dto, TodoDb Db) =>
{
var todo = await Db.Todos.FindAsync(Id);
if (todo is null) return Results.NotFound();
todo.Name = Dto.Name;
todo.IsComplete = Dto.IsComplete;
await Db.SaveChangesAsync();
return Results.NoContent();
});
app.MapDelete("/todoitems/{id}", async (int Id, TodoDb Db) =>
{
if (await Db.Todos.FindAsync(Id) is Todo todo)
{
Db.Todos.Remove(todo);
await Db.SaveChangesAsync();
return Results.Ok(new TodoItemDTO(todo));
}
return Results.NotFound();
});
Parametre listelerini yeniden düzenlemede aşağıdaki sınıflar kullanılır:
class CreateTodoItemRequest
{
public TodoItemDTO Dto { get; set; } = default!;
public TodoDb Db { get; set; } = default!;
}
class EditTodoItemRequest
{
public int Id { get; set; }
public TodoItemDTO Dto { get; set; } = default!;
public TodoDb Db { get; set; } = default!;
}
Aşağıdaki kod, ve önceki struct
ve sınıflarını kullanarak AsParameters
yeniden düzenlenmiş uç noktaları gösterir:
app.MapPost("/ap/todoitems", async ([AsParameters] CreateTodoItemRequest request) =>
{
var todoItem = new Todo
{
IsComplete = request.Dto.IsComplete,
Name = request.Dto.Name
};
request.Db.Todos.Add(todoItem);
await request.Db.SaveChangesAsync();
return Results.Created($"/todoitems/{todoItem.Id}", new TodoItemDTO(todoItem));
});
app.MapPut("/ap/todoitems/{id}", async ([AsParameters] EditTodoItemRequest request) =>
{
var todo = await request.Db.Todos.FindAsync(request.Id);
if (todo is null) return Results.NotFound();
todo.Name = request.Dto.Name;
todo.IsComplete = request.Dto.IsComplete;
await request.Db.SaveChangesAsync();
return Results.NoContent();
});
app.MapDelete("/ap/todoitems/{id}", async ([AsParameters] TodoItemRequest request) =>
{
if (await request.Db.Todos.FindAsync(request.Id) is Todo todo)
{
request.Db.Todos.Remove(todo);
await request.Db.SaveChangesAsync();
return Results.Ok(new TodoItemDTO(todo));
}
return Results.NotFound();
});
Yukarıdaki parametreleri değiştirmek için aşağıdaki record
türler kullanılabilir:
record TodoItemRequest(int Id, TodoDb Db);
record CreateTodoItemRequest(TodoItemDTO Dto, TodoDb Db);
record EditTodoItemRequest(int Id, TodoItemDTO Dto, TodoDb Db);
struct
ile AsParameters
kullanmak, tür kullanmaktan record
daha yüksek performansa sahip olabilir.
AspNetCore.Docs.Samples deposundaki tam örnek kod.
Özel Bağlama
Parametre bağlamasını özelleştirmenin iki yolu vardır:
- Yol, sorgu ve üst bilgi bağlama kaynakları için, türü için statik
TryParse
bir yöntem ekleyerek özel türleri bağlayın. - Bir türe bir
BindAsync
yöntem uygulayarak bağlama işlemini denetleyin.
TryParse
TryParse
iki API'ye sahiptir:
public static bool TryParse(string value, out T result);
public static bool TryParse(string value, IFormatProvider provider, out T result);
Aşağıdaki kod URI /map?Point=12.3,10.1
ile birlikte görüntülenirPoint: 12.3, 10.1
:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// GET /map?Point=12.3,10.1
app.MapGet("/map", (Point point) => $"Point: {point.X}, {point.Y}");
app.Run();
public class Point
{
public double X { get; set; }
public double Y { get; set; }
public static bool TryParse(string? value, IFormatProvider? provider,
out Point? point)
{
// Format is "(12.3,10.1)"
var trimmedValue = value?.TrimStart('(').TrimEnd(')');
var segments = trimmedValue?.Split(',',
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (segments?.Length == 2
&& double.TryParse(segments[0], out var x)
&& double.TryParse(segments[1], out var y))
{
point = new Point { X = x, Y = y };
return true;
}
point = null;
return false;
}
}
BindAsync
BindAsync
aşağıdaki API'lere sahiptir:
public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo parameter);
public static ValueTask<T?> BindAsync(HttpContext context);
Aşağıdaki kod URI /products?SortBy=xyz&SortDir=Desc&Page=99
ile birlikte görüntülenirSortBy:xyz, SortDirection:Desc, CurrentPage:99
:
using System.Reflection;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// GET /products?SortBy=xyz&SortDir=Desc&Page=99
app.MapGet("/products", (PagingData pageData) => $"SortBy:{pageData.SortBy}, " +
$"SortDirection:{pageData.SortDirection}, CurrentPage:{pageData.CurrentPage}");
app.Run();
public class PagingData
{
public string? SortBy { get; init; }
public SortDirection SortDirection { get; init; }
public int CurrentPage { get; init; } = 1;
public static ValueTask<PagingData?> BindAsync(HttpContext context,
ParameterInfo parameter)
{
const string sortByKey = "sortBy";
const string sortDirectionKey = "sortDir";
const string currentPageKey = "page";
Enum.TryParse<SortDirection>(context.Request.Query[sortDirectionKey],
ignoreCase: true, out var sortDirection);
int.TryParse(context.Request.Query[currentPageKey], out var page);
page = page == 0 ? 1 : page;
var result = new PagingData
{
SortBy = context.Request.Query[sortByKey],
SortDirection = sortDirection,
CurrentPage = page
};
return ValueTask.FromResult<PagingData?>(result);
}
}
public enum SortDirection
{
Default,
Asc,
Desc
}
Bağlama hataları
Bağlama başarısız olduğunda, çerçeve hata ayıklama iletisini günlüğe kaydeder ve hata moduna bağlı olarak istemciye çeşitli durum kodları döndürür.
Hata modu | Null Atanabilir Parametre Türü | Bağlama Kaynağı | Durum kodu |
---|---|---|---|
{ParameterType}.TryParse Döndürür false |
evet | route/query/header | 400 |
{ParameterType}.BindAsync Döndürür null |
evet | özel | 400 |
{ParameterType}.BindAsync Atar |
fark etmez | özel | 500 |
JSON gövdesinin seri durumdan çıkarılamaması | fark etmez | gövde | 400 |
Yanlış içerik türü (değil application/json ) |
fark etmez | gövde | Kategori 415 |
Bağlama Önceliği
Bir parametreden bağlama kaynağını belirleme kuralları:
- Parametrede (From* öznitelikleri) aşağıdaki sırayla tanımlanan açık öznitelik:
- Yol değerleri:
[FromRoute]
- Sorgu dizesi:
[FromQuery]
- Üstbilgi:
[FromHeader]
- Beden:
[FromBody]
- Form:
[FromForm]
- Hizmet:
[FromServices]
- Parametre değerleri:
[AsParameters]
- Yol değerleri:
- Özel türler
HttpContext
HttpRequest
(HttpContext.Request
)HttpResponse
(HttpContext.Response
)ClaimsPrincipal
(HttpContext.User
)CancellationToken
(HttpContext.RequestAborted
)IFormCollection
(HttpContext.Request.Form
)IFormFileCollection
(HttpContext.Request.Form.Files
)IFormFile
(HttpContext.Request.Form.Files[paramName]
)Stream
(HttpContext.Request.Body
)PipeReader
(HttpContext.Request.BodyReader
)
- Parametre türü geçerli bir statik
BindAsync
yönteme sahiptir. - Parametre türü bir dizedir veya geçerli bir statik
TryParse
yöntemi vardır.- Parametre adı örneğin, yol şablonunda varsa,
app.Map("/todo/{id}", (int id) => {});
rotadan bağlanır. - Sorgu dizesinden bağlanır.
- Parametre adı örneğin, yol şablonunda varsa,
- Parametre türü bağımlılık ekleme tarafından sağlanan bir hizmetse, kaynak olarak bu hizmeti kullanır.
- parametresi gövdedendir.
Gövde bağlaması için JSON seri durumdan çıkarma seçeneklerini yapılandırma
Gövde bağlama kaynağı seri durumdan çıkarma için kullanır System.Text.Json . Bu varsayılanı değiştirmek mümkün değildir, ancak JSON serileştirme ve seri durumdan çıkarma seçenekleri yapılandırılabilir.
JSON seri durumdan çıkarma seçeneklerini genel olarak yapılandırma
Bir uygulama için genel olarak geçerli olan seçenekler çağrılarak ConfigureHttpJsonOptionsyapılandırılabilir. Aşağıdaki örnek, ortak alanları ve JSON çıkışını biçimlendirmektedir.
var builder = WebApplication.CreateBuilder(args);
builder.Services.ConfigureHttpJsonOptions(options => {
options.SerializerOptions.WriteIndented = true;
options.SerializerOptions.IncludeFields = true;
});
var app = builder.Build();
app.MapPost("/", (Todo todo) => {
if (todo is not null) {
todo.Name = todo.NameField;
}
return todo;
});
app.Run();
class Todo {
public string? Name { get; set; }
public string? NameField;
public bool IsComplete { get; set; }
}
// If the request body contains the following JSON:
//
// {"nameField":"Walk dog", "isComplete":false}
//
// The endpoint returns the following JSON:
//
// {
// "name":"Walk dog",
// "nameField":"Walk dog",
// "isComplete":false
// }
Örnek kod hem serileştirmeyi hem de seri durumdan çıkarmayı yapılandırdığından çıktı JSON'unu okuyabilir NameField
ve içerebilir NameField
.
Uç nokta için JSON seri durumdan çıkarma seçeneklerini yapılandırma
ReadFromJsonAsync bir nesneyi kabul JsonSerializerOptions eden aşırı yüklemelere sahiptir. Aşağıdaki örnek, ortak alanları ve JSON çıkışını biçimlendirmektedir.
using System.Text.Json;
var app = WebApplication.Create();
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web) {
IncludeFields = true,
WriteIndented = true
};
app.MapPost("/", async (HttpContext context) => {
if (context.Request.HasJsonContentType()) {
var todo = await context.Request.ReadFromJsonAsync<Todo>(options);
if (todo is not null) {
todo.Name = todo.NameField;
}
return Results.Ok(todo);
}
else {
return Results.BadRequest();
}
});
app.Run();
class Todo
{
public string? Name { get; set; }
public string? NameField;
public bool IsComplete { get; set; }
}
// If the request body contains the following JSON:
//
// {"nameField":"Walk dog", "isComplete":false}
//
// The endpoint returns the following JSON:
//
// {
// "name":"Walk dog",
// "isComplete":false
// }
Yukarıdaki kod özelleştirilmiş seçenekleri yalnızca seri durumdan çıkarma işlemine uyguladığından çıktı JSON dışlar NameField
.
İstek gövdesini okuma
bir veya HttpRequest parametresini kullanarak HttpContext istek gövdesini doğrudan okuyun:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/uploadstream", async (IConfiguration config, HttpRequest request) =>
{
var filePath = Path.Combine(config["StoredFilesPath"], Path.GetRandomFileName());
await using var writeStream = File.Create(filePath);
await request.BodyReader.CopyToAsync(writeStream);
});
app.Run();
Yukarıdaki kod:
- kullanarak HttpRequest.BodyReaderistek gövdesine erişir.
- İstek gövdesini yerel bir dosyaya kopyalar.
Parametre bağlama, istek verilerini yol işleyicileri tarafından ifade edilen kesin olarak belirlenmiş parametrelere dönüştürme işlemidir. Bağlama kaynağı, parametrelerin nereye bağlandığını belirler. Bağlama kaynakları HTTP yöntemine ve parametre türüne göre açık veya çıkarılabilir.
Desteklenen bağlama kaynakları:
- Yol değerleri
- Sorgu dizesi
- Üst bilgi
- Gövde (JSON olarak)
- Bağımlılık ekleme tarafından sağlanan hizmetler
- Özel
.NET 6 ve 7'de form değerlerinden bağlama yerel olarak desteklenmez.
Aşağıdaki GET
yol işleyicisi bu parametre bağlama kaynaklarından bazılarını kullanır:
var builder = WebApplication.CreateBuilder(args);
// Added as service
builder.Services.AddSingleton<Service>();
var app = builder.Build();
app.MapGet("/{id}", (int id,
int page,
[FromHeader(Name = "X-CUSTOM-HEADER")] string customHeader,
Service service) => { });
class Service { }
Aşağıdaki tabloda, önceki örnekte kullanılan parametrelerle ilişkili bağlama kaynakları arasındaki ilişki gösterilmektedir.
Parametre | Bağlama Kaynağı |
---|---|
id |
yol değeri |
page |
sorgu dizesi |
customHeader |
üst bilgi |
service |
Bağımlılık ekleme tarafından sağlanır |
, , HEAD
OPTIONS
ve DELETE
HTTP yöntemleri GET
gövdeden örtük olarak bağlanmaz. Bu HTTP yöntemleri için gövdeden (JSON olarak) bağlanmak için ile [FromBody]
açıkça bağlayın veya dosyasından HttpRequestokuyun.
Aşağıdaki örnek POST yol işleyicisi, parametresi için person
bir bağlama gövdesi kaynağı (JSON olarak) kullanır:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/", (Person person) => { });
record Person(string Name, int Age);
Yukarıdaki örneklerde yer alan parametrelerin tümü istek verilerinden otomatik olarak bağlıdır. Parametre bağlamanın sağladığı kolaylığı göstermek için aşağıdaki yol işleyicileri istek verilerini doğrudan istekten nasıl okuyacaklarını gösterir:
app.MapGet("/{id}", (HttpRequest request) =>
{
var id = request.RouteValues["id"];
var page = request.Query["page"];
var customHeader = request.Headers["X-CUSTOM-HEADER"];
// ...
});
app.MapPost("/", async (HttpRequest request) =>
{
var person = await request.ReadFromJsonAsync<Person>();
// ...
});
Açık Parametre Bağlama
Öznitelikler, parametrelerin nereye bağlı olduğunu açıkça bildirmek için kullanılabilir.
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
// Added as service
builder.Services.AddSingleton<Service>();
var app = builder.Build();
app.MapGet("/{id}", ([FromRoute] int id,
[FromQuery(Name = "p")] int page,
[FromServices] Service service,
[FromHeader(Name = "Content-Type")] string contentType)
=> {});
class Service { }
record Person(string Name, int Age);
Parametre | Bağlama Kaynağı |
---|---|
id |
adıyla yol değeri id |
page |
adlı sorgu dizesi "p" |
service |
Bağımlılık ekleme tarafından sağlanır |
contentType |
adıyla üst bilgi "Content-Type" |
Not
.NET 6 ve 7'de form değerlerinden bağlama yerel olarak desteklenmez.
Bağımlılık ekleme ile parametre bağlama
En düşük API'ler için parametre bağlama, tür hizmet olarak yapılandırıldığında bağımlılık ekleme yoluyla parametreleri bağlar. Özniteliğin bir parametreye açıkça uygulanması [FromServices]
gerekmez. Aşağıdaki kodda, her iki eylem de saati döndürür:
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IDateTime, SystemDateTime>();
var app = builder.Build();
app.MapGet("/", ( IDateTime dateTime) => dateTime.Now);
app.MapGet("/fs", ([FromServices] IDateTime dateTime) => dateTime.Now);
app.Run();
İsteğe bağlı parametreler
Yol işleyicilerinde bildirilen parametreler gerekli olarak değerlendirilir:
- Bir istek yolla eşleşiyorsa, yol işleyicisi yalnızca istekte tüm gerekli parametreler sağlandığında çalışır.
- Tüm gerekli parametrelerin sağlanamaması hatayla sonuçlanır.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int pageNumber) => $"Requesting page {pageNumber}");
app.Run();
URI | result |
---|---|
/products?pageNumber=3 |
3 döndürüldü |
/products |
BadHttpRequestException : Sorgu dizesinden gerekli "int pageNumber" parametresi sağlanmadı. |
/products/1 |
HTTP 404 hatası, eşleşen yol yok |
İsteğe bağlı yapmak pageNumber
için türü isteğe bağlı olarak tanımlayın veya varsayılan bir değer sağlayın:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");
string ListProducts(int pageNumber = 1) => $"Requesting page {pageNumber}";
app.MapGet("/products2", ListProducts);
app.Run();
URI | result |
---|---|
/products?pageNumber=3 |
3 döndürüldü |
/products |
1 döndürüldü |
/products2 |
1 döndürüldü |
Yukarıdaki null atanabilir ve varsayılan değer tüm kaynaklar için geçerlidir:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/products", (Product? product) => { });
app.Run();
Yukarıdaki kod, istek gövdesi gönderilmezse null bir ürünle yöntemini çağırır.
NOT: Geçersiz veri sağlanırsa ve parametre null atanabilirse, yol işleyicisi çalıştırılmaz .
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");
app.Run();
URI | result |
---|---|
/products?pageNumber=3 |
3 Döndürülen |
/products |
1 Döndürülen |
/products?pageNumber=two |
BadHttpRequestException : "two" parametresi "Nullable<int> pageNumber" bağlanamadı. |
/products/two |
HTTP 404 hatası, eşleşen yol yok |
Daha fazla bilgi için Bağlama Hataları bölümüne bakın.
Özel türler
Aşağıdaki türler açık öznitelikler olmadan bağlanır:
HttpContext: Geçerli HTTP isteği veya yanıtı hakkındaki tüm bilgileri barındıran bağlam:
app.MapGet("/", (HttpContext context) => context.Response.WriteAsync("Hello World"));
HttpRequest ve HttpResponse: HTTP isteği ve HTTP yanıtı:
app.MapGet("/", (HttpRequest request, HttpResponse response) => response.WriteAsync($"Hello World {request.Query["name"]}"));
CancellationToken: Geçerli HTTP isteğiyle ilişkili iptal belirteci:
app.MapGet("/", async (CancellationToken cancellationToken) => await MakeLongRunningRequestAsync(cancellationToken));
ClaimsPrincipal: İstekle ilişkili kullanıcı, ile HttpContext.Userilişkili:
app.MapGet("/", (ClaimsPrincipal user) => user.Identity.Name);
İstek gövdesini veya Stream
olarak bağlama PipeReader
İstek gövdesi, kullanıcının verileri işlemesi gereken senaryoları verimli bir Stream
şekilde desteklemek için veya PipeReader
olarak bağlanabilir ve:
- Verileri blob depolamaya depolayın veya bir kuyruk sağlayıcısına sıralayın.
- Depolanan verileri bir çalışan işlemi veya bulut işleviyle işleme.
Örneğin, veriler Azure Kuyruk depolama alanına sıralanabilir veya Azure Blob depolamada depolanabilir.
Aşağıdaki kod bir arka plan kuyruğu uygular:
using System.Text.Json;
using System.Threading.Channels;
namespace BackgroundQueueService;
class BackgroundQueue : BackgroundService
{
private readonly Channel<ReadOnlyMemory<byte>> _queue;
private readonly ILogger<BackgroundQueue> _logger;
public BackgroundQueue(Channel<ReadOnlyMemory<byte>> queue,
ILogger<BackgroundQueue> logger)
{
_queue = queue;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await foreach (var dataStream in _queue.Reader.ReadAllAsync(stoppingToken))
{
try
{
var person = JsonSerializer.Deserialize<Person>(dataStream.Span)!;
_logger.LogInformation($"{person.Name} is {person.Age} " +
$"years and from {person.Country}");
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
}
}
}
}
class Person
{
public string Name { get; set; } = String.Empty;
public int Age { get; set; }
public string Country { get; set; } = String.Empty;
}
Aşağıdaki kod istek gövdesini bir Stream
ile bağlar:
app.MapPost("/register", async (HttpRequest req, Stream body,
Channel<ReadOnlyMemory<byte>> queue) =>
{
if (req.ContentLength is not null && req.ContentLength > maxMessageSize)
{
return Results.BadRequest();
}
// We're not above the message size and we have a content length, or
// we're a chunked request and we're going to read up to the maxMessageSize + 1.
// We add one to the message size so that we can detect when a chunked request body
// is bigger than our configured max.
var readSize = (int?)req.ContentLength ?? (maxMessageSize + 1);
var buffer = new byte[readSize];
// Read at least that many bytes from the body.
var read = await body.ReadAtLeastAsync(buffer, readSize, throwOnEndOfStream: false);
// We read more than the max, so this is a bad request.
if (read > maxMessageSize)
{
return Results.BadRequest();
}
// Attempt to send the buffer to the background queue.
if (queue.Writer.TryWrite(buffer.AsMemory(0..read)))
{
return Results.Accepted();
}
// We couldn't accept the message since we're overloaded.
return Results.StatusCode(StatusCodes.Status429TooManyRequests);
});
Aşağıdaki kod dosyanın tamamını Program.cs
gösterir:
using System.Threading.Channels;
using BackgroundQueueService;
var builder = WebApplication.CreateBuilder(args);
// The max memory to use for the upload endpoint on this instance.
var maxMemory = 500 * 1024 * 1024;
// The max size of a single message, staying below the default LOH size of 85K.
var maxMessageSize = 80 * 1024;
// The max size of the queue based on those restrictions
var maxQueueSize = maxMemory / maxMessageSize;
// Create a channel to send data to the background queue.
builder.Services.AddSingleton<Channel<ReadOnlyMemory<byte>>>((_) =>
Channel.CreateBounded<ReadOnlyMemory<byte>>(maxQueueSize));
// Create a background queue service.
builder.Services.AddHostedService<BackgroundQueue>();
var app = builder.Build();
// curl --request POST 'https://localhost:<port>/register' --header 'Content-Type: application/json' --data-raw '{ "Name":"Samson", "Age": 23, "Country":"Nigeria" }'
// curl --request POST "https://localhost:<port>/register" --header "Content-Type: application/json" --data-raw "{ \"Name\":\"Samson\", \"Age\": 23, \"Country\":\"Nigeria\" }"
app.MapPost("/register", async (HttpRequest req, Stream body,
Channel<ReadOnlyMemory<byte>> queue) =>
{
if (req.ContentLength is not null && req.ContentLength > maxMessageSize)
{
return Results.BadRequest();
}
// We're not above the message size and we have a content length, or
// we're a chunked request and we're going to read up to the maxMessageSize + 1.
// We add one to the message size so that we can detect when a chunked request body
// is bigger than our configured max.
var readSize = (int?)req.ContentLength ?? (maxMessageSize + 1);
var buffer = new byte[readSize];
// Read at least that many bytes from the body.
var read = await body.ReadAtLeastAsync(buffer, readSize, throwOnEndOfStream: false);
// We read more than the max, so this is a bad request.
if (read > maxMessageSize)
{
return Results.BadRequest();
}
// Attempt to send the buffer to the background queue.
if (queue.Writer.TryWrite(buffer.AsMemory(0..read)))
{
return Results.Accepted();
}
// We couldn't accept the message since we're overloaded.
return Results.StatusCode(StatusCodes.Status429TooManyRequests);
});
app.Run();
- Verileri okurken,
Stream
ile aynı nesnedirHttpRequest.Body
. - İstek gövdesi varsayılan olarak arabelleğe alınmıyor. Ceset okunduktan sonra geri alınamaz. Akış birden çok kez okunamaz.
Stream
PipeReader
ve, temel alınan arabellekler atılacağı veya yeniden kullanılacağı için en düşük eylem işleyicisi dışında kullanılamaz.
IFormFile ve IFormFileCollection kullanılarak dosya yüklemeleri
Aşağıdaki kod dosyayı karşıya yüklemek için ve IFormFileCollection kullanırIFormFile:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.MapPost("/upload", async (IFormFile file) =>
{
var tempFile = Path.GetTempFileName();
app.Logger.LogInformation(tempFile);
using var stream = File.OpenWrite(tempFile);
await file.CopyToAsync(stream);
});
app.MapPost("/upload_many", async (IFormFileCollection myFiles) =>
{
foreach (var file in myFiles)
{
var tempFile = Path.GetTempFileName();
app.Logger.LogInformation(tempFile);
using var stream = File.OpenWrite(tempFile);
await file.CopyToAsync(stream);
}
});
app.Run();
Kimliği doğrulanmış dosya yükleme istekleri Yetkilendirme üst bilgisi, istemci sertifikası veya cookie üst bilgi kullanılarak desteklenir.
ASP.NET Core 7.0'da antiforgery için yerleşik destek yoktur. Antiforgery, ASP.NET Core 8.0 ve sonraki sürümlerde kullanılabilir. Ancak, hizmeti kullanılarak IAntiforgery
uygulanabilir.
Üst bilgilerden ve sorgu dizelerinden dizileri ve dize değerlerini bağlama
Aşağıdaki kod, sorgu dizelerini ilkel türler, dize dizileri ve StringValues dizilerine bağlamayı gösterir:
// Bind query string values to a primitive type array.
// GET /tags?q=1&q=2&q=3
app.MapGet("/tags", (int[] q) =>
$"tag1: {q[0]} , tag2: {q[1]}, tag3: {q[2]}");
// Bind to a string array.
// GET /tags2?names=john&names=jack&names=jane
app.MapGet("/tags2", (string[] names) =>
$"tag1: {names[0]} , tag2: {names[1]}, tag3: {names[2]}");
// Bind to StringValues.
// GET /tags3?names=john&names=jack&names=jane
app.MapGet("/tags3", (StringValues names) =>
$"tag1: {names[0]} , tag2: {names[1]}, tag3: {names[2]}");
Sorgu dizelerini veya üst bilgi değerlerini karmaşık türlerden oluşan bir diziye bağlama, tür TryParse
uygulandığında desteklenir. Aşağıdaki kod bir dize dizisine bağlanır ve belirtilen etiketlere sahip tüm öğeleri döndürür:
// GET /todoitems/tags?tags=home&tags=work
app.MapGet("/todoitems/tags", async (Tag[] tags, TodoDb db) =>
{
return await db.Todos
.Where(t => tags.Select(i => i.Name).Contains(t.Tag.Name))
.ToListAsync();
});
Aşağıdaki kod modeli ve gerekli TryParse
uygulamayı gösterir:
public class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
// This is an owned entity.
public Tag Tag { get; set; } = new();
}
[Owned]
public class Tag
{
public string? Name { get; set; } = "n/a";
public static bool TryParse(string? name, out Tag tag)
{
if (name is null)
{
tag = default!;
return false;
}
tag = new Tag { Name = name };
return true;
}
}
Aşağıdaki kod bir int
diziye bağlanır:
// GET /todoitems/query-string-ids?ids=1&ids=3
app.MapGet("/todoitems/query-string-ids", async (int[] ids, TodoDb db) =>
{
return await db.Todos
.Where(t => ids.Contains(t.Id))
.ToListAsync();
});
Yukarıdaki kodu test etmek için aşağıdaki uç noktayı ekleyerek veritabanını öğelerle Todo
doldurun:
// POST /todoitems/batch
app.MapPost("/todoitems/batch", async (Todo[] todos, TodoDb db) =>
{
await db.Todos.AddRangeAsync(todos);
await db.SaveChangesAsync();
return Results.Ok(todos);
});
Aşağıdaki verileri önceki uç noktaya geçirmek gibi HttpRepl
bir API test aracı kullanın:
[
{
"id": 1,
"name": "Have Breakfast",
"isComplete": true,
"tag": {
"name": "home"
}
},
{
"id": 2,
"name": "Have Lunch",
"isComplete": true,
"tag": {
"name": "work"
}
},
{
"id": 3,
"name": "Have Supper",
"isComplete": true,
"tag": {
"name": "home"
}
},
{
"id": 4,
"name": "Have Snacks",
"isComplete": true,
"tag": {
"name": "N/A"
}
}
]
Aşağıdaki kod üst bilgi anahtarına X-Todo-Id
bağlanır ve eşleşen Id
değerlere Todo
sahip öğeleri döndürür:
// GET /todoitems/header-ids
// The keys of the headers should all be X-Todo-Id with different values
app.MapGet("/todoitems/header-ids", async ([FromHeader(Name = "X-Todo-Id")] int[] ids, TodoDb db) =>
{
return await db.Todos
.Where(t => ids.Contains(t.Id))
.ToListAsync();
});
Not
Sorgu dizesinden bir string[]
bağlama yapılırken eşleşen sorgu dizesi değerinin olmaması, null değer yerine boş bir diziyle sonuçlanır.
[AsParameters] ile bağımsız değişken listeleri için parametre bağlama
AsParametersAttribute karmaşık veya özyinelemeli model bağlamasına değil, türlere basit parametre bağlamasını etkinleştirir.
Aşağıdaki kodu inceleyin:
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
var app = builder.Build();
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.Select(x => new TodoItemDTO(x)).ToListAsync());
app.MapGet("/todoitems/{id}",
async (int Id, TodoDb Db) =>
await Db.Todos.FindAsync(Id)
is Todo todo
? Results.Ok(new TodoItemDTO(todo))
: Results.NotFound());
// Remaining code removed for brevity.
Aşağıdaki GET
uç noktayı göz önünde bulundurun:
app.MapGet("/todoitems/{id}",
async (int Id, TodoDb Db) =>
await Db.Todos.FindAsync(Id)
is Todo todo
? Results.Ok(new TodoItemDTO(todo))
: Results.NotFound());
struct
Aşağıdakiler, önceki vurgulanan parametreleri değiştirmek için kullanılabilir:
struct TodoItemRequest
{
public int Id { get; set; }
public TodoDb Db { get; set; }
}
Yeniden düzenlenmiş GET
uç nokta, AsParameters özniteliğiyle öncekini struct
kullanır:
app.MapGet("/ap/todoitems/{id}",
async ([AsParameters] TodoItemRequest request) =>
await request.Db.Todos.FindAsync(request.Id)
is Todo todo
? Results.Ok(new TodoItemDTO(todo))
: Results.NotFound());
Aşağıdaki kod, uygulamadaki ek uç noktaları gösterir:
app.MapPost("/todoitems", async (TodoItemDTO Dto, TodoDb Db) =>
{
var todoItem = new Todo
{
IsComplete = Dto.IsComplete,
Name = Dto.Name
};
Db.Todos.Add(todoItem);
await Db.SaveChangesAsync();
return Results.Created($"/todoitems/{todoItem.Id}", new TodoItemDTO(todoItem));
});
app.MapPut("/todoitems/{id}", async (int Id, TodoItemDTO Dto, TodoDb Db) =>
{
var todo = await Db.Todos.FindAsync(Id);
if (todo is null) return Results.NotFound();
todo.Name = Dto.Name;
todo.IsComplete = Dto.IsComplete;
await Db.SaveChangesAsync();
return Results.NoContent();
});
app.MapDelete("/todoitems/{id}", async (int Id, TodoDb Db) =>
{
if (await Db.Todos.FindAsync(Id) is Todo todo)
{
Db.Todos.Remove(todo);
await Db.SaveChangesAsync();
return Results.Ok(new TodoItemDTO(todo));
}
return Results.NotFound();
});
Parametre listelerini yeniden düzenlemede aşağıdaki sınıflar kullanılır:
class CreateTodoItemRequest
{
public TodoItemDTO Dto { get; set; } = default!;
public TodoDb Db { get; set; } = default!;
}
class EditTodoItemRequest
{
public int Id { get; set; }
public TodoItemDTO Dto { get; set; } = default!;
public TodoDb Db { get; set; } = default!;
}
Aşağıdaki kod, ve önceki struct
ve sınıflarını kullanarak AsParameters
yeniden düzenlenmiş uç noktaları gösterir:
app.MapPost("/ap/todoitems", async ([AsParameters] CreateTodoItemRequest request) =>
{
var todoItem = new Todo
{
IsComplete = request.Dto.IsComplete,
Name = request.Dto.Name
};
request.Db.Todos.Add(todoItem);
await request.Db.SaveChangesAsync();
return Results.Created($"/todoitems/{todoItem.Id}", new TodoItemDTO(todoItem));
});
app.MapPut("/ap/todoitems/{id}", async ([AsParameters] EditTodoItemRequest request) =>
{
var todo = await request.Db.Todos.FindAsync(request.Id);
if (todo is null) return Results.NotFound();
todo.Name = request.Dto.Name;
todo.IsComplete = request.Dto.IsComplete;
await request.Db.SaveChangesAsync();
return Results.NoContent();
});
app.MapDelete("/ap/todoitems/{id}", async ([AsParameters] TodoItemRequest request) =>
{
if (await request.Db.Todos.FindAsync(request.Id) is Todo todo)
{
request.Db.Todos.Remove(todo);
await request.Db.SaveChangesAsync();
return Results.Ok(new TodoItemDTO(todo));
}
return Results.NotFound();
});
Yukarıdaki parametreleri değiştirmek için aşağıdaki record
türler kullanılabilir:
record TodoItemRequest(int Id, TodoDb Db);
record CreateTodoItemRequest(TodoItemDTO Dto, TodoDb Db);
record EditTodoItemRequest(int Id, TodoItemDTO Dto, TodoDb Db);
struct
ile AsParameters
kullanmak, tür kullanmaktan record
daha yüksek performansa sahip olabilir.
AspNetCore.Docs.Samples deposundaki tam örnek kod.
Özel Bağlama
Parametre bağlamasını özelleştirmenin iki yolu vardır:
- Yol, sorgu ve üst bilgi bağlama kaynakları için, türü için statik
TryParse
bir yöntem ekleyerek özel türleri bağlayın. - Bir türe bir
BindAsync
yöntem uygulayarak bağlama işlemini denetleyin.
TryParse
TryParse
iki API'ye sahiptir:
public static bool TryParse(string value, out T result);
public static bool TryParse(string value, IFormatProvider provider, out T result);
Aşağıdaki kod URI /map?Point=12.3,10.1
ile birlikte görüntülenirPoint: 12.3, 10.1
:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// GET /map?Point=12.3,10.1
app.MapGet("/map", (Point point) => $"Point: {point.X}, {point.Y}");
app.Run();
public class Point
{
public double X { get; set; }
public double Y { get; set; }
public static bool TryParse(string? value, IFormatProvider? provider,
out Point? point)
{
// Format is "(12.3,10.1)"
var trimmedValue = value?.TrimStart('(').TrimEnd(')');
var segments = trimmedValue?.Split(',',
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (segments?.Length == 2
&& double.TryParse(segments[0], out var x)
&& double.TryParse(segments[1], out var y))
{
point = new Point { X = x, Y = y };
return true;
}
point = null;
return false;
}
}
BindAsync
BindAsync
aşağıdaki API'lere sahiptir:
public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo parameter);
public static ValueTask<T?> BindAsync(HttpContext context);
Aşağıdaki kod URI /products?SortBy=xyz&SortDir=Desc&Page=99
ile birlikte görüntülenirSortBy:xyz, SortDirection:Desc, CurrentPage:99
:
using System.Reflection;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// GET /products?SortBy=xyz&SortDir=Desc&Page=99
app.MapGet("/products", (PagingData pageData) => $"SortBy:{pageData.SortBy}, " +
$"SortDirection:{pageData.SortDirection}, CurrentPage:{pageData.CurrentPage}");
app.Run();
public class PagingData
{
public string? SortBy { get; init; }
public SortDirection SortDirection { get; init; }
public int CurrentPage { get; init; } = 1;
public static ValueTask<PagingData?> BindAsync(HttpContext context,
ParameterInfo parameter)
{
const string sortByKey = "sortBy";
const string sortDirectionKey = "sortDir";
const string currentPageKey = "page";
Enum.TryParse<SortDirection>(context.Request.Query[sortDirectionKey],
ignoreCase: true, out var sortDirection);
int.TryParse(context.Request.Query[currentPageKey], out var page);
page = page == 0 ? 1 : page;
var result = new PagingData
{
SortBy = context.Request.Query[sortByKey],
SortDirection = sortDirection,
CurrentPage = page
};
return ValueTask.FromResult<PagingData?>(result);
}
}
public enum SortDirection
{
Default,
Asc,
Desc
}
Bağlama hataları
Bağlama başarısız olduğunda, çerçeve hata ayıklama iletisini günlüğe kaydeder ve hata moduna bağlı olarak istemciye çeşitli durum kodları döndürür.
Hata modu | Null Atanabilir Parametre Türü | Bağlama Kaynağı | Durum kodu |
---|---|---|---|
{ParameterType}.TryParse Döndürür false |
evet | route/query/header | 400 |
{ParameterType}.BindAsync Döndürür null |
evet | özel | 400 |
{ParameterType}.BindAsync Atar |
önemli değil | özel | 500 |
JSON gövdesinin seri durumdan çıkarılamaması | önemli değil | gövde | 400 |
Yanlış içerik türü (değil application/json ) |
önemli değil | gövde | Kategori 415 |
Bağlama Önceliği
Bir parametreden bağlama kaynağını belirleme kuralları:
- Parametrede (From* öznitelikleri) aşağıdaki sırayla tanımlanan açık öznitelik:
- Yol değerleri:
[FromRoute]
- Sorgu dizesi:
[FromQuery]
- Üstbilgi:
[FromHeader]
- Beden:
[FromBody]
- Hizmet:
[FromServices]
- Parametre değerleri:
[AsParameters]
- Yol değerleri:
- Özel türler
HttpContext
HttpRequest
(HttpContext.Request
)HttpResponse
(HttpContext.Response
)ClaimsPrincipal
(HttpContext.User
)CancellationToken
(HttpContext.RequestAborted
)IFormFileCollection
(HttpContext.Request.Form.Files
)IFormFile
(HttpContext.Request.Form.Files[paramName]
)Stream
(HttpContext.Request.Body
)PipeReader
(HttpContext.Request.BodyReader
)
- Parametre türü geçerli bir statik
BindAsync
yönteme sahiptir. - Parametre türü bir dizedir veya geçerli bir statik
TryParse
yöntemi vardır.- Rota şablonunda parametre adı varsa.
id
içindeapp.Map("/todo/{id}", (int id) => {});
, yolundan bağlıdır. - Sorgu dizesinden bağlanır.
- Rota şablonunda parametre adı varsa.
- Parametre türü bağımlılık ekleme tarafından sağlanan bir hizmetse, kaynak olarak bu hizmeti kullanır.
- parametresi gövdedendir.
Gövde bağlaması için JSON seri durumdan çıkarma seçeneklerini yapılandırma
Gövde bağlama kaynağı seri durumdan çıkarma için kullanır System.Text.Json . Bu varsayılanı değiştirmek mümkün değildir, ancak JSON serileştirme ve seri durumdan çıkarma seçenekleri yapılandırılabilir.
JSON seri durumdan çıkarma seçeneklerini genel olarak yapılandırma
Bir uygulama için genel olarak geçerli olan seçenekler çağrılarak ConfigureHttpJsonOptionsyapılandırılabilir. Aşağıdaki örnek, ortak alanları ve JSON çıkışını biçimlendirmektedir.
var builder = WebApplication.CreateBuilder(args);
builder.Services.ConfigureHttpJsonOptions(options => {
options.SerializerOptions.WriteIndented = true;
options.SerializerOptions.IncludeFields = true;
});
var app = builder.Build();
app.MapPost("/", (Todo todo) => {
if (todo is not null) {
todo.Name = todo.NameField;
}
return todo;
});
app.Run();
class Todo {
public string? Name { get; set; }
public string? NameField;
public bool IsComplete { get; set; }
}
// If the request body contains the following JSON:
//
// {"nameField":"Walk dog", "isComplete":false}
//
// The endpoint returns the following JSON:
//
// {
// "name":"Walk dog",
// "nameField":"Walk dog",
// "isComplete":false
// }
Örnek kod hem serileştirmeyi hem de seri durumdan çıkarmayı yapılandırdığından çıktı JSON'unu okuyabilir NameField
ve içerebilir NameField
.
Uç nokta için JSON seri durumdan çıkarma seçeneklerini yapılandırma
ReadFromJsonAsync bir nesneyi kabul JsonSerializerOptions eden aşırı yüklemelere sahiptir. Aşağıdaki örnek, ortak alanları ve JSON çıkışını biçimlendirmektedir.
using System.Text.Json;
var app = WebApplication.Create();
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web) {
IncludeFields = true,
WriteIndented = true
};
app.MapPost("/", async (HttpContext context) => {
if (context.Request.HasJsonContentType()) {
var todo = await context.Request.ReadFromJsonAsync<Todo>(options);
if (todo is not null) {
todo.Name = todo.NameField;
}
return Results.Ok(todo);
}
else {
return Results.BadRequest();
}
});
app.Run();
class Todo
{
public string? Name { get; set; }
public string? NameField;
public bool IsComplete { get; set; }
}
// If the request body contains the following JSON:
//
// {"nameField":"Walk dog", "isComplete":false}
//
// The endpoint returns the following JSON:
//
// {
// "name":"Walk dog",
// "isComplete":false
// }
Yukarıdaki kod özelleştirilmiş seçenekleri yalnızca seri durumdan çıkarma işlemine uyguladığından çıktı JSON dışlar NameField
.
İstek gövdesini okuma
bir veya HttpRequest parametresini kullanarak HttpContext istek gövdesini doğrudan okuyun:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/uploadstream", async (IConfiguration config, HttpRequest request) =>
{
var filePath = Path.Combine(config["StoredFilesPath"], Path.GetRandomFileName());
await using var writeStream = File.Create(filePath);
await request.BodyReader.CopyToAsync(writeStream);
});
app.Run();
Yukarıdaki kod:
- kullanarak HttpRequest.BodyReaderistek gövdesine erişir.
- İstek gövdesini yerel bir dosyaya kopyalar.
ASP.NET Core