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
पुराने चीजों को फिर से काम करने के लिए 404: पर युद्ध
Sunday, 23 November 2025
परिचय
जब आप 'ब्लैगिंग कर रहे हैं तब से |2004 | | ( | हाँ | МSK3 | सचमुच ), | आप बहुत सी डिजिटल अवशेषों को एकत्रित करते हैं | सब कुछ टूट गया था. बाहरी लिंक जो एक दशक पहले लुप्त साइटों की ओर संकेत करता है
नोट: यह शीघ्र आने वाला है, साथ ही सेमेटिक खोज श्रृंखला और ब्लॉग पर कार्यात्मकता के साथ-साथ M SK2 बस एक बड़ा परिवर्तन था और मैं पहले बाहर निकलने के लिए कुछ छोटे लेख थे
समस्या तीन भागों में विभाजित होती है
आंतरिक लिंक - अपने उपयोग से आयात प्रक्रिया के दौरान स्थिर ArchiveOrg आयातक औज़ार. मेरे पुराने पोस्टों ने एक-दूसरे को पुराने यूआरएल योजना के उपयोग से संदर्भित किया था।
बाहरी लिंक (outgoing यह एक बड़ा है।
आने वाले अनुरोध लोग अभी भी पुराने यूआरएल को पहुँचने की कोशिश करते हैं जैसे
/archive/2006/05/15/123.aspxअर्थ खोज प्रणाली अक्सर यह पता कर सकती है कि वे क्या थे
अब हो सकता है आयात प्रक्रिया में आरजीओआरजी की खोजें कर रहे हैं। भविष्य विच्छेदन, केवल उन नहीं जो आयात समय में मौजूद थे
इस लेख में मेरा दृष्टिकोण शामिल है
- बाहर जा रहे लिंक:
BrokenLinkArchiveMiddleware- मृत बाहरी लिंक को अभिलेखागार के साथ प्रतिस्थापित करता है - इनकमिंग लिंक: semantic खोज के साथ 404 ह्यान्डलर , सही सामग्री को पुराने यूआरएल योजनाओं से भी पाता है
- सीखने की प्रणाली: कैसे साइट समय के साथ उपयोगकर्ता क्लिकों से अधिक स्मार्ट हो जाता है
- पृष्ठभूमि संसाधन: अनुरोधों को अवरोधित किए बिना लिंक जाँचना
पुरानी सामग्री के साथ समस्या
यहाँ-एमएसके0 इंटरनेट के बारे में एक बात है-एम एसके1 यह-एम ایسके2 स्थायी नहीं है-म एसके3 उस उत्कृष्ट ब्लॉग पोस्ट को जो आप ने लिंक किया था।
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
लेकिन जब आप को सैकड़ों पोस्ट्स हजारों लिंक के साथ मिलते हैं तो यह नहीं है कि
वास्तुकला की समीक्षा
प्रणाली के दो मुख्य घटक tandem में काम करते हैं
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
भाग 1: निर्गमन लिंकों को संभालना
BrokenLinkArchiveMiddleware
यह मिडलवेयर एचटीएमएल प्रतिक्रियाओं को अवरोधित करता है और तीन बातें करता है
- पृष्ठभूमि जांच के लिए सभी लिंक निकालता है
- archive.org संस्करणों के साथ ज्ञात टूटी बाहरी लिंक को बदलता है
- टूटे हुए आंतरिक लिंक के लिए प्रतिस्थापन खोजने के लिए सेमेटिक खोज का उपयोग करता है
यहाँ मुख्य अंतर्दृष्टि यह है कि हम अभिलेखागार.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] में विशेषता .NET हमें compile देता है
[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();
}
अब आप काफी युक्तियुक्त रूप से उपयोग कर सकते हैं एचटीएमएल एजिल्लिटी पैक यहाँ अधिक जटिल परिदृश्यों के लिए पैर्सर है लेकिन यह इस उद्देश्य के लिए अधिkill था.
पृष्ठभूमि जांच के लिए पंजीकरण लिंक
हम लिंक की जाँच करते समय प्रतिक्रिया को अवरोधित नहीं करना चाहते हैं
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 हमें एक नया स्कोप की जरूरत है क्योंकि मूल अनुरोध
महत्वपूर्ण: यह एक है अंततः सुसंगतता मॉडल. जब कोई भी लिंक पहले खोजा गया है, यह वैधता के लिए कतार में लाया जाता है | - | हम नहीं जानते हैं |' | यह पता नहीं है कि क्या यह | МSK3 | अभी टूटी है \ . अगला उस पृष्ठ की आगन्तुक स्थिर लिंक देखेगा. नियमित ट्रैफिक के साथ एक ब्लॉग के लिए।
टूटे लिंक को बदलना
एक बार जब हम जानते हैं कि एक लिंक टूटा है और एक अभिलेखागार .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);
भाग 2: पृष्ठभूमि लिंक जांच
मेड्यूलवेयर अनुरोध के दौरान लिंक जाँच नहीं करता है - जो बहुत धीमी हो सकता है | . | इसके बजाय यह एक डाटाबेस टेबल से पृष्ठभूमि प्रक्रमण के लिए लिंक खोजने की कतारें बनाता है BrokenLinkCheckerBackgroundService वास्तविक जाँच को संभालता है
यह सेवा प्रति घंटे चलती है और दो बातें करती है
- लिंक वैधता जाँचें - हेड अनुरोध करता है कि देखें कि क्या लिंक अभी भी जीवित हैं
- अभिलेख लाएँ.org URLs - टूटे हुए लिंक के लिए
हम इस बारे में अच्छे नागरिक हैं।
request.Headers.UserAgent.ParseAdd(
"Mozilla/5.0 (compatible; MostlylucidBot/1.0; +https://www.mostlylucid.net)");
यह साइट प्रशासकों को दिखाता है कि क्या उनके सर्वर पर hammering करता है, , और वे हमें ढूंढ सकते हैं यदि वे उत्सुक हैं.
महत्वपूर्ण है , लिंक हैं आवधिक रूप से पुन: जांचाएक लिंक जो पिछले सप्ताह काम कर रहा था आज मुर्दा हो सकता है।
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
Archive.org CDX API
अभिलेखागार.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 में एक पोस्ट लिखता हूँ, तो मैं ,\I'\अर्काइव को प्राप्त करूँगा \ .\org स्न्यापसॉट के आसपास से \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);
}
इसका अर्थ होता है कि प्रत्येक लिंक को प्रतिदिन कम से कम एक बार पुनः वैध किया जाता है। ConsecutiveFailures क्षेत्र हमें एक बिट क्षमा करने के लिए छोड़ देता है - हम नहीं कर रहे हैं' एक अस्थायी त्रुटि के बाद विच्छेदित के रूप में लिंक चिह्नित
भाग 3: आने वाले अनुरोधों को नियंत्रित करना
अब सिक्के के दूसरी ओर लोगों के लिए, जो URLs की मांग कर रहे हैं जो मौजूद नहीं है
- पुराना यूआरएल योजनाएँ - मेरी प्राचीन ब्लॉग ने ऐसे पथों का उपयोग किया है
/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/*यह प्रत्येक पृष्ठ दृश्य पर डाटाबेस क्वेरी है।
अधिक बेहतर दृष्टिकोण : हैक में हैक करने के लिए \404 हैंडलर |. | यदि ASP | . | NET ने पहले से ही पृष्ठ को निर्धारित किया है नहीं है तब हम पुनर्निर्देश के लिए जाँचें. वैध पृष्ठों पर कोई व्यर्थ प्रश्न नहीं
मजेदार तथ्य: ASP के आरंभिक दिनों में इस तरह हम दोस्ती यूआरएल बनाते थे काम करता था.Scott Guthrie'MVC शुरू करने वाले विमान डेमो का उपयोग इस दृष्टिकोण से किया गया;IIS में चिपका हुआ404प्रचालन प्रणाली,यूआरएल पढ़ा और सही सामग्री प्रदान करें.यह ALSO था जो मैं एक सिस्टम में मैंने वेबफॉर्म्स में बनाया थाUsered
सीखने की प्रणाली
जब कोई एक 404, चिपकता है तो हम उन्हें fuzzy string matching का उपयोग करके सुझाव दिखाते हैं और (optionally) semantic search के प्रयोग से.
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 --> [*]
404 ह्यान्डलर
सभी पुनर्निर्देशन तर्क में रहता है ErrorControllerASP 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 विधि learned redirects और उच्च को संभालता है
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
}
समानता अंकन
सिफारिश सेवा लेवेंस्टेन दूरी का उपयोग करता है (edit दूरी) साथ ही कुछ हियूरिसिक्स
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;
}
}
दो-Tier स्वचालित
हमारे पास स्वचालित पुनर्निर्देश के दो स्तर हैं
पहला-समय उच्च: यदि समानता प्राप्ताङ्क है, तो यह एक स्पष्ट प्रकार्य है।
**सीखा पुनर्निर्देशन (301)**के बाद हम एक स्थायी 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 को पुनर्निर्देशन की आवश्यकता हो सकती है
// 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
निष्कर्ष
यह सिस्टम कुछ समय से चल रहा है और यह है
प्रमुख सिद्धांत
- Don't ब्लॉक अनुरोध - सस्ते ऑपरेशन के लिए पृष्ठभूमि प्रक्रमण का उपयोग करें
- उपयोगकर्ताओं से सीखें - उनके क्लिक मूल्यवान संकेत हैं
- auto-redirects के साथ संरक्षित होना - सिर्फ फिर से निर्देशित करें जब आप
- समय---आर्काइविंग से अवगत @info: whatsthis
- अनुग्रही अवनति - यदि सेवाएं उपलब्ध नहीं हैं तो
कुछ लिंक सचमुच हमेशा गायब हो जाते हैं और अर्थ खोज में कभी भी सही प्रतिस्थापन नहीं मिलता है. लेकिन यह एक भयानक दृश्य बेहतर है कि हजारों टूटे हुए लिंक के बारे में झूठ छोड़ने से
निकट आ रहा है: सेमेटिक खोज गहरी डुबो
इस सप्ताह (\w/\c\24\November\3\I\MSC4\will publishing my semantic search series which goes into much more detail about how the vector\M SK5\based search works under the hood\MST6\ That series will cover embedding generation\MSP7\ Qdrant vector storage\M SP8\ and how it all ties to this \Msp9\ handling system\MSp10\ If you\MSSP11\are curious about how ISemanticSearchService.SearchAsync वास्तव में ढूंढता है "similarMSC1 content, thatM SK3s where youMST4ll want to look
संपूर्ण स्रोत रिपोजिटरी में है यदि आप अपने परियोजनाओं के लिए इसका कोई भी nick करना चाहते हैं