Sending a 410 Gone header, then redirecting - c#

I've got a page that checks if a user is logged in or the file is public then pushes a PDF to the browser via Response.WriteFile.
Works great except when Google indexes a file and then we remove the file. So I'm looking at adding a 410 Gone to the Response.Status and then redirecting to our error.aspx page.
Firebug tells me that it gets a "302 Found" status code on the document page when a file is deleted. I'm expecting a 410.
Redirect code is:
Response.Status = "410 Gone";
Response.AddHeader("Location", Request.Url.ToString());
Response.Redirect("error.aspx");
Could someone please tell me what I'm getting wrong please?

Redirection is done by sending a status that indicates that the resource is available somewhere else, such as 301 Moved Permanently or 302 Found. You can't send two status codes in the same response. Either the requested resource does not exist (so you send 410) or it does exist at some other location (so you send 301 or 302 or whatever).
I don't think you should be redirecting to an error page, though, because an error message isn't a separate resource that should have its own URL. If a client requests a file and the file is gone, send a 410 Gone status with the error message as the response body — that way the error message comes back from the URL of the nonexistent file itself. A search engine will see the status code and understand that the file is gone, and a browser will show the response body to the user so he can read the error message.

If you look at the spec for 410 Gone, it states that "no forwarding address is known", so a redirect does not seem valid. You can return that same body on that response that you would from error.aspx if you want human users to see something.
The requested resource is no longer available at the server and no
forwarding address is known. This condition is expected to be
considered permanent. Clients with link editing capabilities SHOULD
delete references to the Request-URI after user approval. If the
server does not know, or has no facility to determine, whether or not
the condition is permanent, the status code 404 (Not Found) SHOULD be
used instead. This response is cacheable unless indicated otherwise.
The 410 response is primarily intended to assist the task of web
maintenance by notifying the recipient that the resource is
intentionally unavailable and that the server owners desire that
remote links to that resource be removed. Such an event is common for
limited-time, promotional services and for resources belonging to
individuals no longer working at the server's site. It is not
necessary to mark all permanently unavailable resources as "gone" or
to keep the mark for any length of time -- that is left to the
discretion of the server owner.

