Is there a way to specify the base url for can.Model?
I see the server path being hard coded this way:
Wine = can.Model({
findAll : 'GET //localhost/Cellar-CanJS-Bootstrapped/api/wines',
findOne : 'GET //localhost/Cellar-CanJS-Bootstrapped/api/wines/{id}',
create : 'POST //localhost/Cellar-CanJS-Bootstrapped/api/wines',
update : 'PUT //localhost/Cellar-CanJS-Bootstrapped/api/wines/{id}',
destroy : 'DELETE //localhost/Cellar-CanJS-Bootstrapped/api/wines/{id}'
},{
})
What I'm looking for is a way specify the base url for the Model so that I can continue to configure my can.Model is the standard way.
Unfortunately setting a model base URL is not a built in feature yet but planned for the next minor release. This is how I usually do it at the moment:
function getUrl(url, method) {
method = method || 'GET';
return method + ' ' + BASE_URL + '/' + url;
}
Wine = can.Model({
findAll : getUrl('api/wines');
findOne : getUrl('api/wines/{id}'),
create : getUrl('api/wines', 'POST'),
update : getUrl('wines/{id}', 'PUT'),
destroy : getUrl('api/wines/{id}', 'DELETE')
},{
});
getUrl can be a Model static method or located in a better spot (e.g. application bootstrap) as well.
You can use the resource property:
Wine = can.Model.extend({
resource : "//localhost/Cellar-CanJS-Bootstrapped/api/wines",
},{});
Docs: http://canjs.com/docs/can.Model.resource.html
Related
I am unable to load a side loaded Windows Universal application using WinAppDriver
I have verified my code works if I try to load the Windows Calculator application but I am unable to load my target application. I get an error message "The system cannot find the file specified". I used the Get-AppxPackage method in PowerShell to get the application information for my target application. The method returned the data structure below:
Name : AACE4B69.MazikAXCashier
Publisher : CN=XXXXXXXXXXX
Architecture : Neutral
ResourceId :
Version : 2.4.1.9
PackageFullName : AACE4B69.MazikAXCashier_2.4.1.9_neutral__gfhc11b3bvd9y
InstallLocation : C:\Program Files\WindowsApps\AACE4B69.MazikAXCashier_2.4.1.9_neutral__gfhc11b3bvd9y
IsFramework : False
PackageFamilyName : AACE4B69.MazikAXCashier_gfhc11b3bvd9y
PublisherId : gfhc11b3bvd9y
IsResourcePackage : False
IsBundle : False
IsDevelopmentMode : False
NonRemovable : False
Dependencies : {Microsoft.WinJS.2.0_1.0.9600.17018_neutral__8wekyb3d8bbwe}
IsPartiallyStaged : False
SignatureKind : Developer
Status : Ok
I have tried to load the application using the following fields in the AppID field:
Name
PackageFullName
PackageFamilyName
[TestFixture]
public class Tests
{
protected static WindowsDriver<WindowsElement> windowSession;
public const String AppID = "AACE4B69.MazikAXCashier_2.4.1.9_neutral__gfhc11b3bvd9y";
public const String driverURL = "http://127.0.0.1:4723";
[SetUp]
public void Setup()
{
if (windowSession == null)
{
// Configure the application connection and load data
AppiumOptions opt = new AppiumOptions();
opt.AddAdditionalCapability("app", AppID);
try
{
// Create the driver object
windowSession = new WindowsDriver<WindowsElement>(new Uri(driverURL), opt);
Assert.IsNotNull(windowSession);
}
catch (Exception e)
{
Console.WriteLine("Unable to open application with error: " + e.Message);
}
}
}
[Test]
public void Test1()
{
Assert.Pass();
}}
I would expect one of these names to be the required Application ID and result in the app loading. Instead I get the error message "The system cannot find the file specified".
This application is side loaded and I am wondering if there is something different about that. Any help or suggestions are appreciated.
OK, I figured out the trick. I needed to user the PackageFamilyName appended with a qualifier. In my case this came out to be
public const String AppID = "AACE4B69.MazikAXCashier_gfhc11b3bvd9y!App";
I got this value via a two step process. I used the Powershell Get-appxPackage Mazik to get the PackageFamilyName as I previously did. I then appended "!App" to this string to get the required value. I determined the "!App" string based on another Powershell script I found in an online article. I used the following script to determine what the appended ID value was:
(Get-AppxPackageManifest (Get-AppxPackage Mazik)).package.applications.application.id
This returned "App" and I constructed the final string as described above. It looks like the "!App" is the typical ending to the required AppID but I have to assume there are different values for different situations. Once I got the correct AppID value everything worked as expected and the side loaded application was not an issue
The application key is usually a little shorter I believe. Are you sure you didn't make a mistake while copying it over to your code? Please double check app IDs for errors.
I created a shortcut to the Clocks app and then right clicked it. The "Target type" and "Target" fields contain the exact application key that work.
I'm trying to update the content of the body property of an activitymimeattachment. I've tested several methods in a plugin, as well as the webapi. Somehow the property is not updated.
A sample webapi call:
url: https://tenant.crm4.dynamics.com/api/data/v9.0/activitymimeattachments(81f6a467-6c6e-e811-a845-000d3a2a0765)
headers:
Content-type: Application/json
method: PATCH
{
"objecttypecode" : "email",
"objectid_activitypointer#odata.bind" : "/emails(08d23aaf-676e-e811-a845-000d3a2a0765)",
"body" : "bWlncmF0ZWQ="
}
The webapi returns 204: No content.
If I e.g. add the "subject" property, that will get updated, but the body remains the same.
When I do a post with the exact same body, it runs successful and the attachment is created.
Does anybody know why this happens and how to solve it?
There is an MSDN example using .Net.
Sample: Create, retrieve, update, and delete an email attachment.
It appears to also set the FileName, I suggest you attempt the same.
ActivityMimeAttachment _sampleAttachment = new ActivityMimeAttachment
{
ObjectId = new EntityReference(Email.EntityLogicalName, _emailId),
ObjectTypeCode = Email.EntityLogicalName,
Subject = String.Format("Sample Attachment {0}", i),
Body = System.Convert.ToBase64String(
new ASCIIEncoding().GetBytes("Example Attachment")),
FileName = String.Format("ExampleAttachment{0}.txt", i)
};
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap =
{
["apiVersion"] = typeof( ApiVersionRouteConstraint )
}
};
config.MapHttpAttributeRoutes(constraintResolver);
config.AddApiVersioning(o => o.AssumeDefaultVersionWhenUnspecified = true);
[ApiVersion("2.05")]
[RoutePrefix("api/v{version:apiVersion}/ger")]
public class caGerController
[Route("~/api/ger/getDetail")]
[Route("getDetail")]
GetGerData
[ApiVersion("1")]
[RoutePrefix("api/v{version:apiVersion}/gerDetail")]
public class caGerDetailsController
caGerController
[Route("~/api/gerDetail/getDetail")]
[Route("getDetail")]
GetGerData
>> GetGerData
Result:
Both URL working with v1 version ROUTE.
Second URL working for both, v1 and direct without v1 route as well i.e. [Route("~/api/gerDetail/getDetail")]
PROBLEM: first URL is only working with v1 and its not working with direct route like " [Route("~/api/ger/getDetail")]"
and getting an error as below:
"Error": {
"Code": "ApiVersionUnspecified",
"Message": "An API version is required, but was not specified."
}
How to solve this issue?
When I change from 2.05 to 1.0 then it works but 2.0 or 2.05 both do not work. Is there a separate folder required?
The ApiVersionUnspecified happens because all routes require an explicit API version by default. You opt out of this behavior using:
options.AssumeDefaultVersionWhenUnspecified = true
This setting means that a default API version is assumed when a client doesn't provide one. The default value is:
options.DefaultApiVersion // equals 1.0 by default
When you use the URL segment versioning method, you can't have two different controllers that both an unversioned route. The route without an API version can only map to a single controller. Since the default is "1.0" and you have a controller with the unversioned route, that's the one that will always been matched.
By adding API versioning, the default behavior is that it uses QueryString versioning.
config.AddApiVersioning(cfg => {});
api-version=1.0
To specify a version, you can add the querystring parameter api-version=1.0 at the end.
Example:
http://localhost:6600/api/test?api-version=1.0
You can change the version like this:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
...
public static void Register(HttpConfiguration config)
{
...
config.AddApiVersioning(cfg =>
{
cfg.DefaultApiVersion = new ApiVersion(1,1);
});
So you can change the version like this:
http://localhost:6600/api/test?api-version=1.1
By adding AssumeDefaultVersionWhenUnspecified, you don't have to specify a version.
config.AddApiVersioning(cfg =>
{
cfg.DefaultApiVersion = new ApiVersion(1,1);
cfg.AssumeDefaultVersionWhenUnspecified = true;
});
This will work:
http://localhost:6600/api/test
You can also add ReportApiVersions
config.AddApiVersioning(cfg =>
{
cfg.DefaultApiVersion = new ApiVersion(1,1);
cfg.AssumeDefaultVersionWhenUnspecified = true;
cfg.ReportApiVersions = true;
});
The response will have a new header api-supported-versions which specifies what versions are supported for the call they made.
I am working on New web application which is Using Web API as Business Layer and Knock out Js as client side frame work to binding. I have a requirement like Pass the certain search criteria to Web API Controller and get the Data from DB and Create and Send the Excel/MS-Word file on the fly as a downloadable content.
I am new to both the Web API and Knock out, I am searching on the Net and get partial solution and I am looking here to get more optimal solution for this use case.
Below is my code:
Client:
function GetExcelFile() {
var $downloadForm = $("<form method='POST'>")
.attr("action", baseUrl + "api/FileHandler/GetExcelFileTest")
.attr("target", "_blank")
$("body").append($downloadForm);
$downloadForm.submit();
$downloadForm.remove();
}
On Button Click having this code snippet to create a form on the fly and Get response from Web API.
Web API Code:
[HttpPost]
public HttpResponseMessage GetExcelFileTest()
{
var response = new HttpResponseMessage();
//Create the file in Web App Physical Folder
string fileName = Guid.NewGuid().ToString() + ".xls";
string filePath = HttpContext.Current.Server.MapPath(String.Format("~/FileDownload/{0}", fileName));
StringBuilder fileContent = new StringBuilder();
//Get Data here
DataTable dt = GetData();
if (dt != null)
{
string str = string.Empty;
foreach (DataColumn dtcol in dt.Columns)
{
fileContent.Append(str + dtcol.ColumnName);
str = "\t";
}
fileContent.Append("\n");
foreach (DataRow dr in dt.Rows)
{
str = "";
for (int j = 0; j < dt.Columns.Count; j++)
{
fileContent.Append(str + Convert.ToString(dr[j]));
str = "\t";
}
fileContent.Append("\n");
}
}
// write the data into Excel file
using (StreamWriter sw = new StreamWriter(fileName.ToString(), false))
{
sw.Write(fileContent.ToString());
}
IFileProvider FileProvider = new FileProvider();
//Get the File Stream
FileStream fileStream = FileProvider.Open(filePath);
//Set response
response.Content = new StreamContent(fileStream);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = fileName;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/ms-excel");
response.Content.Headers.ContentLength = fileStream.Length;
//Delete the file
//if(File.Exists(filePath))
//{
// File.Delete(filePath);
//}
return response;
}
Using this code I am able to download an Excel File. Still I have some more open questions to make this code optimal.
Q1) I need to Pass view model(Search Criteria) to API Controller Using the dynamically create form ? (OR) Any better ways to get Excel file from Web API.
Q2) I am sure it's not a good way to create Excel file in Physical folder and Get FileStream and send as a respone. How to do on the fly ? OR any other optimal ways.
Please suggest me to do better ways.. Thanks
Q1) You can quite easily pass the view-model, but it's also similarly easy to pull that information from the posted form.
Passing the view-model
If you want to pass the view-model to a WebAPI method then remember that said method must take as a parameter an object with the same properties. So if the object that you wish to post back always has the same properties then it's trivial to build a server-side class with the same properties and receive an instance of that class.
To post back this client-side object you can do something like this (uses jQuery, which I see you're already using):
$.ajax({
contentType: "application/json",
data: my-view-model.toJSON(),
type: "POST",
url: baseUrl + "api/FileHandler/GetExcelFileTest" });
I haven't attached any success or error handlers here because the JavaScript isn't concerned with the return, but you might wish to add some handlers in case an exception is thrown in your WebAPI method. I recommend doing that by adding the following to the above $.ajax() call:
statusCode: {
500: function(jqXhr, textStatus, errorThrown) {
},
[other HTTP error codes]
}
[Read the documentation for the $.ajax() call here.]
One additional tip here: when you call my-view-model.toJSON() (or self.toJSON(), if called from within your view-model) Knockout will first of all determine if your view-model contains a toJSON() method. If so, it will use this method; if not then it will call the browser's implementation of this function. However, the browser's implementation of this function will serialise everything, which can be particularly length if you have, for example, long select lists in your view-model. Therefore, if you wish only to send back a subset of the view-model's properties then define your own toJSON function on your view-model like so:
var toJSON = function() {
return {
Property1: ...,
Property2: ...
};
}
[Read more about converting a view-model to JSON here.]
Posting the form as-is
If you don't wish to expend the effort to do the view-model wiring then you can just post the form exactly like you have in your question. You can then retrieve the values from the form by using
Request.Form["my-field"];
Q2)
You're probably right in pointing out that it's not wise to create the Excel file in the physical folder. However, as far as I'm aware (interested if someone says otherwise) you'll have to use a 3rd-party library for this. Microsoft do offer an Office automation library but I have a suspicion that you also need Office to be installed at the same location.
Creating Excel spreadsheets dynamically is something I've done several times but for the actual creation I use Aspose.Cells, which requires a license. Although I do create a physical version and then delete it, I believe Aspose.Cells may allow you to create it as a stream. But take a look around, there are certainly other libraries which offer Excel automation.
Returning the File from the Server
Calling $.ajax({...}) alone won't allow you to present the user with a "Save as..." dialog. What I do in this situation - and this won't work if you wish to store the generated file only in memory (FileStream, for example) and not on the file system - is to respond to the $.ajax({...}) call with a filename for the generated file.
The next step is to direct the user towards that filename.
So I have something like this in my JavaScript:
$.ajax({
dataType: "json",
type: "GET", // you'll probably want POST in your case
url: ...,
success: function(response) {
if (response && response.Uri && response.Uri.length) {
window.location.href = [root URL] + response.Uri;
}
}
});
But don't be alarmed by this redirect. That window.location.href points directly to a folder on the server, no controller needed. Because the browser then receives a file it presents the "Save as..." dialog while remaining on the same webpage.
I have created following route
routes.MapRoute("ThumbnailRoute",// Route name
"Image/{action}/{session}/{parentId}/{fileName}/{ctype}/{thumbNailSize}", // URL with parameters
new { controller = "Image", action = "GenerateThumbnail", session = "", parentId = "", fileName = "", ctype = "", thumbNailSize = 70 }, // Parameter defaults
new { controller = #"[^\.]*", action = #"[^\.]*" });
and my extension method returns a string like following which will be the src attribute of the img tag:
return string.Format("/{0}/{1}/{2}/{3}/{4}/{5}/{6}", controller, action, session, parentId, fileName, ctype, thumbNailSize);
when I right click on the pages and choose properties for both dev and prod environments the src av img tag is same (http://localhost/Image/GenerateThumbnail/de-DE/121/0beac6da-7c09-4faf-ad4b-48326f9d337e.jpg/jpeg/70) only different is the domain name (localhost, www.domain.com) but the images de not appear on prod. thanks for your help
If the URLs look fine on the production version - perhaps the issue isn't with the routing, but rather with the code in the action method.
Have you checked what response you get from the browser when hitting the production URL?
What response do you get when your browse to http://www.domain.com/Image/GenerateThumbnail/de-DE/121/0beac6da-7c09-4faf-ad4b-48326f9d337e.jpg/jpeg/70?
Never hardcode urls as you did. Always use url helpers. The thing is that when you deploy your application in IIS there's a virtual directory name. So the correct url is the following:
http://foo.com/MyAppName/Image/GenerateThumbnail/de-DE/121/0beac6da-7c09-4faf-ad4b-48326f9d337e.jpg/jpeg/70
instead of:
http://foo.com/Image/GenerateThumbnail/de-DE/121/0beac6da-7c09-4faf-ad4b-48326f9d337e.jpg/jpeg/70
Since you have hardcoded the url you get 404.
So use the RouteUrl method to generate it which will take into account this virtual directory if any. Don't use any string formatting to build urls:
public ActionResult Index()
{
string url = Url.RouteUrl("ThumbnailRoute", new
{
action = "GenerateThumbnail",
controller = "Image",
session = session,
parentId = parentId,
fileName = fileName,
ctype = ctype,
thumbNailSize = thumbNailSize
});
...
}