This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Sunday, 09 November 2025
UUDATE (2025- 11- 10): मेरे वास्तविक कोड से और अधिक व्यावहारिक उदाहरण जोड़े वास्तव में दुनिया के पैटर्न, व्यापार - समाप्त, मिल गया, और जटिल राज्य प्रबंधन के प्रति सरल से विकास शामिल है. विस्तृत मैं मेमोरी मॉडलों, दृश्य Babag का उपयोग (सही और गलत), अनुक्रिया Cacs/ tuts, और उत्पादन से सीखा.
HTTP प्रसिद्ध है. आपका एएसटीम नहीं है. आपका एएसएसएसए नहीं है. उपयोक्ता साइन इनबॉक्स में से कुछ वस्तुओं को जोड़ता है, जो कि पृष्ठ, कल, और आपको याद दिलाने की आशा करते हैं.
यह पोस्ट कैटलॉग विकल्प दिखाता है, ठोस, नक़ली उदाहरण सभी तीन ढेरों में नक़ली उदाहरण दिखाता है, और आपको मार्गदर्शन देता है ताकि आप काम के लिए सही औज़ार चुन सकें.
नोट: यह मेरे परीक्षण का हिस्सा है एआई (के रूप में ड्राफ्टिंग) + मेरी खुद का संपादन. एक ही आवाज, एक ही स्फीतिवाद; सिर्फ तेजी से उंगलियों के साथ.
flowchart LR
subgraph Client
Q[Query String]
R[Route Values]
H[Headers]
F[Form/Hidden Fields]
CK[Cookies]
LS[Local/Session Storage]
end
subgraph Server
I[HttpContext.Items<br/>\nper request only]
TD[TempData<br/>\none redirect]
SS[Session]
MC[IMemoryCache]
DC[IDistributedCache]
AU[Auth Cookie / Claims]
JT[JWT / Bearer]
DB[Database / Durable Store]
BUS[Outbox / Queue]
end
Q --> Model[Model Binding]
R --> Model
F --> Model
H --> Model
CK --> App[Your Code]
Model --> App
App -->|Set| CK
App -->|Set| TD
App -->|Set| SS
App -->|Set| MC
App -->|Set| DC
App -->|Issue| AU
App -->|Issue| JT
App -->|Persist| DB
sequenceDiagram participant M as Middleware participant E as Endpoint/Controller M->>M: Compute TenantId M->>E: HttpContext.Items["TenantId"] = 42 E->>E: Read Items["TenantId"]
मध्य- एलबम उदाहरण (सभी ढेर):
app.Use(async (context, next) =>
{
var tenantId = context.Request.Headers["X-TenantId"].FirstOrDefault() ?? "public";
context.Items["TenantId"] = tenantId;
await next(context);
});
app.MapGet("/whoami", (HttpContext ctx) => new { Tenant = ctx.Items["TenantId"] });
public IActionResult WhoAmI() => Json(new { Tenant = HttpContext.Items["TenantId"] });
public IActionResult OnGet() => new JsonResult(new { Tenant = HttpContext.Items["TenantId"] });
उदाहरण
app.MapGet("/orders/{id:int}", (int id, int? page) => Results.Ok(new { id, page }));
// GET /orders/5?page=2
[HttpGet("/orders/{id:int}")]
public IActionResult Details(int id, int? page)
=> View(new { id, page });
public IActionResult OnGet(int id, int? page)
=> Page();
लिंक बनाया जा रहा है जो संरक्षित स्थिति को बनाए रखता है:
// Razor Pages
<a asp-page="/Orders/Details" asp-route-id="@Model.Id" asp-route-page="@Model.Page">Next</a>
// MVC
@Html.ActionLink("Next", "Details", "Orders", new { id = Model.Id, page = Model.Page }, null)
app.Use(async (ctx, next) =>
{
var correlationId = ctx.Request.Headers["X-Correlation-Id"].FirstOrDefault()
?? Guid.NewGuid().ToString("n");
ctx.Response.Headers["X-Correlation-Id"] = correlationId;
await next(ctx);
});
आईएसडी- एएस- पैटर्न: (p)
flowchart TD
C[Client POST /pay\nIdempotency-Key:k] --> S{Seen k?}
S -- No --> E[Execute charge]
E --> P[Persist result by k]
P --> R[Return 200 + result]
S -- Yes --> L[Load result by k]
L --> R
एमवीसी/आरएम्ब्टर पृष्ठ में पजीजी पैटर्न:
sequenceDiagram participant U as User participant P as POST Action participant R as Redirect participant G as GET Action U->>P: POST form P-->>R: 302 Redirect U->>G: GET redirected G-->>U: Final page (no resubmits)
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Save(SettingsModel model)
{
// validate & persist
return RedirectToAction(nameof(Summary), new { tab = model.SelectedTab });
}
public IActionResult OnPost(SettingsModel model)
{
return RedirectToPage("/Settings/Summary", new { tab = model.SelectedTab });
}
न्यूनतम एपीआई उदाहरण:
app.MapPost("/prefs/theme/{value}", (HttpContext ctx, string value) =>
{
ctx.Response.Cookies.Append("theme", value, new CookieOptions
{
HttpOnly = false,
Secure = true,
SameSite = SameSiteMode.Lax,
Expires = DateTimeOffset.UtcNow.AddYears(1)
});
return Results.Ok();
});
app.MapGet("/prefs/theme", (HttpContext ctx)
=> Results.Text(ctx.Request.Cookies["theme"] ?? "system"));
MVC/RUror पृष्ठ उपयोग एक जैसे है HttpContext.
अखंडता के लिए, एनईसी का उपयोग करें. एनईसी डाटा सुरक्षा तंत्र को सुरक्षित रखने के लिए आप अपने आप को कुकीज़ में डाल देते हैं.
सेटअप ( प्रोग्राम.c):
builder.Services.AddControllersWithViews().AddSessionStateTempDataProvider(); // optional
builder.Services.AddSession();
var app = builder.Build();
app.UseSession();
नियंत्रण में:
TempData["StatusMessage"] = "Saved!";
return RedirectToAction("Index");
राइसर पेज हैंडलर में:
TempData["StatusMessage"] = "Saved!";
return RedirectToPage("/Index");
दृश्य/पेज में:
@if (TempData["StatusMessage"] is string msg) {
<div class="alert alert-success">@msg</div>
}
flowchart LR A[POST /save] -->|TempData set| B[302 Redirect] B --> C[GET /index] C -->|TempData read consumed| D[Render message]
कॉन्फ़िगर:
builder.Services.AddDistributedMemoryCache(); // or AddStackExchangeRedisCache
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(20);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
var app = builder.Build();
app.UseSession();
सत्र इस्तेमाल किया जा रहा है (कोई ढेर नहीं):
app.MapPost("/cart/add/{id:int}", (HttpContext ctx, int id) =>
{
var key = "cart";
var bytes = ctx.Session.Get(key);
var list = bytes is null ? new List<int>() : System.Text.Json.JsonSerializer.Deserialize<List<int>>(bytes)!;
list.Add(id);
ctx.Session.Set(key, System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(list));
return Results.Ok(list);
});
सत्र सहायक:
public static class SessionExtensions
{
public static void Set<T>(this ISession session, string key, T value)
=> session.SetString(key, System.Text.Json.JsonSerializer.Serialize(value));
public static T? Get<T>(this ISession session, string key)
=> session.TryGetValue(key, out var data)
? System.Text.Json.JsonSerializer.Deserialize<T>(data)
: default;
}
मैं एक बार एक विशाल यूके सरकार पर काम किया है यह परियोजना जहाँ सत्र राज्य का दुरुपयोग (बहुत से अन्य निर्माणीय पापों के बारे में) एक प्रदर्शन का उपयोग कर रहे हैं . टीम का सामान लूट लिया था सभी सत्र में: उपयोक्ता वरीयता, बहु- चरण फ़ॉर्म डाटा, खोज परिणाम, अस्थायी गणना, यहाँ तक कि कैश देखना चाहिए कि एक उचित कैश या डाटाबेस में किया गया है.
समस्या: सत्र स्थिति वेब में भंडारित किया गया था (SueT सत्र सत्र स्थिति में.config, यह पहले से ही था.config, यह हर निवेदन भारी सत्र वस्तुओं को चलाने के लिए था. जैसा कि लोड किया गया था, सत्र स्तर में अधिकतम किया गया है प्रति उपयोगकर्ता के दस उपयोगकर्ता के साथ. हजार गैरक्शन उपयोगकर्ता के साथ, सर्वर स्मरण के बाहर भाग गया.
कठिन समाधान: हम Warttttttttttttttttta की सुविधा के लिए हवाई जहाज पर उड़ान चलाने के लिए उनके सुपरddy पर जाँच चलाने के लिए, समय पर, यूरोप के सबसे शक्तिशाली विंडोज़ मशीन. यह एक जानवर था: येलेनियम प्रक्रियाकों के दर्जनों दर्जनों लोग, रैम के सैकड़ों टाइटाबाइट्स. विचार यह साबित करने के लिए था कि पर्याप्त हार्डवेयर के साथ व्यवस्था माँगों को पूरा कर सकती थी.
परिणाम: सुपरdome पर भी, हम जरूरी गैर मौज़ूदा उपयोक्ता लक्ष्य को नहीं मारा. सत्र की संरचना बुनियादी रूप से टूटी थी. खड़े स्केलिंग बुरा डिजाइन नहीं बचा सकता था. सत्र सीरियलीकरण / उच्च ऊंचाई, विशाल सत्र वस्तुओं से दबाव के साथ, तंत्र बस कुछ भी नहीं किया जा सकता था - किसी भी उचित लागत पर नहीं.
सुरक्षा सपना: प्रदर्शन मुद्दों से भी बदतर, हमने एक coding त्रुटि पायी जिससे सत्र स्थिति का कारण हुआ उपयोक्ता के बीच रिसाव. उपयोक्ता का सत्र डाटा कभी उपयोक्ता B के सत्र में प्रकट होगा. यह सिर्फ शर्मिंदा नहीं था - यह भयानक था. उपयोक्ता मौजूद थे. एनएचएस लाठी मरीज़ रिकॉर्ड और इंफेक्शन सिस्टम पर पहुंचना। हमने अनजाने में एक डेटा सुरक्षा भंग व्यवस्था बनाई थी जो विभिन्न स्वास्थ्य पेशेवरों के सत्रों के दौरान संवेदनशील चिकित्सा जानकारी का विरोध कर सकता है।
क्या हुआ होगा:
सबक: सत्र स्थिति खड़ा नहीं है और मुश्किल से तराजू आड़ा (जो कि स्टिकी सत्र या वितरित किए गए भंडारों के साथ भी, आप अभी भी हर निवेदन पर लगाए जा रहे हैं)। सुपर-सेटो का प्रयोग साबित किया गया कि निर्माण समस्याओं में हार्डवेयर फेंक देना महँगे और अक्सर बेकार है।
लेकिन अधिक महत्वपूर्ण: सत्र स्थिति बग सुरक्षा viobans हो जाते हैं. थ्रेड सुरक्षित मुद्दों, जाति की स्थिति, गलत सत्र आईडी नियंत्रण - इन सिर्फ प्रदर्शन समस्याओं का कारण नहीं है, वे उपभोक्ताओं के बीच संवेदनशील डाटा पैदा कर सकते हैं. एक स्वास्थ्य के संदर्भ में (या बैंकिंग, या किसी भी विनियमित उद्योग) यह एक प्रस्ताव और संभावना आपराधिक अपराधी है.
आधुनिक सलाह:
मैं ओसीश:
builder.Services.AddMemoryCache();
app.MapGet("/rates", (IMemoryCache cache) =>
{
var key = "fx:usd:eur";
if (!cache.TryGetValue(key, out decimal rate))
{
rate = 0.92m; // pretend fetch
cache.Set(key, rate, TimeSpan.FromMinutes(5));
}
return Results.Ok(rate);
});
आईडीदीश (उदा., रेड):
builder.Services.AddStackExchangeRedisCache(o => o.Configuration = "localhost:6379");
app.MapGet("/feature/{name}", async (IDistributedCache cache, string name) =>
{
var val = await cache.GetStringAsync($"feat:{name}");
return Results.Text(val ?? "off");
});
कैश- एन्टी- स्पैम पैटर्न चेतावनी: यदि यह ठोस या आधिकारिक हो, इसे डाटाबेस में जमा करें तथा उसे वैकल्पिक रूप से कैश करें.
कैश अगल (सबसे आम):
पढ़ने के लिए फ़ाइल सिस्टम: कैश बार्वर पर लोड होता है.
लिखने के लिए: कैश तथा समर्थन के लिए लिखते हैं.
लिखें: कैश को लिखें, अतुल्यकालिक रूप से स्टोर करने के लिए फ्लश करें (पारिंक: नुकसान/in).
ताज़ा सिर: ठंडे यादों से बचने के लिए पहले गर्म कुंजियाँ ताज़ा करें.
जब आपको बहुत से संबंधित प्रविष्टियों को गलत करना होगा:
संस्करणित प्रीफिक्सेस (प्रोसेफिक्स): छोटी कुंजी में वैश्विक संस्करण जांचें तथा इसके साथ कुंजी को लिखना.
// version key: "v:products"; keys like $"{version}:product:{id}"
var version = await cache.GetStringAsync("v:products") ?? "1";
var key = $"{version}:product:{id}";
सभी उत्पादों को रिप करने के लिए: विbss: prods (स्वाभाविक रूप से पुरानी उपसर्ग गुम हो जाएगा).
टैग प्रति समूह नियत करें (Rdds): प्रति टैग प्रति टैग के कुंजी का सेट रखें; अवैध, सदस्यों को लाने और मिटाने पर.
// using StackExchange.Redis directly for sets + efficient deletes
var mux = await ConnectionMultiplexer.ConnectAsync("localhost:6379");
var db = mux.GetDatabase();
var tag = "tag:category:42";
var key = $"prod:{prodId}";
await db.StringSetAsync(key, serialized, expiry: TimeSpan.FromMinutes(30));
await db.SetAddAsync(tag, key); // remember membership
// later, invalidate the whole tag
var members = await db.SetMembersAsync(tag);
if (members.Length > 0)
{
var keys = Array.ConvertAll(members, m => (RedisKey)m);
await db.KeyDeleteAsync(keys);
}
await db.KeyDeleteAsync(tag);
Phr/ sler अवैध: एक "अवैध:keye" संदेश प्रकाशित करें; प्रत्येक नोड अपने स्थानीय मे से कुंजी हटा देता है।
पैटर्न के साथ स्कैन: STAN/CATTT को गर्म पथों में से दूर किया जाना चाहिए, छोटे कुंजी स्पेस पर नौकर के लिए ठीक है.
T GetOrAdd<T>(IMemoryCache cache, string key, Func<ICacheEntry, T> factory)
=> cache.GetOrCreate(key, e =>
{
e.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10);
e.SlidingExpiration = TimeSpan.FromMinutes(2);
e.Priority = CacheItemPriority.Normal;
e.Size = 1;
return factory(e);
});
static async Task<T?> GetOrSetJsonAsync<T>(IDistributedCache cache, string key, Func<Task<T>> factory, TimeSpan ttl)
{
var json = await cache.GetStringAsync(key);
if (json is not null)
return System.Text.Json.JsonSerializer.Deserialize<T>(json);
var value = await factory();
var opts = new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = ttl };
await cache.SetStringAsync(key,
System.Text.Json.JsonSerializer.Serialize(value),
opts);
return value;
}
यहाँ है कि मैं वास्तव में अपने ब्लॉग मंच में Racrac का उपयोग कैसे करते हैं, परीक्षण और त्रुटि के माध्यम से विकास. मैं आप तीन असली पैटर्न दिखाएगा जटिल के लिए सरल तरीके.
यह मेरा पहला कवरिंग कार्यान्वयन था. ब्लॉग श्रेणियाँ अक्सर बदलता नहीं है, इसलिए 30 मिनट के लिए उन्हें कैश करें:
// From BaseController.cs
private const string CacheKey = "Categories";
private async Task<List<string>> GetCategories()
{
baseControllerService.MemoryCache.TryGetValue(CacheKey, out var value);
if (value is List<string> categories) return categories;
logger.LogInformation("Fetching categories from BlogService");
categories = (await BlogViewService.GetCategories(true)).OrderBy(x => x).ToList();
baseControllerService.MemoryCache.Set(CacheKey, categories, TimeSpan.FromMinutes(30));
return categories;
}
क्यों यह काम करता है:
मिल गया मैं मारा: शुरू में मैं एक स्ट्रिंग कुंजी "चार" का उपयोग किया। ठीक काम करता हूँ जब तक आप कई नियंत्रण है और एक ही बार फिर एक ही कुंजी को फिर से प्रयोग करता है। अब मैं स्थिर या गंभीर प्रकार की कुंजियों का उपयोग करता हूँ (यह पहले भाग है झगड़े से दूर होने से बचने के लिए)।
यह कैश्स अनुवाद स्थिति प्रति उपयोक्ता है. यह अधिक जटिल है क्योंकि यह सीमाओं की जरूरत है और जब तक उपयोक्ता सक्रिय है जीवित रहना चाहिए:
// From TranslateCacheService.cs - tracks translation tasks per user
public void AddTask(string userId, TranslateTask task)
{
CachedTasks CachedTasks() => new()
{
Tasks = new List<TranslateTask> { task },
AbsoluteExpiration = DateTime.Now.AddHours(6)
};
if (memoryCache.TryGetValue(userId, out CachedTasks? tasks))
{
tasks ??= CachedTasks();
var currentTasks = tasks.Tasks;
// Keep only the 5 most recent tasks
currentTasks = currentTasks.OrderByDescending(x => x.StartTime).ToList();
if (currentTasks.Count >= 5)
{
var lastTask = currentTasks.Last();
currentTasks.Remove(lastTask);
}
currentTasks.Add(task);
currentTasks = currentTasks.OrderByDescending(x => x.StartTime).ToList();
tasks.Tasks = currentTasks;
memoryCache.Set(userId, tasks, new MemoryCacheEntryOptions
{
AbsoluteExpiration = tasks.AbsoluteExpiration,
SlidingExpiration = TimeSpan.FromHours(1) // Extends on access
});
}
else
{
var absoluteExpiration = DateTime.Now.AddHours(6);
var cachedTasks = CachedTasks();
memoryCache.Set(userId, cachedTasks, new MemoryCacheEntryOptions
{
AbsoluteExpiration = absoluteExpiration,
SlidingExpiration = TimeSpan.FromHours(1)
});
}
}
क्यों यह भिन्न है:
मैंने बनाया है: शुरू में मैं कार्यों की संख्या को सीमित नहीं कर पाया. एक शक्ति उपयोगकर्ता 50+ संस्करण उत्पन्न होता है और मेरे पास एक स्मृति लीक था. अब मैं अधिकतम 5 प्रति उपयोक्ता बनाए रखें.
ट्रायल- ऑफः यह लाखों उपयोक्ताओं के लिए स्केल नहीं होगा. यदि यह एक समस्या बन जाता है, तो मैं आईडीिस्टीसीक (Rdeds) के लिए ले जाया करता हूँ या उपयोक्ता Id + प्रारंभ समय के साथ डाटाबेस में स्टोर करता हूँ.
इस कैश से से सर्लॉगबिल का उपयोग करने में सक्षम और ट्रैक कैश सक्षम होता है:
// From UmamiDataSortService.cs - caches Umami analytics metrics
public async Task<List<MetricsResponseModels>?> GetMetrics(DateTime startAt, DateTime endAt, string prefix = "")
{
using var activity = Log.Logger.StartActivity("GetMetricsWithPrefix");
try
{
var cacheKey = $"Metrics_{startAt:yyyyMMdd}_{endAt:yyyyMMdd}_{prefix}";
if (cache.TryGetValue(cacheKey, out List<MetricsResponseModels>? metrics))
{
activity?.AddProperty("CacheHit", true);
return metrics;
}
activity?.AddProperty("CacheHit", false);
var metricsRequest = new MetricsRequest
{
StartAtDate = startAt,
EndAtDate = endAt,
Type = MetricType.url,
Limit = 500
};
var metricRequest = await dataService.GetMetrics(metricsRequest);
if (metricRequest.Status != HttpStatusCode.OK) return null;
var filteredMetrics = metricRequest.Data
.Where(x => x.x.StartsWith(prefix))
.ToList();
cache.Set(cacheKey, filteredMetrics, TimeSpan.FromHours(1));
activity?.AddProperty("MetricsCount", filteredMetrics?.Count() ?? 0);
activity?.Complete();
return filteredMetrics;
}
catch (Exception e)
{
activity?.Complete(LogEventLevel.Error, e);
return null;
}
}
क्या इस उत्पादन को तैयार करता है:
yyyyMMdd कुंजी में इतना भिन्न समय फ़ॉर्मेट कैश साझा करेंएवोल्यूशन: शुरू में मैं 10 मिनट के लिए कैश. लेकिन उममी मेरे लिए भारी हैं लाने के लिए और ज्यादा नहीं बदल रहे हैं, तो 1 घंटे ठीक है. मैंने यह खोज निकाला डेटा देखने और अत्यधिक कैश देखने के द्वारा.
क्रिया में मॉनीटर किया जा रहा है: Seqt में (Magrider), मैं प्रश्न कर सकता हूँ:
ActivityName = "GetMetricsWithPrefix" and CacheHit = false
यह मुझे बताता है मेरे कैश याद दर. यदि यह उच्च है, तो मैं TTL या कुंजी रणनीति समायोजित करता हूँ.
उर्फ पैटर्न केस का प्रयोग करें (c) |---------|----------|------------|--------------|---------------| | कोटियाँ INRWACACKS, कम से कम 30 मिस्ट्री की आवश्यकता नहीं (छोटे से छोटा) मूल लॉगिंग की जरूरत नहीं है | अनुवाद कार्य PRECT पर-user, 6h + 1h स्लाइडिंग मैनुअल (5 वस्तु अधिकतम) Advious (d!) | मेट्रिक्स STEGRESENTHANTHIENTHARENTHARECHANTHANTH (समय- set- seted)
कुंजी सबकः
जब मैं McrryCCacs का उपयोग नहीं करते हैं:
कुकी अनुमोदन सेटअप करें:
builder.Services.AddAuthentication("Cookies")
.AddCookie("Cookies", o =>
{
o.LoginPath = "/login";
o.Cookie.SecurePolicy = CookieSecurePolicy.Always;
o.SlidingExpiration = true;
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
दावाों के साथ हस्ताक्षर करें (एमवीसी/मिन):
app.MapPost("/login", async (HttpContext ctx) =>
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, "123"),
new Claim(ClaimTypes.Name, "Alice"),
new Claim(ClaimTypes.Role, "Admin")
};
var identity = new ClaimsIdentity(claims, "Cookies");
await ctx.SignInAsync("Cookies", new ClaimsPrincipal(identity));
return Results.Redirect("/");
});
पढ़ने का दावा (कोई ढेर नहीं):
[Authorize]
app.MapGet("/me", (ClaimsPrincipal user)
=> Results.Ok(new { user.Identity!.Name, Roles = user.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value) }));
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", o =>
{
o.Authority = "https://demo.identityserver.io"; // example
o.Audience = "api";
o.RequireHttpsMetadata = true;
});
प्रयोक्ता:
[Authorize(AuthenticationSchemes = "Bearer")]
app.MapGet("/secure", () => "ok");
मेरिफ़ॉयर ने दृश्य दिखाया:
sequenceDiagram participant C as Client participant STS as Token Service participant API as API C->>STS: Authenticate (username/password) STS-->>C: JWT (signed) C->>API: GET /secure (Authorization: Bearer <jwt>) API->>API: Validate signature, expiry, audience API-->>C: 200
ईएफ कोर रंग:
builder.Services.AddDbContext<AppDb>(o => o.UseSqlServer(cs));
app.MapPost("/cart/items", async (AppDb db, AddItem cmd) =>
{
var cart = await db.Carts.FindAsync(cmd.CartId) ?? new Cart(cmd.CartId);
cart.Add(cmd.ProductId, cmd.Qty);
await db.SaveChangesAsync();
return Results.Created($"/cart/{cart.Id}", cart);
});
builder.Services.AddResponseCaching();
var app = builder.Build();
app.UseResponseCaching();
app.MapGet("/products", (HttpContext ctx) =>
{
ctx.Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue { Public = true, MaxAge = TimeSpan.FromSeconds(30) };
return Results.Ok(new[] { new { Id = 1, Name = "Widget" } });
}).CacheOutput();
टैग उदाहरण:
app.MapGet("/resource", (HttpContext ctx) =>
{
var version = "W/\"abc123\""; // compute based on data hash
ctx.Response.Headers.ETag = version;
if (ctx.Request.Headers.IfNoneMatch == version)
return Results.StatusCode(StatusCodes.Status304NotModified);
return Results.Text("payload");
});
मैं दोनों प्रतिक्रियाओं का उपयोग भिन्न कारणों से मेरे ब्लॉग पर एक साथ करते हैं. यहाँ है क्यों आप दोनों को चाहते हैं और वे कैसे अलग हैं.
एनईएस.ENTC कोर है दो समान दिखाई देने वाली कैश सिस्टम्स:
Cache-Control, Vary) खंगालक्स तथा सीडीएन को बताता है कि कैश कैसे करें
ईवे एक दूसरे को मेल खाते हैं. अनुक्रिया चांसिया क्लाएंट/CDNs संभालता है; आउटपुटCT सर्वर को साइड कैशिंग संभालता है.
// From BlogController.cs
[Route("{slug}")]
[HttpGet]
[ResponseCache(Duration = 300, VaryByHeader = "hx-request",
VaryByQueryKeys = new[] { nameof(slug), nameof(language) },
Location = ResponseCacheLocation.Any)]
[OutputCache(Duration = 3600, VaryByHeaderNames = new[] { "hx-request" },
VaryByQueryKeys = new[] { nameof(slug), nameof(language) })]
public async Task<IActionResult> Show(string slug, string language = "en")
{
var post = await blogViewService.GetPost(slug, language);
if (post == null) return NotFound();
// ... populate user info, comments, etc ...
if (Request.IsHtmx()) return PartialView("_PostPartial", post);
return View("Post", post);
}
जब कोई अनुरोध करता है तो क्या होता है /blog/my-post:
आउटपुटCT जांच प्रथम: क्या मेरे पास इसके लिए कैशेड प्रतिक्रिया है my-post + en भाषा?
अनुक्रिया (_C): आउटपुटCCK के बाद प्रतिक्रिया उत्पन्न होती है, प्रतिक्रिया प्रतिक्रिया बताता है:
Cache-Control: public, max-age=300
Vary: hx-request
ब्राउज़र कलोनComment: 300 सेकंड के लिए ब्राउज़र कैश जवाब (5 मिनट). उपक निवेदन उसी उपयोक्ता से भी रद्द नहीं होता.
सीडीएन कैशिंग (यदि बादल के उपयोग से पता लगाएँ/ तेज: सीडीएन कैश 5 मिनट के लिए. उपयोक्ताओं ने दुनिया भर में सीडीN को मारा, मेरे सर्वर नहीं.
[ResponseCache(Duration = 300)] // 5 minutes client/CDN cache
[OutputCache(Duration = 3600)] // 1 hour server cache
कारण:
एवोल्यूशन: शुरू में तो मैं 5 मिनट में दोनों था. लेकिन इसका मतलब था कि मेरा सर्वर हर 5 मिनट फिर से दे रहा था, भले ही सामग्री में परिवर्तन न हों. अब:
VaryByHeader = "hx-request" // ResponseCache
VaryByHeaderNames = new[] { "hx-request" } // OutputCache
ऐसा क्यों: HMAX निवेदन शामिल हैं hx-request: true शीर्षक. मैं अलग जवाब देता हूँ:
के बगैर VaryBy, कैश गलत फ़ॉर्मेट को लौटा देगा. VaryBy, मैं हर पृष्ठ के दो संस्करण कैश.
उदाहरण:
User requests /blog/my-post → Cache key: "blog/my-post:en:hx=false" → Full HTML cached
HTMX requests /blog/my-post → Cache key: "blog/my-post:en:hx=true" → Partial HTML cached
[ResponseCache(VaryByQueryKeys = new[] { nameof(slug), nameof(language) })]
[OutputCache(VaryByQueryKeys = new[] { nameof(slug), nameof(language) })]
इस के बिना समस्या: /blog/my-post?language=fr अंग्रेज़ी कैश्ड संस्करण सेवा करते ।
वीआई के द्वाराQuery @ info: whatsthis कैश प्रविष्टि अलग करें:
/blog/my-post?language=en कैश कुंजी में "en" शामिल हैं/blog/my-post?language=fr कैश कुंजी में "fr" शामिल हैंब्लॉग सूची से वास्तविक उपयोग:
[Route("blog")]
[ResponseCache(Duration = 300, VaryByHeader = "hx-request",
VaryByQueryKeys = new[] { "page", "pageSize", "startDate", "endDate", "language", "orderBy", "orderDir" })]
[OutputCache(Duration = 3600, VaryByHeaderNames = new[] { "hx-request" },
VaryByQueryKeys = new[] { "page", "pageSize", "startDate", "endDate", "language", "orderBy", "orderDir" })]
public async Task<IActionResult> Index(int page = 1, int pageSize = 20, /* ... */)
कैश विस्फोट चेतावनी: पैरामीटर = अलग कैश प्रविष्टि का प्रत्येक अद्वितीय संयोजन:
page=1&pageSize=20&language=en नीचे एक प्रविष्टिpage=2&pageSize=20&language=en ✔ एक और प्रविष्टिpage=1&pageSize=10&language=en ✔ एक और प्रविष्टिमाइिगेशन:
अनुक्रिया- केस बक्स के बाहर काम करता है, लेकिन के लिए आउटपुटCTCKt आपको सेटअप की आवश्यकता है:
// Program.cs
builder.Services.AddOutputCache(options =>
{
options.MaximumBodySize = 64 * 1024 * 1024; // 64 MB max response size
options.SizeLimit = 100 * 1024 * 1024; // 100 MB total cache size
});
var app = builder.Build();
app.UseOutputCache(); // Must be in middleware pipeline
आउटपुटCTCT के लिए छोड़ दिया गया है:
Set-Cookie शीर्षिकाCache-Control: no-storeउदाहरण जहाँ मैं इसका उपयोग नहीं करता:
// Comment submission - authenticated, POST, and per-user
[HttpPost]
[Authorize]
public async Task<IActionResult> AddComment(CommentModel model)
{
// No caching attributes - this is user-specific and changes state
}
मैं Piences का उपयोग ट्रैक करने के लिए (मेरे आईएओ के माध्यम से) का उपयोग करें:
// Pseudo-code for metrics
cache_hits_total{cache="output"} 45230
cache_misses_total{cache="output"} 892
मेरा कैश हिट दर: ~98% ब्लॉग पोस्टों के लिए (सबसे लोकप्रिय पोस्टों को बार - बार मारता है).
प्रभाव:
कभी कभी मैं डिबगिंग कर रहा हूँ और हर बार ताजा जवाब की जरूरत है:
// During development, comment out caching
// [ResponseCache(Duration = 300, ...)]
// [OutputCache(Duration = 3600, ...)]
public async Task<IActionResult> Show(string slug, string language = "en")
या एनवायरनमेंट- विशिष्ट विन्यास इस्तेमाल करें:
#if DEBUG
// No caching in development
#else
[ResponseCache(Duration = 300, ...)]
[OutputCache(Duration = 3600, ...)]
#endif
बेहतर तरीका: कॉन्फ़िगरेशन इस्तेमाल करें:
[ResponseCache(Duration = responseCacheDuration, ...)]
कहाँ responseCacheDuration विकास में 0 है, 300 उत्पादन में.
प्रोविडेंस:
कनेक्शनः
जब मैं कैशिंग छोड़ दूँ:
मेरा फैसला: ज़्यादातर लोगों के लिए एक ब्लॉग के लिए और उच्च पठन अनुपात के लिए, दो सीलिंग एक बहुत ही जीतने वाला है. मैं अधिकारी पैनलों में इस का उपयोग जल्दी से डाटा बदलने के साथ नहीं करेगा.
ये तीन अक्सर उलझन में हैं. यहाँ वे कैसे अलग हैं और मैं उत्पादन में वास्तव में क्या इस्तेमाल करते हैं.
// ViewData: string-keyed dictionary
ViewData["Title"] = "Blog";
ViewData["Categories"] = new List<string> { "ASP.NET", "C#" };
// ViewBag: dynamic wrapper around ViewData
ViewBag.Title = "Blog";
ViewBag.Categories = new List<string> { "ASP.NET", "C#" };
// TempData: survives one redirect (backed by session or cookie)
TempData["Message"] = "Post saved!";
return RedirectToAction("Index");
दिखाई दे रहा है (R)
|---------|----------|---------|----------|
| क़िस्म | ViewDataDictionary कामों के लिए अर्ज़ी भरनीName ITempDataDictionary |
| जीवन समय भौगोलिक मौजूदा निवेदनmahjongg मौजूदा आग्रह एक पुनर्निदेशित कर रहा है
| कुंजी पहुँच स्ट्रिंग कुंजी यूनिडायरेक्शनल
| सुरक्षा क़िस्म माइक्रोसेंकेंड कुछ नहीं (सेकेंड की जरूरत है) ( पाएगा)Nonecal-reces)None
| कम्पाइल- समय जाँच एनटीएलएमNext %s
| रूट से बच गए एनटीएलएम Noएमएमई जीएमईName
मेरे चिट्ठे में, मैं सिर्फ देखने के लिए दृश्य का उपयोग करता हूँ नियंत्रण से बाहर डाटा (नात्र, वर्ग, आदि) साझा करने के लिए।
// From BaseController.cs - runs before every action
public override async Task OnActionExecutionAsync(ActionExecutingContext filterContext,
ActionExecutionDelegate next)
{
logger.LogInformation("OnActionExecutionAsync");
if (!Request.IsHtmx())
{
// Analytics settings for layout
ViewBag.UmamiPath = AnalyticsSettings.UmamiPath;
ViewBag.UmamiWebsiteId = AnalyticsSettings.WebsiteId;
ViewBag.UmamiScript = AnalyticsSettings.UmamiScript;
}
logger.LogInformation("Adding categories to viewbag");
ViewBag.Categories = await GetCategories(); // Cached list
await base.OnActionExecutionAsync(filterContext, next);
}
तो मेरे खाका में,_Layout.cshtml):
@if (ViewBag.Categories is List<string> categories)
{
<nav>
@foreach (var cat in categories)
{
<a asp-controller="Blog" asp-action="Category" asp-route-category="@cat">@cat</a>
}
</nav>
}
@if (!string.IsNullOrEmpty(ViewBag.UmamiPath))
{
<script async src="@ViewBag.UmamiScript"
data-website-id="@ViewBag.UmamiWebsiteId"></script>
}
क्यों यह नमूना कार्य करता है:
मैंने जल्दी बनाया है: मैं सेटिंग कर रहा था ViewBag.Categories हर एक क्रिया विधि में । OnActionExecutionAsync आधार नियंत्रण में यह हल किया.
// From BlogController.cs
[Route("category/{category}")]
public async Task<IActionResult> Category(string category, int page = 1, int pageSize = 10)
{
ViewBag.Category = category; // Used in view for heading
ViewBag.Title = category + " - Blog"; // Used in layout <title>
var posts = await blogViewService.GetPostsByCategory(category, page, pageSize);
// ... populate posts model ...
if (Request.IsHtmx()) return PartialView("_BlogSummaryList", posts);
return View("Index", posts);
}
दृश्य में:
@{
ViewData["Title"] = ViewBag.Title; // Standard MVC convention for <title>
}
<h1>Category: @ViewBag.Category</h1>
यह ठीक है क्योंकि:
Title और Category प्रत्येक दृश्य मॉडल को गुणों में गुणएन्टी- पैटर्न:
// DON'T DO THIS
ViewBag.User = new UserViewModel { Name = "Scott", IsAdmin = true };
ViewBag.Posts = new List<Post> { ... };
ViewBag.Metadata = new { Tags = new[] { "a", "b" }, Date = DateTime.Now };
समस्याएँ:
ViewBag.Usr असफल एटबेहतर: अत्यंत- क़िस्म का दृश्य मॉडल:
// DO THIS instead
public class BlogIndexViewModel : BaseViewModel
{
public string Category { get; set; }
public List<PostSummary> Posts { get; set; }
public PaginationInfo Pagination { get; set; }
}
public IActionResult Category(string category, int page = 1)
{
var model = new BlogIndexViewModel
{
Category = category,
Posts = await GetPosts(category, page),
// Inherited from BaseViewModel:
Authenticated = user.LoggedIn,
Name = user.Name,
AvatarUrl = user.AvatarUrl
};
return View("Index", model);
}
मानक टेंप डाटा केस का प्रयोग करें:
[HttpPost]
public IActionResult SavePost(PostModel model)
{
// Save post...
TempData["SuccessMessage"] = "Post saved successfully!";
return RedirectToAction("Index");
}
public IActionResult Index()
{
// TempData["SuccessMessage"] available here (consumed on read)
return View();
}
क्यों मैं अपने ब्लॉग में टेंप डाटा का प्रयोग नहीं करता:
[HttpPost]
public async Task<IActionResult> Submit(ContactViewModel model)
{
if (!ModelState.IsValid)
return PartialView("_ContactForm", model); // Show errors inline
await sender.SendEmailAsync(contactModel);
// Return success view directly (no redirect)
return PartialView("_Response", new ContactViewModel
{
Email = model.Email,
Name = model.Name,
Comment = "Message sent!"
});
}
जब टेंप डाटा समझ आता है:
टेंप डाटा मिला: कुकीज़ द्वारा डिफ़ॉल्ट पर वापस (बीस. वेनॉट कोर 2. 0). यदि आप बहुत सारे वस्तुओं को स्मिट्स में डाल देते हैं तो आप कुकी को हर निवेदन के साथ भेज रहे हैं. बड़ी स्थिति के लिए, सत्र समर्थन या डीबी के साथ इस्तेमाल करें.
flowchart TD
A[Need to pass data to view?] --> B{What kind?}
B -->|Global layout data| C[ViewBag in BaseController]
B -->|Simple page-specific| D[ViewBag in action]
B -->|Complex model| E[Strongly-typed ViewModel]
B -->|Survive redirect| F{Using HTMX?}
F -->|Yes| G[Return partial with message]
F -->|No| H[TempData for flash message]
मेरे नियम:
कौन - सी सरकार का इस्तेमाल करती है?
flowchart TD
A[Start Wizard] --> B{Short-lived?\nSingle browser?}
B -- Yes --> S[Session/TempData]
B -- No/Complex --> D[DB + key in route]
S --> PRG[Use PRG between steps]
D --> PRG
उदाहरण (डीबी + रूट कुंजी):
app.MapPost("/wizard/{id}", async (AppDb db, Guid id, StepInput input) =>
{
var flow = await db.Flows.FindAsync(id) ?? new Flow(id);
flow.Apply(input);
await db.SaveChangesAsync();
return Results.Redirect($"/wizard/{id}/next");
});
TempData["Flash"] = "Profile saved";
return RedirectToAction("Index");
राजेर दृश्य:
@if (TempData["Flash"] is string flash) {
<div class="alert alert-info">@flash</div>
}
flowchart LR U[User] -- cart-id cookie --> S[Server] S --> DB[(Cart Table)] S <--> Cache[Distributed Cache]
Secure, HttpOnly, SameSite, IsEssential (यदि सहमति/ अनुकूलनशीलता से आवश्यक हो)classDiagram
class Items {
+Per-request only
+Great for middleware->endpoint handoff
}
class QueryRoute {
+Explicit, linkable
-User-controlled
}
class Headers {
+Tracing, idempotency
-Noisy, untrusted
}
class Cookies {
+Persist small prefs
-Size, perf, consent
}
class TempData {
+One-redirect messages
-Ephemeral
}
class Session {
+Conversational state
-Scaling complexity
}
class MemoryCache {
+Fast, in-proc
-Not shared across instances
}
class DistributedCache {
+Shared across servers
-Serialization, ops
}
class AuthCookieClaims {
+Identity, roles
-Don’t overstuff
}
class JWT {
+Stateless, cross-domain
-Revocation/rotation
}
class DB {
+Durable, authoritative
-Latency, complexity
}
क्विक लेने का तरीका:
सभी तीन tuts एक ही पुरातन पर बैठते हैं ( सर्वोत्तम संदर्भ, मॉडल बाइंडिंग, प्राधिकार, डेटा सुरक्षा). ऊपर दिए उदाहरण दिखाते हैं कि एपीआई्स में अधिकतर अलग होते हैं:
Results.*.वे सभी यहाँ चर्चा की गयी एक ही स्थिति में भाग लेते हैं ।
इन सभी विकल्पों को दिखाने के बाद, मेरा ईमानदारी से पता चलता है कि मेरे ब्लॉग मंच के लिए उत्पादन में क्या काम करता है।
DIRERYEALLLLYEARTEANT का प्रयोग करें |-----------|-----------|-----------|--------------| | मैं मेमोरीCak बहुत अधिक उच्च पंखीय श्रेणी, मेगन, अनुवाद कार्य sciptivix | आउटपुटCTCKt SMTP उच्च दिन का अधिकतम भारदार ब्लॉग पोस्टों को दिया गया है, जो कि UNDRICWWL की सूची देते हैं | अनुक्रिया- केस INBTH ("1एलीकिंग शीर्ष CTHATKSTCKT के साथ संचालित करता है | बाजू पट्टी देखें (a) डिजीटीटीटीटीएस विन्यास, पृष्ठ शीर्षकों को सादा सामान के लिए संक्षिप्त रूप से ठीक करता है | प्रमाणीकरण सीडी- एसक्यूएल उपयोक्ता पहचान, प्रशासक झंडा- ऋण के लिए दायाँ दायाँ औजार | रूट/Query मध्यम विसम्मेलन, फ़िल्टरिंग, clugssmoltest और लिंकेबल | डाटाबेस ब्लॉग ब्लॉग पोस्ट, टिप्पणी करता है, स्टेटCART सत्य का स्रोत | कुकीज़ DURENT उपयोक्ता वरीयता (प्रयोग) DULLONEDLLLONK, अभी तक समर्थित नहीं है | सत्र अतुल्यकालिक स्थिति कभी नज़रअंदाज़ नहीं की - औऱ कोई वितरित स्टोर सेटअप सेटअप सेटअप नहीं किया गया | टेंप डाटा DTATCKS - प्रमाण की आवश्यकता नहीं | Yiva. Tiks गीत-सूची कभी नहीं इस्तेमाल किया गया | आईडीदीश INBLAN - gowCKS एकल सर्वर (अब के लिए) Advirous सर्वर
मैं इसके लिए क्या इस्तेमाल करता हूं:
अभ्यास:
मैनें मारा है:
स्केलिंग सीमाः अगर मैं कई सर्वरों के लिए मिलता है, मुझे साझा स्थिति के लिए आईडीiriocioc (Reds) की आवश्यकता होगी. अब, एकल सर्वर + स्मृति कैश सही है.
मैं इसके लिए क्या इस्तेमाल करता हूं:
अभ्यास:
मैनें मारा है:
के लिए उत्तम: सिर्फ स्थिर सामग्री के साथ पढ़ें. व्यक्तिगत या वास्तविक समय डाटा के लिए अच्छा नहीं है.
मैं इसके लिए क्या इस्तेमाल करता हूं:
अभ्यास:
मैनें मारा है:
मैं का पालन करें: साधारण कैंची के लिए सिर्फ देखें. जटिल वस्तुओं को दृश्य में चला जाता है.
मैं इसके लिए क्या इस्तेमाल करता हूं:
sub कॉन्फ़िग के विरुद्ध दावा करें)अभ्यास:
User.Claims हर जगहमैनें मारा है:
सर्वोत्तम अभ्यासः दावा करते हैं कि कम से कम और स्थिर रखने का दावा करते हैं।
सत्र (कभी नहीं प्रयोग में):
टेंप डाटा (कभी नहीं उपयोग में):
+/- संदर्भ. यह वेम (कभी नहीं इस्तेमाल किया):
आईडीदीश (कभी इस्तेमाल नहीं किया गया):
फेस 1 (प्रयोगात्मक): कम ट्रैफिक के लिए ठीक काम किया ।
फेस 2 (पहले): मैंने वर्गों के लिए रिपर जोड़े. तुरंत डीबी लोड की कमी को नोट किया. यह सरल: 30 मिनट TTL, कोई फैंसी तर्क.
फेस 3 (रद्द कर रहा है): जब ट्रैफिक चालू हो रहा हो तो ब्लॉग पोस्ट के लिए आउटपुटCocents जोड़े. भारन प्रदर्शन सुधार. आरंभिक त्रुटि: केवल 5 मिनट के लिए ही कैश्ड. सामग्री की गति 1 घंटे की गति बढ़ाने के बाद स्थिति में परिवर्तनों को कम से कम किया गया.
फेस 4 (बॉवर्सनेसनेस): % 1 से अधिक जानकारी के लिए फ़ाइलों को अद्यतन किया जा रहा है. कैश संवेदनशील होना कुंजी में फ़ॉर्मेटिंग की अनुमति देने के कारण उच्च था. स्थिर कुंजी फ़ॉर्मेट को स्थिर करने के लिए निश्चित रूप से सक्षम किया जा रहा है. yyyyMMdd पूरे समय के बजाय. हिट दर 60% से 95% तक चला.
फेस 5 (HHMXीकरण): जोड़ा गया VaryByHeader के लिए hx-request. शुरू में यह भूल गया और HMMAX निवेदन पूरे पृष्ठ की सेवा की.
वर्तमान स्थिति: ढेर के साथ धन्य है. मैं साइनीक + आउटपुटCCT + CCTCT को अपनी स्थिति प्रबंधन के 98% संभालता हूँ. स्थायी स्थिति के लिए डाटाबेस दावा करता है. यह है कि यह है.
यहाँ से प्रारंभ करें:
जब आवश्यकता हो तो जोड़ें: सत्र 6. (सिर्फ अगर आपके पास सर्वर के किनारे वार्तालाप स्थिति हो) 7. आईडीदीश (केवल जब आप अनेक सर्वरों पर स्केल करते हैं) 8. कुकीज़ ( ग्राहक की पसंद के लिए, सहमति के लिए)
टालें:
कुंजी सबकः
वेब ए. ए. के एक आकार में सभी फिट नहीं है. सबसे अधिक विकल्प चुनें जो आपकी आवश्यकताओं से मिलता है, जब आप कर सकते हैं, और सुरक्षा और जीवन आने के बारे में सुस्पष्ट रूप से हो सकते हैं.
आप इन टुकड़ों के माध्यम से प्रवाह पर गहरे जाना चाहते हैं, मेरे श्रृंखला के साथ शुरू पार्ट 1: ओवरव्यू तथा आधार और खासकर बीच के हिस्से में।
इमारत मुबारक हो.
इन उदाहरणों से पता चलता है कि आप पहले के कुछ हिस्सों में बड़े - बड़े बदलाव कर सकते हैं, जैसे कि वेब साइट, एमवीसी या राज़ पृष्ठ ।
using Microsoft.AspNetCore.DataProtection;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDataProtection();
var app = builder.Build();
app.MapPost("/prefs/secure/{value}", (HttpContext ctx, string value, IDataProtectionProvider dp) =>
{
var protector = dp.CreateProtector("prefs.theme");
var protectedValue = protector.Protect(value);
ctx.Response.Cookies.Append("pref.theme.p", protectedValue, new CookieOptions
{
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Lax,
Expires = DateTimeOffset.UtcNow.AddYears(1)
});
return Results.Ok();
});
app.MapGet("/prefs/secure", (HttpContext ctx, IDataProtectionProvider dp) =>
{
if (ctx.Request.Cookies.TryGetValue("pref.theme.p", out var v))
{
var protector = dp.CreateProtector("prefs.theme");
return Results.Text(protector.Unprotect(v));
}
return Results.NotFound();
});
युक्ति: मल्टीडिशनमेंट में, डाटा संरक्षण कुंजी (उदा., एक साझा फ़ाइल सिस्टम, रेडिस, या डीएप्रिंग कुंजी वीएल) के लिए जारी रखें ताकि कुकीज़ उदाहरणों में पढ़ सकें.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.AddAntiforgery(o => o.HeaderName = "X-CSRF-TOKEN");
var app = builder.Build();
app.MapGet("/antiforgery/token", (IAntiforgery af, HttpContext ctx) =>
{
var tokens = af.GetAndStoreTokens(ctx);
return Results.Json(new { token = tokens.RequestToken });
});
app.MapPost("/submit", (HttpContext ctx) => Results.Ok("posted"))
.AddEndpointFilter(async (efiContext, next) =>
{
var af = efiContext.HttpContext.RequestServices.GetRequiredService<IAntiforgery>();
await af.ValidateRequestAsync(efiContext.HttpContext);
return await next(efiContext);
});
app.MapControllers();
app.MapRazorPages();
[ValidateAntiForgeryToken] और प्रयोग @Html.AntiForgeryToken() रूप में.asp-antiforgery="true" ज़रूरत पड़ने पर।IAntiforgery जैसा दिखाया गया है.builder.Services.AddStackExchangeRedisCache(o => o.Configuration = "localhost:6379");
builder.Services.AddSession(o =>
{
o.IdleTimeout = TimeSpan.FromMinutes(20); // sliding
o.IOTimeout = TimeSpan.FromSeconds(2);
o.Cookie.HttpOnly = true;
o.Cookie.IsEssential = true;
});
var app = builder.Build();
app.UseSession();
छोटा, उपयोगी डाटा सिर्फ भंडारित करें. बहुमुखी बाहरी कार/क्रम को DB के लिए.
builder.Services.AddMemoryCache();
app.MapGet("/fx/{pair}", (IMemoryCache cache, string pair) =>
{
var key = $"fx:{pair.ToLowerInvariant()}";
return Results.Ok(cache.GetOrCreate(key, entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10);
entry.SlidingExpiration = TimeSpan.FromMinutes(2);
entry.Size = 1; // enable size-based eviction if configured
entry.RegisterPostEvictionCallback((k, v, reason, state) =>
{
Console.WriteLine($"Evicted {k} because {reason}");
});
return 0.92m; // fetch from external service in real life
}));
});
builder.Services.AddStackExchangeRedisCache(o => o.Configuration = "localhost:6379");
app.MapGet("/feature/{name}", async (IDistributedCache cache, string name) =>
{
var key = $"feat:{name}";
var cached = await cache.GetStringAsync(key);
if (cached is not null) return Results.Text(cached);
// Lock key to prevent thundering herd (very simple approach)
var lockKey = key + ":lock";
var gotLock = await cache.SetStringAsync(lockKey, "1", new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(5)
});
try
{
cached = await cache.GetStringAsync(key);
if (cached is null)
{
var computed = "on"; // expensive work
var rnd = Random.Shared.Next(0, 15); // jitter
await cache.SetStringAsync(key, computed, new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5).Add(TimeSpan.FromSeconds(rnd))
});
cached = computed;
}
}
finally
{
await cache.RemoveAsync(lockKey);
}
return Results.Text(cached);
});
मजबूत ताला के लिए, प्रधान रेडस (लाएक्सएक्सएक्सएक्सएक्स XX) स्टैक्स के ज़रिए.
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;
var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes("super-secret-key-please-rotate"));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", o =>
{
o.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
IssuerSigningKey = key,
ValidateIssuerSigningKey = true,
ValidateLifetime = true
};
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapPost("/token", () =>
{
var claims = new[] { new Claim(ClaimTypes.Name, "alice") };
var jwt = new JwtSecurityToken(claims: claims, expires: DateTime.UtcNow.AddMinutes(30), signingCredentials: creds);
var token = new JwtSecurityTokenHandler().WriteToken(jwt);
return Results.Json(new { access_token = token });
});
app.MapGet("/who", [Microsoft.AspNetCore.Authorization.Authorize] () => "ok");
record Todo(int Id, string Title, string Version);
var store = new Dictionary<int, Todo> { [1] = new(1, "Ship", "v1") };
app.MapGet("/todo/{id:int}", (int id, HttpContext ctx) =>
{
if (!store.TryGetValue(id, out var t)) return Results.NotFound();
ctx.Response.Headers.ETag = t.Version;
return Results.Json(t);
});
app.MapPut("/todo/{id:int}", (int id, HttpContext ctx, Todo input) =>
{
if (!store.TryGetValue(id, out var current)) return Results.NotFound();
var ifMatch = ctx.Request.Headers["If-Match"].ToString();
if (string.IsNullOrEmpty(ifMatch) || ifMatch != current.Version)
return Results.StatusCode(StatusCodes.Status412PreconditionFailed);
var next = current with { Title = input.Title, Version = $"v{DateTime.UtcNow.Ticks}" };
store[id] = next;
ctx.Response.Headers.ETag = next.Version;
return Results.Ok(next);
});
public static class TempDataJsonExtensions
{
public static void Put<T>(this ITempDataDictionary tempData, string key, T value)
=> tempData[key] = System.Text.Json.JsonSerializer.Serialize(value);
public static T? Get<T>(this ITempDataDictionary tempData, string key)
=> tempData.TryGetValue(key, out var o) && o is string s
? System.Text.Json.JsonSerializer.Deserialize<T>(s)
: default;
}
// Usage in MVC action
TempData.Put("WizardState", new { Step = 2, Name = "Alice" });
var state = TempData.Get<dynamic>("WizardState");
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
[Timestamp] public byte[] RowVersion { get; set; } = default!;
}
// On update
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
return Results.StatusCode(StatusCodes.Status412PreconditionFailed);
}
app.MapPost("/promote", async (HttpContext ctx) =>
{
var u = ctx.User;
var claims = u.Claims.ToList();
claims.Add(new Claim(ClaimTypes.Role, "Editor"));
var id = new ClaimsIdentity(claims, "Cookies");
await ctx.SignInAsync("Cookies", new ClaimsPrincipal(id));
return Results.Ok();
});
इससे पता चलता है कि सुरक्षा की माँगों को पूरा करने के लिए ज़रूरी है: ज़्यादा सुरक्षा डिफ़ॉल्ट, बहुत - से लोग कैश, संकेतों और शर्तओं के लिए तैयार रहते हैं ।
2880 संदर्भ संदर्भ. यह एक प्रति-पुष्ट बैग है (प्रयोगी, वस्तु) कि केवल एक अनुरोध के लिए ही रहते हैं. यह आपके अंत - किनारों, नियंत्रणों, और Rachers पृष्ठों को छूने बिना विश्वव्यापी राज्य या लंबे समय तक वितरित करने के लिए लागू करने के लिए सही मान है.
क्योंकि वस्तुएँ ऑब्जेक्ट कुंजियों का उपयोग करती हैं, निजी स्थिर वस्तु या समर्पित कुंजी नाम की विपत्ति से बचने के लिए.
public static class ItemKeys
{
public static readonly object TenantId = new();
public static readonly object UserLocale = new();
public static readonly object PerRequestCache = new();
}
या विस्तार के साथ टंकण किया जा रहा है:
public static class HttpContextItemsExtensions
{
public static void Set<T>(this HttpContext ctx, object key, T value)
=> ctx.Items[key] = value!;
public static T? Get<T>(this HttpContext ctx, object key)
=> ctx.Items.TryGetValue(key, out var v) ? (T?)v : default;
public static T GetOrCreate<T>(this HttpContext ctx, object key, Func<T> factory)
{
if (ctx.Items.TryGetValue(key, out var existing) && existing is T typed)
return typed;
var created = factory();
ctx.Items[key] = created!;
return created;
}
}
// Program.cs
app.Use(async (ctx, next) =>
{
var tenant = ctx.Request.Headers["X-TenantId"].FirstOrDefault() ?? "public";
ctx.Set(ItemKeys.TenantId, tenant); // using extension above
// Per-request cache holder (optional)
ctx.Set(ItemKeys.PerRequestCache, new Dictionary<string, object?>());
await next(ctx);
});
// Minimal API
app.MapGet("/whoami", (HttpContext ctx) => new
{
Tenant = ctx.Get<string>(ItemKeys.TenantId),
});
// MVC Controller
public IActionResult WhoAmI()
=> Json(new { Tenant = HttpContext.Get<string>(ItemKeys.TenantId) });
// Razor Page handler
public IActionResult OnGet()
=> new JsonResult(new { Tenant = HttpContext.Get<string>(ItemKeys.TenantId) });
वस्तु को एक छोटे सी कैश के रूप में इस्तेमाल करें ताकि वही निवेदन के भीतर बार - बार पढ़ा जा सके
public static class PerRequestCacheExtensions
{
public static async Task<T> GetOrAddAsync<T>(this HttpContext ctx, string key, Func<Task<T>> factory)
{
var bag = ctx.Get<Dictionary<string, object?>>(ItemKeys.PerRequestCache)
?? ctx.GetOrCreate(ItemKeys.PerRequestCache, () => new Dictionary<string, object?>());
if (bag.TryGetValue(key, out var val) && val is T hit)
return hit;
var created = await factory();
bag[key] = created!;
return created;
}
}
// Usage in endpoint
app.MapGet("/profile", async (HttpContext ctx, IUserRepo repo) =>
{
var userId = ctx.User.Identity?.Name ?? "anon";
var profile = await ctx.GetOrAddAsync($"profile:{userId}", () => repo.LoadAsync(userId));
return Results.Json(profile);
});
टिप्पणी:
public class TenantFilter : IAsyncResourceFilter
{
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
{
var tenant = context.HttpContext.Request.Headers["X-TenantId"].FirstOrDefault() ?? "public";
context.HttpContext.Set(ItemKeys.TenantId, tenant);
await next();
}
}
// Register filter globally
services.AddControllersWithViews(o => o.Filters.Add<TenantFilter>());
एक बार गणना करें, तब लॉगिंग स्कोप या मध्य- बिन्दु में पढ़ा जाए.
app.Use(async (ctx, next) =>
{
var correlationId = ctx.Request.Headers["X-Correlation-Id"].FirstOrDefault() ?? Guid.NewGuid().ToString("n");
ctx.Items["CorrelationId"] = correlationId; // string key acceptable for app-local use
using (logger.BeginScope(new { CorrelationId = correlationId }))
{
await next(ctx);
}
});
जल्दी नियम: यदि इस निवेदन के दौरान इसकी गणना की जाती है और आपके अपने कोड द्वारा इस निवेदन के अन्दर पढ़ा जाता है, तो वस्तुएँ आदर्श हैं ।
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.