There's a lot of information for building Uris from Controller and Action names, but how can I do this the other way around?
Basically, all I'm trying to achieve is to get the Controller and Action names from the referring page (i.e. Request.UrlReferrer). Is there an easy way to achieve this?
I think this should do the trick:
// Split the url to url + query string
var fullUrl = Request.UrlReferrer.ToString();
var questionMarkIndex = fullUrl.IndexOf('?');
string queryString = null;
string url = fullUrl;
if (questionMarkIndex != -1) // There is a QueryString
{
url = fullUrl.Substring(0, questionMarkIndex);
queryString = fullUrl.Substring(questionMarkIndex + 1);
}
// Arranges
var request = new HttpRequest(null, url, queryString);
var response = new HttpResponse(new StringWriter());
var httpContext = new HttpContext(request, response)
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
// Extract the data
var values = routeData.Values;
var controllerName = values["controller"];
var actionName = values["action"];
var areaName = values["area"];
My Visual Studio is currently down so I could not test it, but it should work as expected.
To expand on gdoron's answer, the Uri class has methods for grabbing the left and right parts of the URL without having to do string parsing:
url = Request.UrlReferrer.GetLeftPart(UriPartial.Path);
querystring = Request.UrlReferrer.Query.Length > 0 ? uri.Query.Substring(1) : string.Empty;
// Arranges
var request = new HttpRequest(null, url, queryString);
var response = new HttpResponse(new StringWriter());
var httpContext = new HttpContext(request, response)
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
// Extract the data
var values = routeData.Values;
var controllerName = values["controller"];
var actionName = values["action"];
var areaName = values["area"];
To add to gdoran's accepted answer, I found that the action doesn't get populated if a custom route attribute is used. The following works for me:
public static void SetUpReferrerRouteVariables(HttpRequestBase httpRequestBase, ref string previousAreaName, ref string previousControllerName, ref string previousActionName)
{
// No referrer found, perhaps page accessed directly, just return.
if (httpRequestBase.UrlReferrer == null) return;
// Split the url to url + QueryString.
var fullUrl = httpRequestBase.UrlReferrer.ToString();
var questionMarkIndex = fullUrl.IndexOf('?');
string queryString = null;
var url = fullUrl;
if (questionMarkIndex != -1) // There is a QueryString
{
url = fullUrl.Substring(0, questionMarkIndex);
queryString = fullUrl.Substring(questionMarkIndex + 1);
}
// Arrange.
var request = new HttpRequest(null, url, queryString);
var response = new HttpResponse(new StringWriter());
var httpContext = new HttpContext(request, response);
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
if (routeData == null) throw new AuthenticationRedirectToReferrerDataNotFoundException();
// Extract the data.
var previousValues = routeData.Values;
previousAreaName = previousValues["area"] == null ? string.Empty : previousValues["area"].ToString();
previousControllerName = previousValues["controller"] == null ? string.Empty : previousValues["controller"].ToString();
previousActionName = previousValues["action"] == null ? string.Empty : previousValues["action"].ToString();
if (previousActionName != string.Empty) return;
var routeDataAsListFromMsDirectRouteMatches = (List<RouteData>)previousValues["MS_DirectRouteMatches"];
var routeValueDictionaryFromMsDirectRouteMatches = routeDataAsListFromMsDirectRouteMatches.FirstOrDefault();
if (routeValueDictionaryFromMsDirectRouteMatches == null) return;
previousActionName = routeValueDictionaryFromMsDirectRouteMatches.Values["action"].ToString();
if (previousActionName == "") previousActionName = "Index";
}
Here is a lightweight way to do this without creating response objects.
var values = RouteDataContext.RouteValuesFromUri(Request.UrlReferrer);
var controllerName = values["controller"];
var actionName = values["action"];
Uses this custom HttpContextBase class
public class RouteDataContext : HttpContextBase {
public override HttpRequestBase Request { get; }
private RouteDataContext(Uri uri) {
var url = uri.GetLeftPart(UriPartial.Path);
var qs = uri.GetComponents(UriComponents.Query,UriFormat.UriEscaped);
Request = new HttpRequestWrapper(new HttpRequest(null,url,qs));
}
public static RouteValueDictionary RouteValuesFromUri(Uri uri) {
return RouteTable.Routes.GetRouteData(new RouteDataContext(uri)).Values;
}
}
#gordon's solution works, but you need to use
return RedirectToAction(actionName.ToString(), controllerName.ToString(),values);
if you want to go to previous action
The RouteData object can access this info:
var controller = RouteData.Values["controller"].ToString();
var action = RouteData.Values["action"].ToString();
This is a method I made to extract url simplified from referrer because I had token (finished with "))/") in my URL so you can extract easily controller and action from this:
private static string GetURLSimplified(string url)
{
string separator = "))/";
string callerURL = "";
if (url.Length > 3)
{
int index = url.IndexOf(separator);
callerURL = url.Substring(index + separator.Length);
}
return callerURL;
}
I don't believe there is any built-in way to retrieve the previous Controller/Action method call. What you could always do is wrap the controllers and action methods so that they are recorded in a persistent data store, and then when you require the last Controller/Action method, just retrieve it from the database (or whatever you so choose).
Why would you need to construct ActionLink from a url ? The purpose of ActionLink is just the opposite to make a url from some data. So in your page just do:
var fullUrl = Request.UrlReferrer.ToString();
Back
Related
I'm request for get url :
public Uri GetAbsoluteUri()
{
var request = _httpContextAccessor.HttpContext.Request;
UriBuilder uriBuilder = new UriBuilder();
uriBuilder.Scheme = request.Scheme;
uriBuilder.Host = request.Host.Host;
uriBuilder.Path = request.Path.ToString();
uriBuilder.Query = request.QueryString.ToString();
return uriBuilder.Uri;
}
public string RootPath => Path.Combine(WebRootPath, RootFolderName);
public string GetProductPicturePath()
{
return Path.Combine(GetAbsoluteUri().ToString(), RootFolderName, ProductPictureFolder);
}
public string GetProductMainPicturePath()
{
string path = Path.Combine(GetAbsoluteUri().ToString(), RootFolderName, ProductPictureFolder, ProductMainPictureFolder);
return path;
}
public string GetNewPath()
{
string productMainPicturePath = GetProductMainPicturePath();
return Path.Combine(productMainPicturePath);
}
finally i using the GetNewPath().
, but this will give me the address :
https://localhost/api/Product/GetProductList/Upload/ProductPictureFolder/ProductMainPicture/77777.png
but i have 2 problem with this url :
1 - it not contain port in url https://localhost/api but i need return like this : http://localhost:4200/api
2 - This includes the name of the controller and the ActionName, but I need to like this : https://localhost/Upload/ProductPictureFolder/ProductMainPicture/77777.png
but it return for me this : https://localhost/api/Product/GetProductList/Upload/ProductPictureFolder/ProductMainPicture/77777.png
i not need this /api/Product/GetProductList .
Product : Controller Name
GetProductList : ActionName
How Can I Solve This Problem ???
1 - it not contain port in url https://localhost/api but i need return like this
To get port you can use this snippet :
if (request.Host.Port.HasValue)
uriBuilder.Port = request.Host.Port.Value;
2 - This includes the name of the controller and the ActionName, but I
need to like this :
https://localhost/Upload/ProductPictureFolder/ProductMainPicture/77777.png
I suggest you that set UriBuilder Path based on your needs, not from the request. Something like this:
// Make your Upload file path here
var relativePath = Path.Combine(folderName, filename);
var request = _httpContextAccessor.HttpContext.Request;
var uriBuilder = new UriBuilder
{
Host = request.Host.Host,
Scheme = request.Scheme,
Path = relativePath
};
if (request.Host.Port.HasValue)
uriBuilder.Port = request.Host.Port.Value;
var imageUrl = uriBuilder.ToString();
I'm trying to access Azure Cosmos DB using Table API.
The challenge is, despite creating SharedKeyLite, server is still returning Unauthorized - seems like SharedKeyLite is not supported or I'm generating the signature or headers wrong.
Here is the code
static readonly string storageAccountName = "accountName";
static readonly string storageAccountKey = "xxxx";
static readonly string uri = "https://accountName.table.cosmosdb.azure.com/Contacts()";
static readonly string utc_date = DateTime.UtcNow.ToString("r");
static void Main(string[] args)
{
Console.WriteLine(GetResult().Result);
}
static async Task<string> GetResult()
{
// Set this to whatever payload you desire. Ours is null because
// we're not passing anything in.
Byte[] requestPayload = null;
var requestDateString = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
var requestUri = new Uri(uri);
DateTime now = DateTime.UtcNow;
//Instantiate the request message with a null payload.
using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri)
{ Content = (requestPayload == null) ? null : new ByteArrayContent(requestPayload) })
{
ConstructHeaders(httpRequestMessage.Headers, requestDateString);
string authorizationHeader = GenerateSharedKeyLite(storageAccountKey, storageAccountName, uri,requestDateString);
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("SharedKeyLite", authorizationHeader);
// Send the request.
using (HttpResponseMessage httpResponseMessage = await new HttpClient().SendAsync(httpRequestMessage))
{
string json = await httpResponseMessage.Content.ReadAsStringAsync();
return json;
}
}
}
These are the headers I"m adding, expansion of ConstructHeaders method.
Refer this link for request parameters
//Construct the headers
static void ConstructHeaders(HttpRequestHeaders headers, string now)
{
headers.Add("x-ms-date", now);
headers.Add("x-ms-version", "2017-04-17");
// If you need any additional headers, add them here before creating
// the authorization header.
headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (headers.Contains("DataServiceVersion"))
headers.Remove("DataServiceVersion");
headers.Add("DataServiceVersion", "3.0;NetFx");
if (headers.Contains("MaxDataServiceVersion"))
headers.Remove("MaxDataServiceVersion");
headers.Add("MaxDataServiceVersion", "3.0;NetFx");
}
And this is the method that creates the SharedKeyLite
//Created Shared Key Lite
static string GenerateSharedKeyLite(string accessKey, string account, string url, string date)
{
var uri = new Uri(url);
var canonicalizedResourceString = uri.PathAndQuery;
var queryStart = canonicalizedResourceString.IndexOf('?');
if (queryStart > -1)
{
if (queryStart < canonicalizedResourceString.Length - 1)
{
var path = canonicalizedResourceString.Substring(0, queryStart);
var parameters = HttpUtility.ParseQueryString(canonicalizedResourceString.Substring(queryStart + 1));
var sb = new StringBuilder();
foreach (var keyOri in parameters.Keys)
{
var value = parameters[keyOri];
var key = keyOri.ToLowerInvariant();
sb.Append("\n");
sb.Append(key);
sb.Append(":");
sb.Append(value);
}
canonicalizedResourceString = canonicalizedResourceString + sb.ToString();
}
else
{
canonicalizedResourceString = canonicalizedResourceString.Substring(0, canonicalizedResourceString.Length - 1);
}
}
canonicalizedResourceString = $"/{account}{canonicalizedResourceString}";
var stringToSign = $"{date}\n{canonicalizedResourceString}";
var signedSignature = string.Empty;
using (var hmac = new HMACSHA256(Convert.FromBase64String(accessKey)))
{
var outputBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
var signature = Convert.ToBase64String(outputBytes);
return $"{account}:{signature}";
}
}
Any Help? Ideally I want to perform the odata query using simple.odata, but first trying to make this work using HttpClient
Just copy your code and it works on my side. If you haven't modified your code, please make sure your storageAccountName and storageAccountKey are correct.
BTW, in method GenerateSharedKeyLite there's no need to add query parameters to canonicalizedResourceString for entity operation. You only need to add comp if you want to operate component info for table or service. See constructing-the-canonicalized-resource-string.
The query string should include the question mark and the comp parameter (for example, ?comp=metadata). No other parameters should be included on the query string.
How to call one controller from another controller with Header Request in C#? Can somebody tell me.This is the code of ctrlABC and i want to call ctrlABCD method with the header request because ctrlABCD method is working from directly via URL also so i dont want to edit it
public string GetDetailAPI(ModelABC obj)
{
var response = "";
string ControllerName = "CtrlABCD/GetDetail/";
string BaseAddress = "http://192.168.3.21/WFXWebAPI/";
string pageParams = "ID|" + obj.QueueTaskKey + "~";
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("pageParams", pageParams);
client.BaseAddress = new Uri(BaseAddress);
response = client.GetStringAsync("api/" + ControllerName).Result.ToString();
}
}
return response;
}
This is the code of ctrlABCD
public DataSet GetDetail()
{
obj = new ClsABCD();
string pageParams="";
var re = Request;
var headers = re.Headers;
if (headers.Contains("pageParams"))
pageParams = headers.GetValues("pageParams").FirstOrDefault();
return obj.GetDetail(pageParams);
}
You can use the overloads method RedirectToAction(string actionName, string controllerName);
return RedirectToAction("Index", "MyController");
and
#Html.ActionLink("Link Name","Index", "MyController", null, null)
I want to create a unit test method for the below method which is receiving a file upload "text file" data and parsing it, I tried to use Moq and created a method but I am still very confused in the concept, I need a sample code, I've read many stackover flow questions but it is all for Controllers not Web API
the method used
// Enable both Get and Post so that our jquery call can send data, and get a status
[HttpGet]
[HttpPost]
public HttpResponseMessage Upload()
{
// Get a reference to the file that our jQuery sent. Even if multiple files, they will
// all be their own request and be the 0 index
if (HttpContext.Current.Request.Files.Count>0)
{
HttpPostedFile file = HttpContext.Current.Request.Files[0];
if (file != null && file.ContentLength > 0)
{
try
{
var extension = Path.GetExtension(file.FileName);
if (!IsFileFormatSupported(extension))
{
var objectSerialized = SerializeData(GetError( GlobalResources.NotSupportedFileExtension));
return BadResponse(objectSerialized);
}
var path = SaveFileGetPath(file);
var result = GetPaySlips(path);
var SerializedData = SerializeData(result);
return OkResponse(SerializedData);
}
catch (System.Exception exception)
{
var SerializedData = SerializeData(GetError( GlobalResources.CouldNotReadFile + " " + exception.Message));
return BadResponse(SerializedData);
}
}
else
{
var SerializedData = SerializeData(GetError(file.FileName + " " + GlobalResources.FileisCorrupt));
return BadResponse(SerializedData);
}
}else
{
var SerializedData = SerializeData(GetError( GlobalResources.FileisCorrupt));
return BadResponse(SerializedData);
}
}
the code I did so far
var FileUploadCtrl = new UploadController();
Mock<HttpRequestMessage> cc = new Mock<HttpRequestMessage>();
UTF8Encoding enc = new UTF8Encoding();
// Mock<HttpPostedFileBase> file1 = new Mock<HttpPostedFileBase>();
//file1.Expect(f=>f.InputStream).Returns(file1.Object.InputStream);
//cc.Expect(ctx => ctx.Content).Returns(new retur);
// cc.Expect(ctx => ctx.Content).Returns();
var content = new ByteArrayContent( /* bytes in the file */ );
content.Headers.Add("Content-Disposition", "form-data");
var controllerContext = new HttpControllerContext
{
Request = new HttpRequestMessage
{
Content = new MultipartContent { content }
}
};
//file1.Expect(d => d.FileName).Returns("FileTest.csv");
//file1.Expect(d => d.InputStream).Returns(new HttpResponseMessage(HttpStatusCode.OK)));
var config = new HttpConfiguration();
//var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/upload");
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "FileUpload" } });
FileUploadCtrl.ControllerContext = new HttpControllerContext(config, routeData, cc.Object);
var r = FileUploadCtrl.Upload();
Assert.IsInstanceOfType(r, typeof(HttpResponseMessage));
hello I am trying to pass a variable number of parameters inside a HttpWebRequest url. My approach so far is this-
public static async Task<ObservableCollection<Anime>> RequestsAnime(string accessToken, int page = 0, params string[] optionals) // All Anime
{
JsonWebClient client = new JsonWebClient();
string baseUri = "https://anilist.co/api/browse/anime?access_token=" + accessToken;
string completeUri = "";
string parameters = "";
if (optionals != null)
{
parameters = CollectionUtils.ToStringFromArray(optionals);
completeUri = baseUri + parameters;
}
if (page != 0)
{
completeUri = baseUri + "&page=" + page;
}
HttpWebRequest reqForToken = HttpWebRequest.CreateHttp(completeUri);
reqForToken.Method = "GET";
var respToken = await client.DoRequestJsonAsync<ObservableCollection<Anime>>(reqForToken);
return respToken;
}
ToStringFromArray function
public static string ToStringFromArray(string[] arrayString)
{
string result = string.Join("", arrayString);
return result;
}
RequestsAnime Calling
string[] ds = new string[] { "&status", "=active", "&season","=fall" };
var nm = await Requests.CreateMultipleTasksAsync(ds);
CreateMultipleTaskAsync calling
public static async Task<ObservableCollection<Anime>> CreateMultipleTasksAsync(int page=0 ,params string[] optionals)
{
Task<Auth> download1 = RequestsAuth();
Auth length1 = await download1;
Task<ObservableCollection<Anime>> download2 = RequestsAnime(download1.Result.Access_token,page, optionals);
var animeList = await download2;
return animeList;
}
I know its a clumsy way. I need to know a better solution. My objectives are-
Pass variable amount of parameters inside the URL
Append the parameters with "&" and "="
I would suggest you put all your query string parameters in a Dictionary:
var parameters = new Dictionary<string, string>
{
{ "status", "active" },
{ "season", "fall" }
};
You can then call the following method generate a valid query string (notice the calls to Uri.EscapeDataString:
string CreateQueryString(Dictionary<string, string> parameters)
{
return String.Join("&", parameters
.Select(param => String.Format("{0}={1}",
Uri.EscapeDataString(param.Key),
Uri.EscapeDataString(param.Value)))
.ToArray());
}
Just append the queryString to you baseUri:
completeUri = baseUri + "&" + CreateQueryString(parameters);