RedirectToAction is changing the URL - c#

My call to RedirectToAction is acting like RedirectToActionPermanent. That is, the URL is being changed, rather than simply displaying a different view.
Edit: Now that I think about it, RedirectToAction typically acts as a permanent redirect. As in, this is probably the correct behavior. In the below code, if the ModelState is valid, the user is given a 302 redirect back to the index. But then, what's the point of RedirectToActionPermanent?
The redirects are for HTTP errors. I have my Web.config set to point errors to certain action methods in HttpErrorsController. This works perfectly, including showing a temporary redirect, as expected. (https://localhost/ThisPageDoesntExist shows error page but the URL remains the same)
Returning an HttpStatusCodeResult or throwing an HttpException both work as expected.
However, if I try to do a temporary redirect to an error action method by using RedirectToAction, the view is still displayed properly, but the URL changes, e.g. https://localhost/HttpErrors/404.
HttpErrorsController.cs
private ViewResult ErrorView(HttpStatusCode httpStatusCode, string shortDesc, string longDesc)
{
Response.StatusCode = (int)httpStatusCode;
return View("HttpError", new HttpErrorViewModel(httpStatusCode, shortDesc, longDesc));
}
[ActionName("404")]
public ActionResult Error404()
{
return ErrorView(HttpStatusCode.NotFound, "Not Found",
"The requested resource could not be found.");
}
// Other identical methods for each error
ItemController.cs
public ActionResult HttpError(HttpStatusCode status)
{
return RedirectToAction(((int)status).ToString(), "HttpErrors");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ItemViewModel viewModel)
{
if (!Request.IsAjaxRequest())
{
return HttpError(HttpStatusCode.NotAcceptable);
}
if (ModelState.IsValid)
{
db.Items.Add(pm);
db.SaveChanges();
return RedirectToAction("Index");
}
return PartialView("_Create", viewModel);
}
Since writing the above, I've realized I'm probably better off just throwing an HttpException, so that it also gets caught by ELMAH, but I'm still quite confused by the behavior described above.

RedirectToAction method sends a 302 response back to the browser with the location header value which the new url and the browser will make a totally new http GET request to this new url. So what you see is the expected behavior.
If you want not do the redirect, but want to keep the url as it is, do not do return a RedirectResult, return a view result as needed.
RedirectToActionPermanent method sends a 301 Moved Permanently response back to the client. This is usually useful when you are moving one page to another (killing an old page and creating a new one with different url) of your site and wants the client to know it so that they can the calling code to use the new url in future. Think about google search engine changing the links to your new page and showing that in the search result.

Related

Web API redirect to web page

I have a Web API that I need to convert to API Core 2. I have one issue I cannot seem to resolve. There is a redirect in the controller that uses Request.CreateResponse which does not appear to be available in .Net Core?
public HttpResponseMessage Get() {
var response = Request.CreateResponse(HttpStatusCode.Redirect);
response.Headers.Location = new Uri("https://xxx.xxxx.com");
return response;
}
Any idea how I can modify this to work? Basically if a user does not pass any parameters, we just send them to a web page.
The syntax has changed in ASP.Net-Core. HttpResponseMessage is no longer used in action results. They will serialized like any other model returned object.
Use instead, the Controller.Redirect method which returns a RedirectResult
public IActionResult Get() {
return Redirect("https://xxx.xxxx.com");
}
RedirectResult
RedirectResult will redirect us to the provided URL, it doesn’t matter if the URL is relative or absolute, it just redirect, very simple. Other thing to note is that it can redirect us temporarily which we’ll get 302 status code or redirect us permanently which we’ll get 301 status code. If we call the Redirect method, it redirect us temporarily...
Reference Asp.Net Core Action Results Explained
That's what the Redirect() method exactly does:
return Redirect("https://xxx.xxx.com");
But if you want more control, you can return a ContentResult:
response.Headers.Location = new Uri("https://xxx.xxx.com");
return new ContentResult {
StatusCode = (int)HttpStatusCode.Redirect,
Content = "Check this URL"
};
I don't see any benefit for doing this, though. Well, unless if you use some client that will not follow the redirect and you want to provide some instructions or content in the Content property, which the client will see as the body of the returned page.
You can use HttpResponseMessage instead, something like:
var response = new HttpResponseMessage(HttpStatusCode.Redirect);
response.Headers.Location = new Uri("https://insight.xxx.com");

asp.net MVC controller - What should controller return if user is unauthorized?

So I have this code inside the controller of my MVC for a page
[HttpGet, Route]
[Authorize(nameof(Access))]
public async Task<ActionResult> ListStuff()
{
var canRead = HasAccess()
if(!canRead)
{
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
}
I'm using C# attributes for security validation and what I want is if the attribute with the 'HasAccess()' function returns false then the page show show an 'unauthorized' error, as you guys can see I tried throwing an HttpResponseException, I'm pretty sure this isn't the proper way to do it. Any suggestions?
You send a GET http request to your running service, eg:http://localhost:8080/Myservice.svc/$metadata. I've used postman in the past to help with sending http requests.
Link to free postman

Images from http site on https site: mixed mode

My https-based site (site A) uses images from http-based site B. I causes mixed-content error. To fix this, I found solution to swap each external link like http://www.siteB.com/imageX.png with my controller method which do forward to external image. The new link format is:
The code of method /api/misc/forward is following:
[HttpGet]
public async Task<HttpResponseMessage> Forward(string url)
{
HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
try
{
var response = Request.CreateResponse(HttpStatusCode.Found);
response.Headers.Location = new Uri(HttpUtility.UrlDecode(url));
return response;
}
catch (Exception ex)
{
httpResponseMessage.StatusCode = HttpStatusCode.NotFound;
_loggerService.LogException(ex, url);
}
return httpResponseMessage;
}
but the browser still is able to recognize it as mixed mode.... Why?
The original image links sent to browser origins from https-based site.
Any quick tip for it? I dont want to cache all images from site B:).
Because your code sends back a redirect to another location, so, eventually, the browser still go to the HTTP image.
What happens is that your browser calls the controller in HTTPS, then controller action sends back a redirect command to the browser, the browser retrieves the image from the new location that you set in the response.Headers.Location.
If you want to avoid the mixed mode, then you need to retrieve the image from the controller and return a FileResult from the action, this way, the browser will not have to access the HTTP site.
Another approach, would be to just copy the images to you site.

HttpStatusCodeResult(401) returns "302 Found"

Using ASP.NET MVC 5, I would like to return appropriate HTTP status code for different scenarios (401 for user is not authenticated, 403 when user has no right for some resource, etc.), than handle them in jQuery.
But the problem is, when I try to return 401, it always returns "302: Found". What is the trick for a custom status code, and why this doesn't work?
public ActionResult My()
{
if (User.Identity.IsAuthenticated == false)
{
return new HttpStatusCodeResult(401, "User is not authenticated.");
// Returns "302: Found"
}
// ... other code ...
}
EDIT 1: Interesting bit:
If I replace the 401 with a 404 like this:
return new HttpNotFoundResult("User is not authenticated.");
Then it indeed gives a 404 and jQuery can catch the problem. However it's not an elegant solution as the error code is different.
EDIT 2: 302 is not good for me, as the result would be used in jQuery.get().fail(), but 302 won't triger fail()
Lol this is an awesome problem
The way auth works in MVC is that when you aren't logged in and try to access a secure page it throws a 401 exception. MVC then catches this exception and redirects the user to the login page (which is the 302 you are seeing)
I suppose there's a few things you can do to fix it:
Ignore it, its probably the behaviour you want anyway
Disable login page redirection (phil haack has a good article on this here: http://haacked.com/archive/2011/10/04/prevent-forms-authentication-login-page-redirect-when-you-donrsquot-want.aspx)
EDIT
As per your comments, the following code will turn all redirects into 401s when requested via ajax. This is one approach for avoiding the issue listed
public class MvcApplication : HttpApplication {
protected void Application_EndRequest() {
var context = new HttpContextWrapper(Context);
// If we're an ajax request, and doing a 302, then we actually need to do a 401
if (Context.Response.StatusCode == 302 && context.Request.IsAjaxRequest()) {
Context.Response.Clear();
Context.Response.StatusCode = 401;
}
}
}

TempData and Fiddler

public ActionResult Index()
{
TempData["msg"] = "Test";
return RedirectToAction("About");
}
public ActionResult About()
{
var msg = TempData["msg"];
return View();
}
A simple question. I am sure I will slap my forehead when I see the answer to it.
Navigating to the Index action in the browser results in a redirect to the About action and the TempData value is correct.
Why when I navigate to the Index action using the Fiddler composer it results in a redirect to the About action but the TempData value is lost and null?
I think the answer is found here (http://msdn.microsoft.com/en-us/library/ms178581(v=vs.100).aspx):
"Sessions are identified by a unique identifier that can be read by using the SessionID property. When session state is enabled for an ASP.NET application, each request for a page in the application is examined for a SessionID value sent from the browser. If no SessionID value is supplied, ASP.NET starts a new session and the SessionID value for that session is sent to the browser with the response."
When I add this line to the beginning of each Action:
Debug.Write(string.Format("SessionId: {0}\r\n", HttpContext.Session.SessionID));
I see that when you run from a browser the sessionid is the same. When run from the Fiddler composer they are different.
Therefore, TempData is going to be reset using the default TempDataProvider (which stores the TempData in session state).
If requests are the same than results should be the same. Most likely you are not making exact copy of first request when composing fake one. Note that in case of tempData your composed request will work (get tempData) only if it is the first request with this data - so you have to make "copy" of request that is not yet send by application, you can't replay ones that rely on tempData.
The temp data be stored in the session state and wiped out after first request, so as result it will be invalid/missing if you are not correctly sending it information hand/via Fiddler composer OR (as in your case) making second request with the same information to the same controller.
See also other related questions on the same topic.

Categories