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, 23 November 2025
जब आप 2004 के बाद से ब्लॉगिंग कर रहे हैं (हाँ, सच में), आप डिजिटल डीटीटीटीएस के बहुत सारे संग्रह जमा करते हैं. मैंने हाल ही में अपने पुराने पोस्टों को 2004- 2007 से आयात किया ( http://www.wiki.net.net/ राहे ओर से_BAR_BAR_netd) और कि लगभग पता चला गया BAR सभी बाहरी लिंक ऐसे साइटों की ओर इशारा करते हैं जो एक दशक पहले गायब हो गए थे, पुराने यूआरएल योजनाों में जो अब मौज़ूदा संरचना, पूरी हद से मेल नहीं खाते.
समस्या तीन भागों में नीचे गिर जाती है:
आंतरिक लिंक - आयात प्रक्रिया के दौरान स्वयं को मेरे प्रयोग से स्थिर अभिलेखउसके द्वारा किए गए परिवर्तनों के लिए एक्ज़इफ ओरिएन्टेशन उपकरण.. मेरे पुराने पोस्टों ने पुरानी यूआरएल योजना का प्रयोग करते हुए एक दूसरे का उल्लेख किया, तो मैं उत्प्रवासन के भाग के रूप में उन्हें फिर से बदल देता हूँ.
बाहरी कड़ियाँ ( आउट) - यह बड़ा है BAR बाहरी संसाधनों के लिए कड़ी है कि अब से गायब हो गया है, या पूरी तरह से अलग साइट बन गए हैं BAR 2006 से कुछ दस्तावेजों के लिए एक लिंक चला गया BAR एक ब्लॉग पोस्ट जो लंबे समय से उनके साइट के नीचे ले लिया गया था BAR मृत नियंत्रण की आवश्यकता है BAR (R_)
इनकमिंग आग्रह - लोग (और खोज इंजिन) अभी भी पुराने यूआरएलों को ऐसे एक्सेस करने की कोशिश करते हैं /archive/2006/05/15/123.aspx. manmanmanmanmanmium प्रणाली अकसर पता लगा सकते हैं कि वे क्या कर रहे हैं, यहाँ तक कि एक सटीक मैच के बिना भी।
अब, मैं कर सकता है अभिलेख बनाया गया है आयात प्रक्रिया में. लेकिन यहाँ पर बात है, समय पर लिंक ब्रेक. एक साइट है जो आज काम कर रहा है अगले महीने में चला जा सकता है. इस गति से आवधिक पुनः जाँच के साथ तेज, सिस्टम स्वचालित रूप से बाहर हो सकता है. भविष्य न सिर्फ़ वे लोग जो आयात के समय अस्तित्व में थे ।
इस लेख में मेरी तरफ बताया गया है:
BrokenLinkArchiveMiddleware - अभिलेखागार के साथ मरे हुए बाहरी लिंक को प्रतिस्थापित करता है.org स्नैपशॉटयहां इंटरनेट के बारे में बात है: यह स्थायी नहीं है. यह बढ़िया ब्लॉग पोस्ट जिसे आपने 2006 में जोड़ा था? वो दस्तावेज़ बंद हो गया? यह दस्तावेज़ तीन बार बंद हो गया. आपकी खुद की योजना आप से पहले एक उचित प्रवरित सम्मेलन में बसने से पहले? एमआईएसआईएस.
graph TD
A[User requests old post] --> B{Link valid?}
B -->|Yes| C[Happy user]
B -->|No| D[404 Error]
D --> E[Frustrated user]
E --> F[User leaves]
style D stroke:#ef4444,stroke-width:3px
style F stroke:#ef4444,stroke-width:3px
style C stroke:#10b981,stroke-width:3px
लेकिन जब आपके पास हजारों कड़ियों के साथ सैकड़ों पोस्टों को ठीक करने के लिए है, कि पर नहीं है। हमें लिंक की जरूरत है।
तंत्र में दो मुख्य अवयव हैं जो कि टीक में काम करते हैं:
flowchart TB
subgraph Incoming["Incoming Requests"]
A[User Request] --> B{Page exists?}
B -->|Yes| C[Render Page]
B -->|No| D[404 Handler]
D --> E{Learned redirect?}
E -->|Yes| F[301 Permanent Redirect]
E -->|No| G{High-confidence match?}
G -->|Yes| H[302 Temporary Redirect]
G -->|No| I[Show suggestions]
I --> J[User clicks suggestion]
J --> K[Learn redirect]
end
subgraph Outgoing["Outgoing Links"]
C --> L[BrokenLinkArchiveMiddleware]
L --> M[Extract all links]
M --> N[Register for checking]
L --> O[Replace broken links]
O --> P[Archive.org URLs]
O --> Q[Semantic search results]
O --> R[Remove dead links]
end
style F stroke:#10b981,stroke-width:3px
style H stroke:#f59e0b,stroke-width:3px
style P stroke:#3b82f6,stroke-width:3px
style Q stroke:#8b5cf6,stroke-width:3px
इस बीच में एचटीएमएल प्रतिक्रियाएं तथा तीन बातें करता है:
कुंजी अन्तर्दृष्टि यह है कि हम अभिलेख ढूंढना चाहते हैं.org स्नैपशॉट से समय के चारों ओर पोस्ट लिखा गयासन् 2006 के लेख के एक स्नेपशॉट के बारे में पूरी तरह से जानकारी हो सकती है. तो हम ब्लॉग पोस्ट पोस्ट को प्रकाशित तिथि देखते हैं और निकटतम स्नेपशॉट के लिए अभिलेख पूछते हैं.org.
यहाँ कोर स्ट्रक्चर है:
public partial class BrokenLinkArchiveMiddleware(
RequestDelegate next,
ILogger<BrokenLinkArchiveMiddleware> logger,
IServiceScopeFactory serviceScopeFactory)
{
public async Task InvokeAsync(
HttpContext context,
IBrokenLinkService? brokenLinkService,
ISemanticSearchService? semanticSearchService)
{
// Only process HTML responses for blog pages
if (!ShouldProcessRequest(context))
{
await next(context);
return;
}
// Capture the response so we can modify it
var originalBodyStream = context.Response.Body;
using var responseBody = new MemoryStream();
context.Response.Body = responseBody;
await next(context);
// Process the HTML response
if (IsSuccessfulHtmlResponse(context, responseBody))
{
var html = await ReadResponseAsync(responseBody);
html = await ProcessLinksAsync(html, context, brokenLinkService, semanticSearchService);
await WriteModifiedResponseAsync(originalBodyStream, html, context);
}
else
{
await CopyOriginalResponseAsync(responseBody, originalBodyStream);
}
}
}
हम सब बाहर खींचने के लिए एक उत्पन्न जहन्नम का उपयोग करें href गुण. [GeneratedRegex] गुण .netT हमें कंपाइल-समय पीढ़ी देता है, जो दोनों तेजी से और आवंटन मुक्त है:
[GeneratedRegex(@"<a[^>]*\shref\s*=\s*[""']([^""']+)[""'][^>]*>",
RegexOptions.IgnoreCase | RegexOptions.Compiled)]
private static partial Regex HrefRegex();
private List<string> ExtractAllLinks(string html, HttpRequest request)
{
var links = new List<string>();
var matches = HrefRegex().Matches(html);
foreach (Match match in matches)
{
var href = match.Groups[1].Value;
// Skip special links (anchors, mailto, etc.)
if (SkipPatterns.Any(p => href.StartsWith(p, StringComparison.OrdinalIgnoreCase)))
continue;
if (Uri.TryCreate(href, UriKind.Absolute, out var uri))
{
if (uri.Scheme == "http" || uri.Scheme == "https")
links.Add(href);
}
else if (href.StartsWith("/"))
{
// Convert relative URLs to absolute for tracking
var baseUri = new UriBuilder(request.Scheme, request.Host.Host,
request.Host.Port ?? (request.Scheme == "https" ? 443 : 80));
links.Add(new Uri(baseUri.Uri, href).ToString());
}
}
return links.Distinct().ToList();
}
अब आप काफी हद तक उपयोग कर सकते हैं Hatatatacks /secs.com/ALACAN/AAAAS case cly / एक तेज या समान HTML (और अन्य) विश्लेषण यहाँ अधिक जटिल घटनाओं के लिए, लेकिन यह fhotaks के लिए किया गया था - हम सिर्फ जल्दी से बाहर निकालने की जरूरत है.
हम लिंक की जाँच के दौरान प्रतिक्रिया ब्लॉक नहीं करना चाहते. इसके बजाय, हम एक पृष्ठभूमि कार्य से आग:
var allLinks = ExtractAllLinks(html, context.Request);
var sourcePageUrl = context.Request.Path.Value;
if (allLinks.Count > 0)
{
// Fire and forget - don't block the response
_ = Task.Run(async () =>
{
using var scope = serviceScopeFactory.CreateScope();
var scopedService = scope.ServiceProvider.GetRequiredService<IBrokenLinkService>();
await scopedService.RegisterUrlsAsync(allLinks, sourcePageUrl);
});
}
टिप्पणी IServiceScopeFactory - हम एक नया स्कोप की जरूरत है क्योंकि मूल निवेदन सेवा हमारे पृष्ठभूमि कार्य पूरा होने से पहले लागू किया जाएगा।
महत्वपूर्ण: यह एक है अंत में संगतता मॉडल, जब कोई भी लिंक पहले पता चला जाता है, यह वैधीकरण के लिए कतार हो जाता है - हम नहीं जानते कि यह टूट गया है या नहीं. पृष्ठभूमि सेवा यह जाँच करता है (एएसएएएस निवेदन), और अगर यह टूट गया है, एक अभिलेख लाने के लिए लाने.org. अगला उस पृष्ठ पर उपस्थित लोगों को निर्धारित लिंक दिखाई देगा. नियमित यातायात के साथ एक ब्लॉग के लिए, इस विशेष रूप से टूटी हुई कड़ियों का उपयोग पहले खोज के घंटों के भीतर तय किया जा सकता है. सुंदर है हमें अनुमान लगाने के लिए नहीं है कि कौन सा लिंक तोड़ दिया जा सकता है - हम सिर्फ उन सभी को स्वचालित रूप से वैध कर सकते हैं.
एक बार हम जानते हैं कि एक लिंक टूट गया है और एक अभिलेख है.org बदलाव (पिछले पृष्ठभूमि की जाँच से), हम उन्हें एक सहायक विवरण के साथ बदल देते हैं:
foreach (var (originalUrl, archiveUrl) in archiveMappings)
{
if (html.Contains(originalUrl))
{
var tooltipText = $"Original link ({originalUrl}) is dead - archive.org version used";
var originalPattern = $"href=\"{originalUrl}\"";
var archivePattern = $"href=\"{archiveUrl}\" class=\"tooltip tooltip-warning\" " +
$"data-tip=\"{tooltipText}\" data-original-url=\"{originalUrl}\"";
html = html.Replace(originalPattern, archivePattern);
}
}
आंतरिक लिंक के लिए, हम पहली बार परीक्षण की कोशिश करें:
if (isInternal && semanticSearchService != null)
{
var replacement = await TryFindSemanticReplacementAsync(
brokenUrl, semanticSearchService, request, cancellationToken);
if (replacement != null)
{
html = ReplaceHref(html, brokenUrl, replacement);
continue;
}
}
// No replacement found - convert to plain text
html = RemoveHref(html, brokenUrl);
निवेदन - कि अब तक बहुत धीमा हो जाएगा के दौरान आंतरिक लिंक की जाँच नहीं करता. इसके बजाय, यह कतार किसी डाटाबेस तालिका, और एक पृष्ठभूमि प्रक्रिया के लिए कड़ी प्राप्त की गई है BrokenLinkCheckerBackgroundService वास्तविक जाँच संभालता है.
सेवा घंटे दौड़ता है और दो चीजें करता है.
हम इस बारे में अच्छे नागरिक हैं - चेक एक मनपसंद उपयोक्ता एजेंट का उपयोग करता है जो खुद की पहचान कराता है और साइट में वापस लिंक करता है:
request.Headers.UserAgent.ParseAdd(
"Mozilla/5.0 (compatible; MostlylucidBot/1.0; +https://www.mostlylucid.net)");
यह साइट प्रशासकों को देखने देता है कि उनके सर्वर को क्या मार रहा है, और वे हमें देख सकते हैं अगर वे उत्सुक हैं. हम भी जाँच के बीच 2 सेकण्ड अनुरोध करते हैं, जो कि किसी के सर्वर को हटाने से बचने के लिए.
कॉमीसी, लिंक हैं समय-जांच. एक लिंक जो पिछले सप्ताह काम कर रहा था आज मृत हो सकता है. सेवा लिंक लेता है कि पिछले 24 घंटे में चेक नहीं किया गया है और फिर उन्हें फिर से स्थापित नहीं किया गया है. लेकिन, एक बार जब हम एक टूटे हुए लिंक के लिए एक अभिलेख मिल गया है, हम मूल - यह पहले से ही मृत है और हम एक काम करने के लिए नहीं है.
sequenceDiagram
participant BG as Background Service
participant DB as Database
participant Web as External Sites
participant Archive as Archive.org CDX API
loop Every Hour
BG->>DB: Get links not checked in 24h (batch of 20)
loop For each link
BG->>Web: HEAD request
Web-->>BG: Status code
BG->>DB: Update link status + LastCheckedAt
end
BG->>DB: Get broken links needing archive lookup
loop For each broken link
BG->>DB: Look up source post publish date
BG->>Archive: CDX API query (filtered by date)
Archive-->>BG: Closest snapshot
BG->>DB: Store archive URL (permanent)
end
end
अभिलेख.org एक सीडीएक्स (कंत्र निर्देशिका)क प्रदान करता है जो कि हमें स्नैपशॉट के लिए क्वैरी करता है. चतुर सा फ़िल्टर तारीख तक फ़िल्टरिंग है:
private async Task<string?> GetArchiveUrlAsync(
string originalUrl,
DateTime? beforeDate,
CancellationToken cancellationToken)
{
var queryParams = new List<string>
{
$"url={Uri.EscapeDataString(originalUrl)}",
"output=json",
"fl=timestamp,original,statuscode",
"filter=statuscode:200", // Only successful responses
"limit=1"
};
// Find snapshot closest to the blog post's publish date
if (beforeDate.HasValue)
{
queryParams.Add($"to={beforeDate.Value:yyyyMMdd}");
queryParams.Add("sort=closest");
queryParams.Add($"closest={beforeDate.Value:yyyyMMdd}");
}
var apiUrl = $"https://web.archive.org/cdx/search/cdx?{string.Join("&", queryParams)}";
// ... fetch and parse response
return $"https://web.archive.org/web/{timestamp}/{original}";
}
इसका मतलब है कि अगर मैं 2008 में कुछ संसाधन के लिए लिंक में एक पोस्ट लिखा, मैं 2008 से अभिलेख मिल जाएगा, एक आधुनिक संस्करण है कि पूरी तरह से अलग हो सकता है.
हम एक उचित एंटिटी के साथ ट्रैक करें:
[Table("broken_links", Schema = "mostlylucid")]
public class BrokenLinkEntity
{
public int Id { get; set; }
public string OriginalUrl { get; set; } = string.Empty;
public string? ArchiveUrl { get; set; }
public bool IsBroken { get; set; } = false;
public int? LastStatusCode { get; set; }
public DateTimeOffset? LastCheckedAt { get; set; }
public int ConsecutiveFailures { get; set; } = 0;
public string? SourcePageUrl { get; set; } // For publish date lookup
}
भविष्य में ब्रेकिंग करने के लिए कुंजी फिर से जाँच है. लिंक के लिए सेवा अनुमतिओं को हाल ही में वैध नहीं किया गया है:
public async Task<List<BrokenLinkEntity>> GetLinksToCheckAsync(int batchSize, CancellationToken cancellationToken)
{
var cutoff = DateTimeOffset.UtcNow.AddHours(-24);
return await _dbContext.BrokenLinks
.Where(x => x.LastCheckedAt == null || x.LastCheckedAt < cutoff)
.OrderBy(x => x.LastCheckedAt ?? DateTimeOffset.MinValue) // Oldest first
.Take(batchSize)
.ToListAsync(cancellationToken);
}
इसका मतलब है कि हर लिंक एक दिन कम से कम एक बार फिर शुरू हो जाता है. यदि एक पहले काम लिंक 404s वापस आ जाता है, हम इसे पकड़ लेंगे और एक अभिलेख के लिए देख शुरू करेंगे. ConsecutiveFailures क्षेत्र हमें थोड़ा क्षमा करें - हम एक टुकड़े के रूप में एक टुकड़े नहीं है एक अस्थायी त्रुटि के रूप में.
अब सिक्के के अन्य पक्ष के लिए: लोग (और खोज इंजन) जो मौजूद नहीं है उन यूआरएल को अनुरोध करते हैं. इसमें यह भी शामिल है:
/archive/2006/05/15/123.aspxइनमें से कुछ अभी भी सूचीबद्ध, पसंदीदा, या अन्य साइटों से जुड़े हुए हैं ।इटेलिक खोज तंत्र विशेष रूप से यहाँ मदद करता है. यहाँ भी एक सीधे मेल मिलान के बिना, हम अनुरोधित यूआरएल से शब्दों को निकाल सकते हैं और उस सामग्री को पा सकते हैं जो कि अनंतता से मेल खाती है. तंत्र जानता है कि दोनों पोस्ट के लिए डाटा जानता है और प्रत्येक पोस्ट के लिए डाटा जानता है, तो यह "पूरी तरह से मेल खाता है" तो यह भी पूरी तरह से अलग तरह से भिन्न यूआरएल के लिए जोड़ सकता है.
मेरी पहली ख्वाहिश थी कि बीच - बीच में राय कायम करने के लिए बार - बार कुछ फेरबदल करने की गुज़ारिश की जाए, जाने - माने एहसानों की जाँच करें, यहाँ तक कि लात मारने से पहले फिर से कोशिश करें । कर सकता है काम, और यहाँ यह कैसा लग रहा है:
// DON'T DO THIS - runs on EVERY request
public class SlugRedirectMiddleware(RequestDelegate next)
{
public async Task InvokeAsync(HttpContext context, ISlugSuggestionService? service)
{
if (context.Request.Path.StartsWithSegments("/blog"))
{
var targetSlug = await service.GetAutoRedirectSlugAsync(slug, language);
if (!string.IsNullOrWhiteSpace(targetSlug))
{
context.Response.Redirect($"/blog/{targetSlug}", permanent: true);
return;
}
}
await next(context);
}
}
समस्या यह चल रहा है? प्रत्येक निवेदन को /blog/*यही कारण है कि हर पृष्ठ के दृश्य पर एक डाटाबेस क्वैरी है, यहाँ तक कि पूरी तरह से वैध यूआरएल के लिए. बढ़िया यातायात के साथ एक ब्लॉग के लिए, आप उस समय के कोई उचित कारण 99% के लिए डाटाबेस का उपयोग कर रहे हैं.
बहुत बेहतर तरीका है: 404 हैंडलर में हुक. यदि कोई मतलब हो तो पहले से ही पृष्ठ मौजूद नहीं है. तब हम रूट के लिए जाँच करते हैं. वैध पृष्ठों पर कोई बर्बादता नहीं.
अजीब बात है: AERTVC के प्रारंभिक दिनों में यह था कि हम कैसे दोस्ताना यूआरएल काम किया. स्कॉट GVC के विमान डेमो जिसने एमVC शुरू किया था, HISVC प्रणाली में हुक, यूआरएल को पढ़ने और सही सामग्री का काम करने में. यह एक सक्रिय था जो मैं एक प्रणाली में इस्तेमाल किया था एक वेब 3 के लिए एक वेब 3 s. मैं कैसे एक टीम पर एक टीम मिल गया!
जब किसी को 404 हिट करता है, हम उन्हें सुझाव दिखाते हैं कि लिफाफे से मेल खाते हैं और (वैकल्पिक) खोज करते हैं. यदि वे किसी सुझाव पर क्लिक करते हैं, तो हम रिकॉर्ड करते हैं कि पर्याप्त भरोसे के साथ पर्याप्त क्लिक के बाद, हम ऑटो-चिंग शुरू करते हैं.
stateDiagram-v2
[*] --> RequestReceived
RequestReceived --> RoutingCheck
RoutingCheck --> PageFound: Exists
RoutingCheck --> NotFound: 404
PageFound --> [*]
NotFound --> ErrorController
ErrorController --> CheckLearnedRedirect
CheckLearnedRedirect --> Redirect301: Has learned redirect
CheckLearnedRedirect --> CheckHighConfidence: No learned redirect
CheckHighConfidence --> Redirect302: Score >= 0.85 & gap >= 0.15
CheckHighConfidence --> ShowSuggestions: Lower confidence
ShowSuggestions --> UserClicks
UserClicks --> RecordClick
RecordClick --> UpdateWeight
UpdateWeight --> CheckThreshold
CheckThreshold --> EnableAutoRedirect: Weight >= 5 & confidence >= 70%
CheckThreshold --> [*]: Below threshold
EnableAutoRedirect --> [*]
सभी पुनर्निदेशित तर्क जीवन में ErrorController. एक मतलब है. UseStatusCodePagesWithReExecute हमारे त्रुटि हैंडलर के माध्यम से निवेदन को मध्य- भाग दें जब 404 घटित होता है:
public class ErrorController(
BaseControllerService baseControllerService,
ILogger<ErrorController> logger,
ISlugSuggestionService? slugSuggestionService = null) : BaseController(baseControllerService, logger)
{
[Route("/error/{statusCode}")]
[HttpGet]
public async Task<IActionResult> HandleError(int statusCode, CancellationToken cancellationToken = default)
{
var statusCodeReExecuteFeature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
switch (statusCode)
{
case 404:
// Check for auto-redirects before showing 404 page
var autoRedirectResult = await TryAutoRedirectAsync(statusCodeReExecuteFeature, cancellationToken);
if (autoRedirectResult != null)
return autoRedirectResult;
var model = await CreateNotFoundModel(statusCodeReExecuteFeature, cancellationToken);
return View("NotFound", model);
case 500:
return View("ServerError");
default:
return View("Error");
}
}
}
वह TryAutoRedirectAsync विधि दोनों सीखने वाले पुनर्निदेशों तथा पहली बार निर्भरता से मेल खाता है:
private async Task<IActionResult?> TryAutoRedirectAsync(
IStatusCodeReExecuteFeature? statusCodeReExecuteFeature,
CancellationToken cancellationToken)
{
if (slugSuggestionService == null || statusCodeReExecuteFeature == null)
return null;
var originalPath = statusCodeReExecuteFeature.OriginalPath ?? string.Empty;
var (slug, language) = ExtractSlugAndLanguage(originalPath);
// First: check for learned redirects (user previously clicked a suggestion)
// These get 301 Permanent Redirect - confirmed patterns
var learnedTargetSlug = await slugSuggestionService.GetAutoRedirectSlugAsync(
slug, language, cancellationToken);
if (!string.IsNullOrWhiteSpace(learnedTargetSlug))
{
var redirectUrl = BuildRedirectUrl(learnedTargetSlug, language);
logger.LogInformation("Learned auto-redirect (301): {Original} -> {Target}", originalPath, redirectUrl);
return RedirectPermanent(redirectUrl);
}
// Second: check for high-confidence first-time matches
// These get 302 Temporary Redirect until confirmed by user clicks
var firstTimeTargetSlug = await slugSuggestionService.GetFirstTimeAutoRedirectSlugAsync(
slug, language, cancellationToken);
if (!string.IsNullOrWhiteSpace(firstTimeTargetSlug))
{
var redirectUrl = BuildRedirectUrl(firstTimeTargetSlug, language);
logger.LogInformation("First-time auto-redirect (302): {Original} -> {Target}", originalPath, redirectUrl);
return Redirect(redirectUrl);
}
return null; // No redirect - show suggestions
}
सुझाव सेवा लंबी दूरी का उपयोग करती है (दूरी दूरी) और कुछ विशेषज्ञ:
private double CalculateSimilarity(string source, string target)
{
if (string.Equals(source, target, StringComparison.OrdinalIgnoreCase))
return 1.0;
source = source.ToLowerInvariant();
target = target.ToLowerInvariant();
// Levenshtein distance converted to similarity (0-1)
var distance = CalculateLevenshteinDistance(source, target);
var maxLength = Math.Max(source.Length, target.Length);
var levenshteinSimilarity = 1.0 - (double)distance / maxLength;
// Bonus if one string contains the other
var substringBonus = (source.Contains(target) || target.Contains(source)) ? 0.2 : 0.0;
// Bonus for common prefix (catches typos at the end)
var prefixLength = GetCommonPrefixLength(source, target);
var prefixBonus = (double)prefixLength / Math.Min(source.Length, target.Length) * 0.1;
return Math.Min(1.0, levenshteinSimilarity + substringBonus + prefixBonus);
}
जब कोई उपयोगकर्ता किसी सुझाव को क्लिक करता है, हम इसे रिकॉर्ड करते हैं और विश्वास के अंक का अद्यतन करते हैं:
public async Task RecordSuggestionClickAsync(
string requestedSlug,
string clickedSlug,
string language,
int suggestionPosition,
double originalScore,
CancellationToken cancellationToken = default)
{
var redirect = await _context.SlugRedirects
.FirstOrDefaultAsync(r =>
r.FromSlug == normalizedRequestedSlug &&
r.ToSlug == normalizedClickedSlug &&
r.Language == language,
cancellationToken);
if (redirect == null)
{
redirect = new SlugRedirectEntity
{
FromSlug = normalizedRequestedSlug,
ToSlug = normalizedClickedSlug,
Language = language,
Weight = 1
};
_context.SlugRedirects.Add(redirect);
}
else
{
redirect.Weight++;
redirect.LastClickedAt = DateTimeOffset.UtcNow;
}
redirect.UpdateConfidenceScore();
await _context.SaveChangesAsync(cancellationToken);
}
भरोसा स्कोर गणना दोनों क्लिकों और लक्षणों को दर्शाता है:
public void UpdateConfidenceScore()
{
var total = Weight + ShownCount;
ConfidenceScore = total > 0 ? (double)Weight / total : 0.0;
// Enable auto-redirect after 5+ clicks with 70%+ confidence
if (Weight >= AutoRedirectWeightThreshold &&
ConfidenceScore >= AutoRedirectConfidenceThreshold)
{
AutoRedirect = true;
}
}
हमारे पास स्वचालित पुनर्निदेशित के दो स्तर हैं:
प्रथम- समय उच्च भरोसा (302): यदि समानता अंक है > = 0.85 और वहाँ दूसरी सबसे अच्छी मैच के लिए एक महत्वपूर्ण अंतराल है, हम तुरंत एक 302 के साथ पुनर्निदेशित करते हैं. यह स्पष्ट रूप से caseoss.
जवाब (301) सीखा: 5+सी क्लिक के बाद 70%+ आत्म - विश्वास के साथ, हम एक स्थायी 301 दफा का उपयोग करते हैं. यह "मानवों ने कहा है" मामले है.
public async Task<string?> GetFirstTimeAutoRedirectSlugAsync(
string requestedSlug,
string language,
CancellationToken cancellationToken = default)
{
var suggestions = await GetSuggestionsWithScoreAsync(requestedSlug, language, 2, cancellationToken);
if (suggestions.Count == 0) return null;
var topMatch = suggestions[0];
// Need high confidence
if (topMatch.Score < 0.85) return null;
// If there's only one suggestion with high score, redirect
if (suggestions.Count == 1) return topMatch.Post.Slug;
// Multiple suggestions - only redirect if there's a clear winner
var scoreGap = topMatch.Score - suggestions[1].Score;
if (scoreGap >= 0.15)
return topMatch.Post.Slug;
return null; // Too close to call - show suggestions instead
}
के लिए बाहर जाने वाले लिंक, बीच में चुनाव सही है . BrokenLinkArchiveMiddleware एचटीएमएल प्रतिक्रिया दिखाने की जरूरत है पश्चात यह अनुवाद किया गया है लेकिन पहले यह ग्राहक को भेजा गया है. वहाँ कोई अन्य बुद्धिमान जगह नहीं है यह करने के लिए - हम प्रतिक्रिया स्ट्रीम को संशोधित करने की जरूरत है, और मध्य हॉल को वास्तव में उस के लिए बनाया गया है.
के लिए आने वाला पुनर्निदेश- बीच में चुनाव गलत है। हम प्रत्येक पर डाटाबेस जांच हो रही होगी /blog/* सिर्फ यह जाँचने के लिए अनुरोध करें कि क्या हो सकता है, संभवतः इस URL को एक पुनर्निदेशित की आवश्यकता हो सकती है. 404 हैंडलर केवल तब चलाता है जब हम पहले से ही पृष्ठ के अस्तित्व में नहीं है.
// In Program.cs
app.UseStatusCodePagesWithReExecute("/error/{0}"); // Handles 404s through ErrorController
app.UseStaticFiles();
app.UseRouting();
// ... other middleware
app.UseBrokenLinkArchive(); // Process outgoing links in responses (ONLY for middleware)
प्रवाह इस तरह दिखता है:
flowchart LR
subgraph Request["Request Processing"]
direction TB
A[Request] --> B[Static Files]
B --> C[Routing]
C --> D{Page exists?}
D -->|Yes| E[MVC/Endpoints]
D -->|No| F[ErrorController 404]
F --> G{Auto-redirect?}
G -->|Yes| H[Redirect]
G -->|No| I[Show suggestions]
end
subgraph Response["Response Processing"]
direction TB
E --> J[BrokenLinkArchiveMiddleware]
J --> K[Link Extraction]
K --> L[Link Replacement]
L --> M[Response]
end
subgraph Background["Background Processing"]
direction TB
N[BrokenLinkCheckerService] --> O[Check URLs]
O --> P[Fetch Archive.org]
P --> Q[Update Database]
end
K -.->|Register links| N
style F stroke:#ef4444,stroke-width:3px
style J stroke:#8b5cf6,stroke-width:3px
style N stroke:#f59e0b,stroke-width:3px
यह तंत्र अब कुछ समय के लिए चल रहा है और यह संतोषजनक रूप से देखने के लिए चल रहा है. डाटाबेस धीरे से त्वरित फिल्टरों के साथ भरता है, टूटे लिंक अभिलेख मिलता है, और उपयोगकर्ता कम से कम 404 अब देखने के लिए देखते हैं.
मुख्य सिद्धांत:
यह सिद्ध नहीं है - कुछ कड़ियों को सच में हमेशा के लिए चला गया है, और इमानिक खोज हमेशा सही जगह नहीं पाती। लेकिन यह एक लानत दृष्टि है हजारों टूटे कड़ियों के बारे में झूठ बोलने के लिए।
इस सप्ताह (W/CHEHEX नवंबर 2025) मैं अपनी खोज श्रृंखला को प्रकाशित किया जाएगा जो और अधिक विस्तार में चला जाता है कि कैसे सदिश-आधारित खोज अंत के अंतर्गत काम करता है। यह श्रृंखला पीढ़ी, Qavid वेक्टर भंडारण, और कैसे यह सभी संबंधों को नियंत्रित करता है इस 404 सिस्टम में। यदि आप के बारे में सोच रहे हैं, तो आप के बारे में सोच रहे हैं कि कैसे के बारे में सोच रहे हैं। ISemanticSearchService.SearchAsync वास्तव में "विष्ट" सामग्री, कि आप देखना चाहते हैं कि जहां है.
पूरा स्रोत भंडार में है यदि आप किसी का उपनाम अपने परियोजना के लिए करना चाहते हैं.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.