Trouble updating a field of type lookup with Microsoft CRM 2016 Web API - dynamics-crm-2016

I currently have some code that uses http patch to send Microsoft CRM data via the 2016 web api. When my payload includes a text or int datatype it works just fine, but when the payload includes a lookup record I can't get any response beyond 400 bad request.
Below are a few of the instances of payload that I've tried (with sensitive data altered)
payload = {"new_lastweblocation": "Midlothian" }
payload = {"new_location_transaction_LastWebLocationid#odata.bind" : "https://crmnaorgXXXX.crm.dynamics.com/api/data/v8.0/new_locations(1234578-a588-e511-8105-c4346bace18c)"}
payload = {"new_lastweblocation#odata.bind" : "https://crmnaorgXXXX.crm.dynamics.com/api/data/v8.0/new_locations(1234578-a588-e511-8105-c4346bace18c)"}
Essentially I've tried passing plaintext, a guid to the record, a guid to the relationship, a guid linked via odata.bind ... etc.
Clearly my shotgun approach along with the 400 error means that I fundamentally misunderstand how entities are handled in the 2016 web api. Let me know if you have any suggestions.

The approach listed on MSDN for associating entities on create also works when updating. I tested the following query in a 2016 demo environment without issues (where the guids have been replaced with existing account and contact guids, respectively):
PATCH [Organization URI]/api/data/v8.0/accounts/(00000000-0000-0000-0000-000000000001) HTTP/1.1
Content-Type: application/json; charset=utf-8
OData-MaxVersion: 4.0
OData-Version: 4.0
Accept: application/json
{
"name":"Sample Account",
"primarycontactid#odata.bind":"/contacts(00000000-0000-0000-0000-000000000001)"
}
Could you start by verifying that this out-of-the-box use case works before debugging your specific issue with the lookup to a custom entity?

I ended up using this request. The main issue that I was having is that I didn't know what the single-valued navigation property was. To find that I ended up using this request. I altered the select in the url to be select="*"
Original URL
GET [Organization URI]/api/data/v8.1/incidents(39dd0b31-ed8b-e511-80d2-00155d2a68d4)?$select=title,customerid_value&$expand=customerid_contact($select=fullname)
My URL
GET [Organization URI]/api/data/v8.1/incidents(39dd0b31-ed8b-e511-80d2-00155d2a68d4)?$select=*
When using the GET request to try and find the single-valued navigation property make sure to add 'Prefer':'odata.include-annotations"*". I couldn't get the preference header to pass until I put it before my authorization header.
Finally once I got a response from the get request I looked for the variable I was looking for with #Microsoft.Dynamics.CRM.associatednavigationproperty at the end of it and the used the value associated with that. In my case, the field name was new_lastweblocation but the single-value navigation property was new_LastWebLocation

Related

How can I set "Content-Length" for Visual Studio web performance test request?