You can also use this way, first change your desire status.
Response.Status = "410 Gone";
Response.AddHeader("Location", Request.Url.ToString());
ScriptManager.RegisterStartupScript(this, this.GetType(), "redirectScript", "window.location.href=error.aspx", true);`
So, in that you get your desire page and status as well.

This is how I had to do a 301 moved permantently response. It should be similar
//in Global.asax.cs
protected virtual void Application_BeginRequest (Object sender, EventArgs e)
{
if(Request.Url.Host=="www.earlz.biz.tm" || Request.Url.Host=="earlz.biz.tm" || Request.Url.Host=="www.lastyearswishes.com"){
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location","http://lastyearswishes.com"+Request.Url.PathAndQuery);
CompleteRequest(); //I believe this is the missing piece in your code.
}
}

I have been solving similar issue. If the page with resource is removed from the web, I woul like to tell Google Bot 410 Gone, remove from cache, but I would like to offer an alternative similar page to the visitor.
I have solved it like this:
public ActionResult RealEstate(int id, string title)
{
...prepare the model
if (realEstateModel.Result.OfferState == OfferState.Deleted)
{
var alternativeSearchResult = PrepareAlternative(realEstateModel);
return Gone410(alternativeSearchResult, context);
}
else
return View(realEstateModel);
}
Gone410.cshtml look slike this:
#model Bla.ModelGone410
#{
Layout = null;
Html.RenderAction("Index", "Search",
new
{
type = Model.type,
Category = Model.Category,
city_id = Model.city_id,
...
});
}
and RealEstate.cshtml:
#model Bla.realEstateModel
#{
Layout = null;
}
This is realestate view...
This gives the response 410 to google bot and search alternative for the user with no redirect.

Related

What is the difference between Responce.Redirect and Response.RedirectLocation?

If I redirect a page using Response.RedirectLocation ="login.aspx" then how it is different form Responce.Redirect("~/login.aspx")?
Thanks
Response.Redirect(url); Causes a 302 redirect (A temporary redirect) and closes the response causing the user browser to be redirected to the alternative URL.
If you want to permanently update a URL, for example you have made changes in the URL structure and you wish to let the world know that this is a permanent change (ie. search engines to pass on the ranking from the old URL to the new), then this should be indicated via a 301 Permanent Redirect.
This can be done as follows:
Response.Status = "301 Permanent Redirect";
Response.StatusCode = 301;
Response.RedirectLocation = "http://newurl.com/";
Response.End();
Note: Response.RedirectLocation does not stop processing of preceding code (unlike Response.Redirect()). This means that unless you want to continue processing tasks, you need to close the connection.
To summarise:
Response.Redirect() always causes a 302 temporary redirect, and halts processing
Response.RedirectLocation allows more control, for example setting the correct status codes, or continue further processing if required.
RedirectLocation sets the Location HTTP header. Redirect does so much more and completes the response with a 3xx status code.

File download via Webapi controller - handling errors

I am working on an application that provides some links for users to download files.
The page itself is served up by an MVC controller but the links are pointing to a WebAPI controller running on a separate domain.
(I would have preferred same domain but for various reasons it has to be a separate project and it will run on a separate domain. I don't think CORS is part of the issue anyway as this is not using XHR, but I mention it just in case).
So in development, the main MVC project is http://localhost:56626/Reports/
And the links on the page might look like this:
Report 12345
where port 51288 is hosting the Web API.
The WebAPI controller uses ReportID to locate a file, and write its contents into the response stream, setting the disposition as an attachment:
//security.permission checks and scaffolding/database interaction
//left out for clarity
try
{
string filename = #"C:\ReportFiles\TestReport.csv";
var stream = new FileStream(path, FileMode.Open);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
var disp = new ContentDispositionHeaderValue("attachment");
disp.FileName = "TestReport.csv";
result.Content.Headers.ContentDisposition = disp;
return result;
}
catch(Exception ex)
{
//how to return a response that won't redirect on error?
}
By doing this the user can then click on the link and without any redirection, the user gets prompted to save or open the file, which is what I want; they stay on the original page with the links and just get an Open/Save dialog from the browser.
The problem arises when something goes wrong in the Web API controller - either an exception or some internal logic condition that means the file cannot be downloaded.
In this case when clicking the link, the download doesn't happen (obviously) and they get taken to the target URL instead i.e http://localhost:51288/api/ReportDownload?ReportID=12345 which is not desirable for my requirements.
I would much rather be able to catch the error somehow on the client-side by returning for e.g. HTTP 500 in the response, and just display a message to the user that the download failed.
Now to be honest, I don't even understand how the browser can do the "in place" File/Save dialog in the first place:
I always thought if you click a link that has no explicit target attribute,the browser would just open the new request in your current tab i.e it's just another GET request to the target URL, but it seems this is not the case
The browser seems to be doing a hidden background fetch of the target URL in this case (same behaviour in FF,Chrome and IE) which I cannot even see in the F12 tools.
The F12 Network log shows no activity at all except in the specific case where the response has NOT been setup as Content-Disposition: attachment i.e an error -only in this case do I see the (failed) HTTP GET being logged in the Network request list.
I suppose I could just catch any exception in the controller and send back a dummy file called "Error.csv" with contents "Ha Ha Nope!" or something similar, but that would be a last resort...any ideas welcome!
If the user clicks on the link, the browser will follow it - then depending on the response headers and browser configuration, it'll either show the file dialog or render directly - you can't really change that behavior (apart from using preventDefault when the link is clicked, which kind of defeats the purpose).
I'd suggest taking a closer look at http://jqueryfiledownload.apphb.com/ which lets you do something like this:
$.fileDownload('some/file/url')
.done(function () { alert('File download a success!'); })
.fail(function () { alert('File download failed!'); });
Then you could bind the download action using jQuery.

AsyncCallback tied to a WebRequest is never reached

I have an ASPX page which should retrieve some content (some plain text data) asynchronously, and write something before/during/after the operation.
Currently, I can reach the "during" step but page content doesn't change anymore afterwards.
Big issue is I cannot perform any kind of debugging due to infrastructure (mis)configuration and not being allowed to run Remote Debugging Tools, I have to rely on publishing and see what happens...
Code behind looks like this (This is a .NET 3.5 (changing target framework is not an option) project created under VS2008 and later upgraded to VS2010)
void Page_Load()
{
myLabel = "Preparing to fetch content ...";
FetchContent();
}
void FetchContent()
{
try {
// "http://myUrl" returns text with header 'Content-disposition: inline;'
// If called directly, Text can be seen in the browser alright.
WebRequest request = WebRequest.Create("http://myUrl");
myLabel = "Fetching ...";
request.BeginGetResponse(new AsyncCallback((result)=>
{
//EXCEPTION HERE: 401 Unauthorized ??? url works via browser!
WebResponse resp = request.EndGetResponse(result);
StreamReader stream = new StreamReader(resp.GetResponseStream());
myLabel = "Done";
}
} catch {myLabel = "Request KO"; }
}
In the ASPX code, myLabel is simply shown:
<body>
<pre><%=myLabel %></pre>
</body>
The url responds fairly quickly if called from a browser, but in this code myLabel never shows Done., it stays on the Fetching... text like the callback is never fired.
Am I missing something obvious here ?
UPDATE
Closer inspection revealed that EndGetResponse returns a 401 Unauthorized status code. It works flawlessly if I invoke the exact same url via a browser though ! Some now more focused searching got me the solution now.
After finding out the 401 Unauthorized status code in the response, I managed to find other answers right here on SO which made me solve my (as it turns out) trivial issue adding this:
request.UseDefaultCredentials = true;

ASP.NET How to do a HTTP 301 to new domain with path and query intact

How can I do redirect all incoming requests from one domain to another and still keep the path and query?
Example
from: http://domain1.com/some/path/?query to: http://domain2.com/some/path/?query.
I've been fooling around with the system.webserver in web.config, HTTP-handlers and global.asax - but without luck. I only get 404s (because the content has been moved)...
Thanks!
Use Request.RawUrl and replace domain1 with domain2 when you do the redirect.
From the Remarks section:
The raw URL is defined as the part of the URL following the domain
information. In the URL string
http://www.contoso.com/articles/recent.aspx, the raw URL is
/articles/recent.aspx. The raw URL includes the query string, if
present.
Update:
This:
protected void Application_BeginRequest(object sender, EventArgs e)
{
Response.Redirect("http://www.google.com" + Request.RawUrl);
}
Definitely works as you need. You may get a 404 error but that's just because the path part of the Url does doesn't exist on domain2 (google.com on the example above). That's something you should be able to predict/correct or simply not worry. I don't know what your requirements are.
You can put in a redirect in IIS & skip loading ASP.NET code all together.
In the 'HTTP redirect' section of IIS for domain1.com, set the redirect location to:
http://domain2.com/$S$Q
Then check the 'Redirect all requests to exact destination(instead of relative destination)' checkbox.
All requests including folders, files & querystring parameters will get passed to the new domain.
Edit:
Since you don't have access to IIS, you can use the Request.RawUrl method as Icarus described.
To avoid the 404 error, you can check if the page exists before you redirect:
string domain2 = "domain2.com" + Request.RawUrl;
try
{
// *** Establish the request
HttpWebRequest loHttp = (HttpWebRequest)WebRequest.Create(domain2);
// *** Set properties
loHttp.Timeout = 10000; // 10 secs
// Retrieve request info headers
HttpWebResponse loWebResponse = (HttpWebResponse)loHttp.GetResponse();
loWebResponse.Close();
Response.Redirect(domain2); //Page is valid..redirect to it.
}
catch ( WebException ex )
{
if ( ex.Status.Message.Contains("404") ) //or check that the StatusCode property is 404 or similar
Response.Redirect("www.domain2.com" //Redirect to front page since page doesn't exists
}

Response.Redirect HTTP status code

Why is it that ASP/ASP.NET Response.Redirect uses a HTTP-302 status code ("Moved Temporarily") even though in most cases a HTTP-301 status code ("Moved Permanently") would be more appropriate?
Responses with status 301 are supposed to be cacheable, and I don't think you want that behavior for most ASP/ASP.NET redirects.
ASP.NET 4.0 is has the RedirectPermanent method for that if needed.
I've used this handy Permanent Redirect with success:
public void RedirectPermanent(string newPath)
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.Status = "301 Moved Permanently";
HttpContext.Current.Response.AddHeader("Location", newPath);
HttpContext.Current.Response.End();
}
One common use case of Response.Redirect is to move the user to another page in server-side code after a postback, e.g. something along the lines of
private void MyButton_Click(object sender, EventArgs e)
{
if (some condition) {
Response.Redirect("ShowProduct.aspx");
} else {
Response.Redirect("SorryOutOfStock.aspx");
}
}
In those cases, 301 would be completely wrong. In fact, I think that the above case (conditionally move the user to another page after some UI interaction) is a much more common use of Response.Redirectthan a real this-page-moved-to-another-URL-forever scenario (where a return code of 301 would be appropriate).
In addition to the answer from Heinzi, the only entity on the web that is likely to take much notice of the 301 would be the search engines. Most browsers will not track and record 301 in order automatically redirect any subsequent request for the initial URL. Browsers treat 301 identically to how they treat 302. Hence 302 in dynamic content such as generated in ASP.NET is quite appropriate.
The error you are getting is not due to response.redirect !
The HTTP response status code 301 Moved Permanently is used for permanent redirection, meaning current links or records using the URL that the 301 Moved Permanently response is received for should be updated to the new URL provided in the Location field of the response.

Categories