8

I have simple url rewriter:

    private static void RedirectToAPI(RewriteContext context)
    {
        var request = context.HttpContext.Request;
        if (request.Path.Value.StartsWith("/apiendpoint", StringComparison.OrdinalIgnoreCase))
        {           
            var json = JsonConvert.SerializeObject(request.Path.Value
                .Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries)
                .Skip(1));
            var response = context.HttpContext.Response;

            response.Headers[HeaderNames.Location] = $"/custom";
            response.StatusCode = StatusCodes.Status301MovedPermanently;
            context.Result = RuleResult.EndResponse;
            using (var bodyWriter = new StreamWriter(response.Body))
            {
                bodyWriter.Write(json);
                bodyWriter.Flush();
            }
        }
    }

The problem is, when it redirects to /custom url, request has method GET, while this method require POST.

For example, send GET request to url /apiendpoint/first/second/third, then rewriter responds to redirect, accordingly, the following request must be with method POST, but now, it is GET.

How can I change method of request, which is after url rewriter response?

15
  • Did you try request.Method = "POST"? Commented Jun 1, 2017 at 12:28
  • @Gururaj yes, nothing changes, still GET request. Commented Jun 1, 2017 at 12:33
  • request.Method = HttpMethod.Post;? Commented Jun 1, 2017 at 12:36
  • @Gururaj absolutely the same as the previous. However, request.Method changes method of incoming request before rewrite, while I need after. Commented Jun 1, 2017 at 12:41
  • Gosh!, How could I miss this - my apologies. Looking at your code I'm seeing you're not redirecting internally on the server but you're creating a response to redirect the request to another URI. Did you try using DelegatingHandler implementation and overriding SendAsync method to change the request method. This works unless you're not trying to redirect to external URI. Commented Jun 1, 2017 at 12:48

4 Answers 4

2

EDIT: Ah, just now checked the comments. If the initial request is a GET, then this won't work either and you can't tell the browser to POST. Not without returning a view that auto-executes a form with JavaScript.

You need to return a 308, not a 301.

Here is the changed code:

private static void RedirectToAPI(RewriteContext context)
{
    var request = context.HttpContext.Request;
    if (request.Path.Value.StartsWith("/apiendpoint", StringComparison.OrdinalIgnoreCase))
    {           
        var json = JsonConvert.SerializeObject(request.Path.Value
            .Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries)
            .Skip(1));
        var response = context.HttpContext.Response;

        response.Headers[HeaderNames.Location] = $"/custom";
        response.StatusCode = 308;
        context.Result = RuleResult.EndResponse;
        using (var bodyWriter = new StreamWriter(response.Body))
        {
            bodyWriter.Write(json);
            bodyWriter.Flush();
        }
    }
}

308 is a permanent redirect that requires the browser to preserve the method. https://httpstatuses.com/308

The temporary version is 307.

Convenience methods for these redirects are available in MVC Core 2.0.0-preview1.

Sign up to request clarification or add additional context in comments.

Comments

2

If using ASP.NET Core 2.0 there is RedirectPermanentPreserveMethod, you can read about it here. If this method is not supported you can try doing it manually.

1 Comment

with asp.net core 3.1 I am able to use RedirectPermanentPreserveMethod to post data.
1

This is a sample code

public class ConvertGetToPostHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // Add your logic here to decide if the request Method needs to be changed
        // Caution: This works only if you're redirecting internally
        request.Method = HttpMethod.Post;
        return await base.SendAsync(request, cancellationToken);
    }
}

You will have to add the Handler to Handler Pipeline as

config.MessageHandlers.Add(new ConvertGetToPostHandler());

Also, read over this documentation to get some insight into it's purpose and usage.

5 Comments

there is no DelegatingHandler in ASP.NET Core. Middlewares are used instead
Sorry mate. Looks looks like today is not my day. Sorry for wasting your time. I've not worked on Core yet :(
Check this reference Migrating Http Handler to Core middleware, if it helps
Your method is fine, although as previously mentioned, in Asp.Net Core there is Middleware, which is what I were looking for my purpose.
@YuriyN. Thanks for acknowledging my methodology. I've shared a reference earlier which describes migrating from Handler to Core Middleware. Please read the reference and see if that can be of any help to you.
1

Can you set @juunas answer as a correct answer, his answer solved it for me as you can see in my example code.

internal class RedirectCultureRule : IRule
{
    private const string CultureKey = "culture";

    public void ApplyRule(RewriteContext context)
    {
        HttpRequest httpRequest = context.HttpContext.Request;
        httpRequest.Query.TryGetValue(CultureKey, out StringValues cultureValues);
        string culture = cultureValues;

        if (cultureValues.Count > 0 && culture.IsCultureMatch())
        {
            context.Result = RuleResult.ContinueRules;
            return;
        }

        Dictionary<string, string> queryParts = new Dictionary<string, string>();
        NameValueCollection queryString = HttpUtility.ParseQueryString(httpRequest.QueryString.ToString());

        foreach (string key in queryString)
        {
            queryParts[key.Trim()] = queryString[key].Trim();
        }

        if (!queryParts.ContainsKey(CultureKey))
        {
            queryParts[CultureKey] = CultureInfo.CurrentCulture.Name;
        }

        string query = $"?{string.Join("&", queryParts.Select(qp => $"{qp.Key}={qp.Value}"))}";

        if (query.Length > 1)
        {
            httpRequest.QueryString = new QueryString(query);
        }

        string url = UriHelper.GetDisplayUrl(httpRequest);

        HttpResponse httpResponse = context.HttpContext.Response;
        httpResponse.StatusCode = 308;
        httpResponse.Headers[HeaderNames.Location] = url;

        using (StreamReader requestReader = new StreamReader(httpRequest.Body))
        {
            using (StreamWriter responseWriter = new StreamWriter(httpResponse.Body))
            {
                string body = requestReader.ReadToEnd();
                responseWriter.Write(body);
                responseWriter.Flush();
            }
        }

        context.Result = RuleResult.EndResponse;
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.