I have created a web performance test in VS 2015, and am trying to set request headers. When setting headers such as "Referer" or "User-Agent" everything seems to work fine. I just set the header by right-clicking the request and adding a new header, and when I run the test and inspect the request I can see that the new header has been added. However if I try to add a new header named "Content-Length" and then run the test, this header is missing from the request. I'm getting an http error which I assume is because VS doesn't use the "Content-Length" header:
HTTP Error 411. The request must be chunked or have a content length.
I ran into this problem as well. The consistent trend with these issues online seems to be when the content-length is 0. Otherwise VS seems to dynamically come up with the content-length header on it's own.
Explanation of Problem
Esentially, I don't think you can add it manually. VS wants to add this itself, so the real problem is why isn't VS adding this?
The problem comes from the request being simply a "Request" and not a "Web Service Request", the significant difference here being that the latter includes the "String Body" component. I'm assuming that VS does this because the content-length it recorded was 0. I recorded my workflow in Fiddler and got the same result. I would also assume that the reason adding the Content-Length header doesn't work is because this gets calculated by Visual Studio if the request has a body in the request.
The Workaround
Make the request a "Web Service Request" so that it includes the String Body component. In the test, right click and choose "Insert Web Service Request".
I got clued into this from the below post (which also includes an example for a coded UI test):
https://social.msdn.microsoft.com/Forums/en-US/da492346-760e-4971-a666-8897ae7b35e3/length-required-error?forum=vstswebtest
Plugin Option
I couldn't find a way to just convert my existing request into a Web Service Request, so I created a WebTestRequestPlugin that can be added to a request to dynamically add a body to the request.
Add the below to your project
Change the namespace to match your project's name
Build your solution.
Right click on the problematic request and click "Add Request Plugin" and then select this plugin.
using Microsoft.VisualStudio.TestTools.WebTesting;
using System.ComponentModel;
namespace YourProjectName
{
[DisplayName("Add Request Body")]
[Description("If a request is intended to have a content-length of 0, the Web Test won't include an empty String Body and the Content-Length header won't " +
"be added during test execution. This may result in a 411 - Length required error. This plugin can be added to that request to add a body to the request" +
"during test execution.")]
public class AddBodyToRequestPlugin : WebTestRequestPlugin
{
public AddBodyToRequestPlugin()
{
}
public override void PreRequest(object sender, PreRequestEventArgs e)
{
// Add string body if it doesn't exist yet.
if ( e.Request.Body == null )
{
e.Request.Body = new StringHttpBody();
}
}
}
}

Grails Canoo Webtest plugin: invoke() fails to send JSON data in POST request

I'm evaluating Canoo Webtest for automated integration/functional testing as a Grails plugin.
I have a REST app which I'm attempting to test, but Canoo Webtest doesn't seem to properly send the JSON data in POST request. My test code is like below
invoke( description:"Add a product to shopping cart",
url:'shoppingCart/add', method:'POST',
content:'{"class":"shop.service.Product", "name":"A product", "description":"Manufactured by X", "price":99.9}'
//contentFile: '../product.json'
)
The request body is empty no matter whether I use contentFile approach or inline the data as content attribute. The test report shows the data as being sent correctly, but error page shows an error stating 'JSONException: Missing value. at character 0 of '. JSON data as a response of GET request is coming back fine.
I have tested the same functionality with curl and it works perfectly fine. Is there something I'm missing in the Canoo Webtest setup?
Thanks.
EDIT: I'm using Grails 1.3.7 in case that makes any difference
OK. I started to just experiment with the invoke() and seems that setting attribute soapAction value to true does the trick.
I have no clue why this works. The documentation of invoke() for the attribute says
soapAction
Required? no
If the HTTP method is POST and is in fact a SOAP POST request,
this allows the SOAP Action header to be set. Ignored for GETs.
Apparently it sets some needed request header. Haven't checked which one.
Thank you for reading :)
Cannot yet mark this answer as the correct one, but will do so when it's possible.

How to set the default serializer in ASP.NET Web API?

I'm currently watching a video course about ASP.NET Web API. When a controller gets called, the data gets returned in JSON by default. I was just wondering, because when I copy this sample project from the video, I get XML.
The frustration is big, please help me to solve this.
I'm pretty new to ASP.NET Web API, so please bear with me.
UPDATE
The controller doesn't contain special code. It's the simple code, which gets generated from the API Controller with empty read/write actions template.
ASP.NET WebAPI comes with built-in content negotitation therefore the format of the return value is determined by the request itself - more specifically by the Accept/Content-Type headers (depending on which ones are present, Accept header appears to be favoured over the Content-type).
I assume you're viewing the results in a browser and by default it's probably asking for application/xml. You will need to toy around with some settings/developer tools on the browser and force it to send Content-Type: application/json to get the correct response (assuming your returning HttpResponseMessage).
in Global.asax: add the line:
GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
It'll look like this.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
BundleTable.Bundles.RegisterTemplateBundles();
GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
}
James is close, but the content negotiation actually uses the [Accept] header,
not [Content-Type]
As with nearly everything else in MVC, you can override the content negotiation components to ensure the desire content is returned
W3c clearly states-
14.1 Accept
The Accept request-header field can be used to specify certain media types which are acceptable for the response.
-and-
14.17 Content-Type
The Content-Type entity-header field indicates the media type of the entity-body sent to the recipient or, in the case of the HEAD method, the media type that would have been sent had the request been a GET.
This page headers is very useful to understand request/response negotiation.

