@sachinerande
Thank you for sharing your use case which clarifies what is expected behavior. I thought that it is supported but it appears that it does not work with combination of options. Anyway, at the moment you can implement IApiUrlBuilder
to handle URL building by yourself. The following code shows how it can be implemented to make URL relative:
using System.Web;
using GroupDocs.Viewer.UI.Api.Configuration;
using GroupDocs.Viewer.UI.Api.Utils;
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddGroupDocsViewerUI();
builder.Services
.AddControllers()
.AddGroupDocsViewerSelfHostApi(config =>
{
config.SetLicensePath("c://licenses//GroupDocs.Viewer.lic");
})
.AddLocalStorage("./Files")
.AddLocalCache("./Cache");
// NOTE: make sure to place it after AddGroupDocsViewerSelfHostApi method,
// since it registers the default implmentation and we have to override it
builder.Services.AddTransient<IApiUrlBuilder, MyApiUrlBuilder>();
var app = builder.Build();
app
.UseRouting()
.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", (HttpContext context) =>
{
context.Response.ContentType = "text/html";
return "<a href='/viewer?file=annual-review.docx'>Open annual-review.docx file</a>";
});
endpoints.MapGroupDocsViewerUI(options =>
{
options.UIPath = "/viewer";
options.ApiEndpoint = "/viewer-api";
});
endpoints.MapGroupDocsViewerApi(options =>
{
options.ApiDomain = "/";
options.ApiPath = "/viewer-api";
});
});
app.Run();
public class MyApiUrlBuilder : IApiUrlBuilder
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly Options _options;
public MyApiUrlBuilder(
IHttpContextAccessor httpContextAccessor,
IOptionsProvider optionsProvider)
{
_httpContextAccessor = httpContextAccessor;
_options = optionsProvider.GetOptions();
}
public string GetApiDomainOrDefault()
{
var request = _httpContextAccessor.HttpContext.Request;
if (string.IsNullOrEmpty(_options.ApiDomain))
{
var baseUrl = $"{request.Scheme}://{request.Host}";
if (!string.IsNullOrEmpty(request.PathBase))
{
baseUrl += request.PathBase.Value;
}
return baseUrl;
}
return _options.ApiDomain;
}
public string BuildPageUrl(string file, int page, string extension) =>
BuildUrl(
apiDomain: GetApiDomainOrDefault(),
apiPath: _options.ApiPath,
apiMethodName: GroupDocs.Viewer.UI.Api.ApiNames.API_METHOD_GET_PAGE,
values: new { file = file, page = page });
public string BuildThumbUrl(string file, int page, string extension) =>
BuildUrl(
apiDomain: GetApiDomainOrDefault(),
apiPath: _options.ApiPath,
apiMethodName: GroupDocs.Viewer.UI.Api.ApiNames.API_METHOD_GET_THUMB,
values: new { file = file, page = page });
public string BuildPdfUrl(string file) =>
BuildUrl(
apiDomain: GetApiDomainOrDefault(),
apiPath: _options.ApiPath,
apiMethodName: GroupDocs.Viewer.UI.Api.ApiNames.API_METHOD_GET_PDF,
values: new { file = file });
public string BuildResourceUrl(string file, int page, string resource) =>
BuildUrl(
apiDomain: GetApiDomainOrDefault(),
apiPath: _options.ApiPath,
apiMethodName: GroupDocs.Viewer.UI.Api.ApiNames.API_METHOD_GET_RESOURCE,
values: new { file = file, page = page, resource = resource });
public string BuildResourceUrl(string file, string pageTemplate, string resourceTemplate) =>
BuildUrl(
apiDomain: GetApiDomainOrDefault(),
apiPath: _options.ApiPath,
apiMethodName: GroupDocs.Viewer.UI.Api.ApiNames.API_METHOD_GET_RESOURCE,
values: new { file = file, page = pageTemplate, resource = resourceTemplate });
/// <summary>
/// Builds a relative URL ignoring doiman name and api path
/// <param name="apiDomain">The base API domain, e.g., "https://www.example.com".</param>
/// <param name="apiPath">The API path, e.g., "viewer-api".</param>
/// <param name="apiMethodName">The API method name, e.g., "get-page".</param>
/// <param name="values">An object containing query parameter key-value pairs, e.g., new { file = "my-file.docx", page = 5 }.</param>
/// <returns>The relative URL as a string, e.g., "/get-page?file=my-file.docx&page=5".</returns>
/// <example>
/// string url = UrlHelper.BuildUrl("https://www.example.com", "viewer-api", "get-page", new { file = "my-file.docx", page = 5 });
/// Console.WriteLine(url); // Output: /get-page?file=my-file.docx&page=5
/// </example>
private static string BuildUrl(string apiDomain, string apiPath, string apiMethodName, object values)
{
if (string.IsNullOrWhiteSpace(apiDomain))
throw new ArgumentNullException(nameof(apiDomain), "API domain cannot be null or empty.");
if (string.IsNullOrWhiteSpace(apiPath))
throw new ArgumentNullException(nameof(apiPath), "API path cannot be null or empty.");
if (string.IsNullOrWhiteSpace(apiMethodName))
throw new ArgumentNullException(nameof(apiMethodName), "API method name cannot be null or empty.");
// Ensure proper URL formatting
string basePath = $"/{apiMethodName.TrimStart('/')}";
var queryString = BuildQueryString(values);
return string.IsNullOrWhiteSpace(queryString) ? basePath : $"{basePath}?{queryString}";
}
private static string BuildQueryString(object values)
{
if (values == null)
return string.Empty;
var queryParameters = new List<string>();
foreach (var property in values.GetType().GetProperties())
{
var key = property.Name;
var value = property.GetValue(values, null);
if (value != null)
{
queryParameters.Add($"{HttpUtility.UrlEncode(key)}={HttpUtility.UrlEncode(value.ToString())}");
}
}
return string.Join("&", queryParameters);
}
}
The sample app: sample-app.zip (3.7 MB)
And it here is view-data method response:
I have created a ticket, internal ID is VIEWERNET-5377 to address this issue in the future version.