I want to rewrite a url which is simple by using the UseRewriter Middleware.
var rewrite = new RewriteOptions()
.AddRedirect("Page1From", "Page1To") // Redirect
.AddRewrite("Page2From", "Page2To", true); // Rewrite
app.UseRewriter(rewrite);
This will have as a result, the url "/Page1From" will be redirected to "Page1To" and the url "Page2From" will display the contents of the "/Page2To" without redirect.
I want to implement the AddRewrite method by using data from database but I only found how to redirect using a custom Rule.
var rewrite = new RewriteOptions()
.AddRedirect("Page1From", "Page1To") // Redirect
.AddRewrite("Page2From", "Page2To", true); // Rewrite
.Add(new MoviesRedirectRule( // Custom Rule
matchPaths: new[] { "/Page3From1", "/Page3From2", "/Page3From3" },
newPath: "/Page3To"));
app.UseRewriter(rewrite);
and the rule is the following:
public class MoviesRedirectRule : IRule
{
private readonly string[] matchPaths;
private readonly PathString newPath;
public MoviesRedirectRule(string[] matchPaths, string newPath)
{
this.matchPaths = matchPaths;
this.newPath = new PathString(newPath);
}
public void ApplyRule(RewriteContext context)
{
var request = context.HttpContext.Request;
// if already redirected, skip
if (request.Path.StartsWithSegments(new PathString(this.newPath)))
{
return;
}
if (this.matchPaths.Contains(request.Path.Value))
{
var newLocation = $"{this.newPath}{request.QueryString}";
var response = context.HttpContext.Response;
response.StatusCode = StatusCodes.Status302Found;
context.Result = RuleResult.EndResponse;
response.Headers[HeaderNames.Location] = newLocation;
}
}
}
This will redirect the following urls:
/Page3From1
/Page3From2
/Page3From3
to /Page3To
I want to create something similar which will not redirect but I want to Rewrite the url so that the url will remain the same but it will display the contents of a specified URL.
Can someone tell me what changes I have to do to the response object in order to make it work?
RewriteRule that is added when you call AddRewrite() just replaces URI parts in request (link to source code):
request.Scheme = scheme;
request.Host = host;
request.Path = pathString;
request.QueryString = query.Add(request.QueryString);
In your case, since you are replacing only Path part, ApplyRule method would be as simple as:
public void ApplyRule(RewriteContext context)
{
var request = context.HttpContext.Request;
if (matchPaths.Contains(request.Path.Value))
{
request.Path = newPath;
context.Result = RuleResult.SkipRemainingRules;
}
}
Related
What I would like to do is be able to type an custom property within the back office search. e.g. put the ISBN into the search field and have the results shown currently it always returns "no items found" as the search will only show results for the title node.
How do I enable the content search as seen in the image to search the data in the custom fields?
The data is in the internal index, I have checked the index is working and can see the result with "Examine Management" if I search via the custom data.
The solution is what I used to extend the search
https://dev.to/skttl/how-to-customize-searching-in-umbraco-list-views-1knk
Add a new file in the App_Code (SearchExtender)
using System.Linq;
using Examine;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
using Umbraco.Web;
using Umbraco.Web.Editors;
using Umbraco.Web.Models.ContentEditing;
namespace SearchExtender
{
public class CustomListViewSearchController : ContentController
{
public CustomListViewSearchController(PropertyEditorCollection propertyEditors, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
: base(propertyEditors, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
{
}
public PagedResult<ContentItemBasic<ContentPropertyBasic>> GetChildrenCustom(int id, string includeProperties, int pageNumber = 0, int pageSize = 0, string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, bool orderBySystemField = true, string filter = "", string cultureName = "")
{
// get the parent node, and its doctype alias from the content cache
var parentNode = Services.ContentService.GetById(id);
var parentNodeDocTypeAlias = parentNode != null ? parentNode.ContentType.Alias : null;
// if the parent node is not "books", redirect to the core GetChildren() method
if (parentNode?.ContentType.Alias != "books")
{
return GetChildren(id, includeProperties, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter);
}
// if we can't get the InternalIndex, redirect to the core GetChildren() method, but log an error
if (!ExamineManager.Instance.TryGetIndex("InternalIndex", out IIndex index))
{
Logger.Error<CustomListViewSearchController>("Couldn't get InternalIndex for searching products in list view");
return GetChildren(id, includeProperties, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter);
}
// find children using Examine
// create search criteria
var searcher = index.GetSearcher();
var searchCriteria = searcher.CreateQuery();
var searchQuery = searchCriteria.Field("parentID", id);
if (!filter.IsNullOrWhiteSpace())
{
searchQuery = searchQuery.And().GroupedOr(new [] { "nodeName", "isbn" }, filter);
}
// do the search, but limit the results to the current page 👉 https://shazwazza.com/post/paging-with-examine/
// pageNumber is not zero indexed in this, so just multiply pageSize by pageNumber
var searchResults = searchQuery.Execute(pageSize * pageNumber);
// get the results on the current page
// pageNumber is not zero indexed in this, so subtract 1 from the pageNumber
var totalChildren = searchResults.TotalItemCount;
var pagedResultIds = searchResults.Skip((pageNumber > 0 ? pageNumber - 1 : 0) * pageSize).Select(x => x.Id).Select(x => int.Parse(x)).ToList();
var children = Services.ContentService.GetByIds(pagedResultIds).ToList();
if (totalChildren == 0)
{
return new PagedResult<ContentItemBasic<ContentPropertyBasic>>(0, 0, 0);
}
var pagedResult = new PagedResult<ContentItemBasic<ContentPropertyBasic>>(totalChildren, pageNumber, pageSize);
pagedResult.Items = children.Select(content =>
Mapper.Map<IContent, ContentItemBasic<ContentPropertyBasic>>(content))
.ToList(); // evaluate now
return pagedResult;
}
}
}
change requests for /umbraco/backoffice/UmbracoApi/Content/GetChildren (the default endpoint for child nodes), and change it to my newly created one, which is located at /umbraco/backoffice/api/CustomListViewSearch/GetChildrenCustom.
This is done easily by adding a js file containing an interceptor like this.
Add file to /App_Plugins/CustomListViewSearch/CustomListViewSearch.js
angular.module('umbraco.services').config([
'$httpProvider',
function ($httpProvider) {
$httpProvider.interceptors.push(function ($q) {
return {
'request': function (request) {
// Redirect any requests for the listview to our custom list view UI
if (request.url.indexOf("backoffice/UmbracoApi/Content/GetChildren?id=") > -1)
request.url = request.url.replace("backoffice/UmbracoApi/Content/GetChildren", "backoffice/api/CustomListViewSearch/GetChildrenCustom");
return request || $q.when(request);
}
};
});
}]);
a package.manifest file in my App_Plugins folder.
{
"javascript": [
"/App_Plugins/CustomListViewSearch/CustomListViewSearch.js"
]
}
If the node Alais isnot working make sure its set in the documnt type (far right on document type name)
I have the following string https://www.something.com/api/mmmm/media/mhm/something.jpg and I want to trim this string to become https://www.something.com/media/mhm/something.jpg essentially I want it to be dynamic so I don't want to hardcode it because the URL won't be the same forever this particular part will change. Put it another way I want to remove everything from /api/ till it reaches to /media/ and I don't seem to be able to do it.
You can use Uri.parse to parse the url and the loop the pathSegments of the uri create a new uri with the pathSegments that you mish to keep, such as:
void main() {
var uri =
Uri.parse('https://www.something.com/api/mmmm/media/mhm/something.jpg');
var exclude = false;
var newPath = <String>[];
for (var seg in uri.pathSegments) {
if (seg == 'api') {
exclude = true;
continue;
}
if (seg == 'media') {
exclude = false;
}
if (!exclude) {
newPath.add(seg);
}
}
var newUri = uri.replace(pathSegments: newPath);
print(newUri); //Prints https://www.something.com/media/mhm/something.jpg
}
An alternative solution would be use a RegExp like this:
void main() {
print(
trimString('https://www.something.com/api/mmmm/media/mhm/something.jpg'));
// https://www.something.com/media/mhm/something.jpg
print(trimString(
'https://www.something.com/api/AlotOfStrangeThings/complateNonsense/&&&&&/media/mhm/something.jpg'));
// https://www.something.com/media/mhm/something.jpg
}
String trimString(String string) =>
string.replaceAll(RegExp(r'/api/(.*)/media/'), '/media/');
I am using the sample code from the Unity website for the WWW class to make an API request but the text response is garbage. It looks like ����. When I log the response headers, I get a 200 response and everything seems ok except that the CONTENT-TYPE is image/jpeg. I have tried several different random .json files to test it out and they all return the same thing. Requesting an image to use as a texture does work.
public class SpeechReq : MonoBehaviour {
//public string url = "https://gist.githubusercontent.com/wethecode/1f79baf168680afb0f2d/raw/755f9fb71dcc34df811b4bc26448d88a0f97f34d/snippets.json";
public string url = "https://gist.githubusercontent.com/damienh/fea91ab710475d499a09/raw/893065428badd8bfdc7b39fe17675b8aa031ac51/gistfile1.json";
IEnumerator Start()
{
WWW www = new WWW(url);
yield return www;
string respText = www.text;
Debug.Log(respText);
//Output: ����
byte[] resp = www.bytes;
var str = System.Text.Encoding.Default.GetString(resp);
Debug.Log(str);
//Output: ÿØÿÃ
if (www.responseHeaders.Count > 0)
{
foreach (KeyValuePair<string, string> entry in www.responseHeaders)
{
Debug.Log(entry.Value + "=" + entry.Key);
//Output: HTTP/1.0 200 OK=STATUS
//...
//image/jpeg=CONTENT-TYPE
}
}
}
}
The WWW class's .text method returns the UTF8 Byte Order Mark at the beginning of the response by default. See a description of the BOM here
You could try:
string jsonText = "";
if (string.IsNullOrEmpty(www.error))
{
jsonText = System.Text.Encoding.UTF8.GetString(www.bytes, 3, www.bytes.Length - 3); // Skip the UTF8 BOM
JSONObject myObject = new JSONObject(jsonText);
}
I want to post some request values alongside the multipart-formdata file contents. In the old API you could use PostFileWithRequest:
[Test]
public void Can_POST_upload_file_using_ServiceClient_with_request()
{
IServiceClient client = new JsonServiceClient(ListeningOn);
var uploadFile = new FileInfo("~/TestExistingDir/upload.html".MapProjectPath());
var request = new FileUpload{CustomerId = 123, CustomerName = "Foo"};
var response = client.PostFileWithRequest<FileUploadResponse>(ListeningOn + "/fileuploads", uploadFile, request);
var expectedContents = new StreamReader(uploadFile.OpenRead()).ReadToEnd();
Assert.That(response.FileName, Is.EqualTo(uploadFile.Name));
Assert.That(response.ContentLength, Is.EqualTo(uploadFile.Length));
Assert.That(response.Contents, Is.EqualTo(expectedContents));
Assert.That(response.CustomerName, Is.EqualTo("Foo"));
Assert.That(response.CustomerId, Is.EqualTo(123));
}
I can't find any such method in the new API, nor any overrides on client.Post() which suggest that this is still possible. Does anyone know if this is a feature that was dropped?
Update
As #Mythz points out, the feature wasn't dropped. I had made the mistake of not casting the client:
private IRestClient CreateRestClient()
{
return new JsonServiceClient(WebServiceHostUrl);
}
[Test]
public void Can_WebRequest_POST_upload_binary_file_to_save_new_file()
{
var restClient = (JsonServiceClient)CreateRestClient(); // this cast was missing
var fileToUpload = new FileInfo(#"D:/test/test.avi");
var beforeHash = this.Hash(fileToUpload);
var response = restClient.PostFileWithRequest<FilesResponse>("files/UploadedFiles/", fileToUpload, new TestRequest() { Echo = "Test"});
var uploadedFile = new FileInfo(FilesRootDir + "UploadedFiles/test.avi");
var afterHash = this.Hash(uploadedFile);
Assert.That(beforeHas, Is.EqualTo(afterHash));
}
private string Hash(FileInfo file)
{
using (var md5 = MD5.Create())
{
using (var stream = file.OpenRead())
{
var bytes = md5.ComputeHash(stream);
return BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", "").ToLower();
}
}
}
None of the old API was removed from the C# Service Clients, only new API's were added.
The way you process an uploaded file inside a service also hasn't changed.
I have a webdav function listed below:
The behavior is completely unexpected....
When I first run the function and pass a URL to a resource (folder in sharepoint) that does not exist, I get a 404 which is expected. I then use another function to create the resource using THE SAME credentials as in this method. No problems yet...
However on 2nd run, after the resource has been created - when I check if resource exists, now I get a 401.
Whats important to note here is that the same credentials are used to check for 401 and create folder, so clearly the credentials are fine...
So it must be something else.... All I want to do is check if a resource exists in SharePoint.... any ideas how to improve this function? Or any theory as to why its giving this 401...
private bool MossResourceExists(string url)
{
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "HEAD";
// Create a new CredentialCache object and fill it with the network
// credentials required to access the server.
var myCredentialCache = new CredentialCache();
if (!string.IsNullOrEmpty(this.Domain ))
{
myCredentialCache.Add(new Uri(url),
"NTLM",
new NetworkCredential(this.Username , this.Password , this.Domain )
);
}
else
{
myCredentialCache.Add(new Uri(url),
"NTLM",
new NetworkCredential(this.Username , this.Password )
);
}
request.Credentials = myCredentialCache;
try
{
request.GetResponse();
return true;
}
catch (WebException ex)
{
var errorResponse = ex.Response as HttpWebResponse;
if (errorResponse != null)
if (errorResponse.StatusCode == HttpStatusCode.NotFound)
{
return false;
}
else
{
throw new Exception("Error checking if URL exists:" + url + ";Status Code:" + errorResponse.StatusCode + ";Error Message:" + ex.Message ) ;
}
}
return true;
}
The only clue I have is that when using http://mysite.com/mydoclib/mytoplevelfolder it works.... any sub folders automatically give 401's....
The thing is that you can't pass the whole url that includes folders to the CredentialCache.Add() method.
For example:
http://MyHost/DocumentLibrary/folder1/folder2 will not work as an Uri to the Add() method, but
http://MyHost/DocumentLibrary/ will work.
I would guess that the lack of permissioning capabilities on folder level in SharePoint is the reason for this. Or the way that otherwise SharePoint handles folders.
What you can do is to separate the parameters in your method to accept a base url (including document libraries / lists) and a folder name parameter.
The CredentialCache gets the base url and the request object gets the full url.
Another way is to use the
request.Credentials = System.Net.CredentialCache.DefaultCredentials;
credentials instead. And, if necessary, do an impersonation if you want to use another account than the executing one.
A third variation is to try with authentication type set to Kerberos instead of NTLM.
Here is my test code. I am able to reproduce the problem if I replace the problem with your code, and this code works for me.
static void Main(string[] args)
{
bool result = MossResourceExists("http://intranet/subtest/content_documents/", "testfolder/testfolder2");
}
private static bool MossResourceExists(string baseUrl, string folder)
{
string completeUrl = baseUrl + folder;
var request = (HttpWebRequest)WebRequest.Create(completeUrl);
request.Method = "HEAD";
// Create a new CredentialCache object and fill it with the network
// credentials required to access the server.
var myCredentialCache = new CredentialCache();
if (!string.IsNullOrEmpty(Domain))
{
myCredentialCache.Add(new Uri(baseUrl),
"NTLM",
new NetworkCredential(Username, Password, Domain)
);
}
else
{
myCredentialCache.Add(new Uri(baseUrl),
"NTLM",
new NetworkCredential(Username, Password)
);
}
request.Credentials = myCredentialCache;
//request.Credentials = System.Net.CredentialCache.DefaultCredentials;
try
{
WebResponse response = request.GetResponse();
return true;
}
catch (WebException ex)
{
var errorResponse = ex.Response as HttpWebResponse;
if (errorResponse != null)
if (errorResponse.StatusCode == HttpStatusCode.NotFound)
{
return false;
}
else
{
throw new Exception("Error checking if URL exists:" + completeUrl + ";Status Code:" + errorResponse.StatusCode + ";Error Message:" + ex.Message);
}
}
return true;
}
Hope this helps.