Should I be using POST or GET when retrieving JSON data into jqGrid in my ASP.NET MVC application?

I am using jqgrid in my ASP.NET MVC application. Currently I have mTYpe: 'POST' like this:
jQuery("#myGrid").jqGrid({
mtype: 'POST',
toppager: true,
footerrow: haveFooter,
userDataOnFooter: haveFooter,
But I was reading this article, and I see this paragraph:
Browsers can cache images, JavaScript, CSS files on a user's hard
drive, and it can also cache XML HTTP calls if the call is a HTTP GET.
The cache is based on the URL. If it's the same URL, and it's cached
on the computer, then the response is loaded from the cache, not from
the server when it is requested again. Basically, the browser can
cache any HTTP GET call and return cached data based on the URL. If
you make an XML HTTP call as HTTP GET and the server returns some
special header which informs the browser to cache the response, on
future calls, the response will be immediately returned from the cache
and thus saves the delay of network roundtrip and download time.
Given this is the case, should I switch my jqGrid mType all to use "GET" from "POST" for the mType? (It says XML (doesn't mention JSON). If the answer is yes, then actually what would be a situation why I would ever want to use POST for jqGrid mType as it seems to do the same thing without this caching benefit?
The problem which you describe could be in Internet Explorer, but it will be not exist in jqGrid if you use default options.
If you look at the full URL which will be used you will see parameters like
nd=1339350870256
It has the same meaning as cache: true of jQuery.ajax. jqGrid add the current timestemp to the URL to make it unique.
I personally like to use HTTP GET in jqGrid, but I don't like the usage of nd parameter. The reason I described in the old answer. It would be better to use prmNames: {nd:null} option of jqGrid which remove the usage of nd parameter in the URL. Instead of that one can control the caching on the server side. For example the setting of
Cache-Control: private, max-age=0
is my standard setting. To set the HTTP header you need just include the following line in the code of ASP.NET MVC action
HttpContext.Current.Response.Cache.SetMaxAge (new TimeSpan (0));
You can find more details in the answer.
It's important to understand, that the header Cache-Control: private, max-age=0 don't prevent the caching of data, but the data will be never used without re-validation on the server. Using other HTTP header option ETag you can make the revalidate really working. The main idea, that the value of ETag will be always changed on changing the data on the server. In the case if the previous data are already in the web browser cache the web browser automatically send If-None-Match part in the HTTP request with the value of ETag from the cached data. So if the server see that the data are not changed it can answer with HTTP response having 304 Not Modified status and empty body of the HTTP response. It allows the web browser to use local previously cached data.
In the answer and in this one you will find the code example how to use ETag approach.
If the data that the server sends changes, then you should use POST to avoid getting cached data everytime you request it.
You should not use GET for all the purposes. GET requests are supposed to use for getting data from the server not for saving or deleting operation. GET requests has some limitation since the data you are sending to the server or appended as query-strings you can't send very large data using GET requests. Also you should not use GET request to send sensitive information to the server. You should the POST request in all the other cases like adding, editing and deleting.
As far as I'm aware jqgrid appends a unique key in every GET request so you don't get any benefit from browser caching.
One way around the caching behavior is to make the GET unique each time the request is made. jQuery.ajax() does this with "cache: false" by appending a timestamp to the end of the request. You can replicate this behavior with something similar:
uri = uri + '?_=' + (new Date()).getTime(); // uri represents the URI to the endpoint

asp.net MVC3 App_Offline.htm : possible to return application/json header?

When you want to put your site offline.
Almost everything in our application (API) is JSON. I think it might be better to return JSON, as the programs build on top of this API expects a JSON response on all of the methods.
Is it possible to return JSON instead with the correct content type in the App_Offline.htm?
Have you tried removing the ContentType header and adding it after :
http://i2.iis.net/ConfigReference/system.webServer/httpProtocol/customHeaders
I have never done that with app_offline.htm but might work.

Resources