.NET Service reference sending wrong accept header (text/plain) - asp.net-core

I've created an ASP.NET Core web application, and installed + used the Swashbuckle.AspNetCore version 6.1.5 Nuget package. This hosts the following openapi document on https://example.com/swagger/v1/swagger.json.
Also my API supports content-negotiation.
When sending no Accept header, or Accept: text/xml header, the api will return an XML string
When sending an Accept: application/json header, the api will return a JSON string
Now I've tried consuming my api through the swagger document:
Create a new .NET Core console application
Right-click the project file → Add → Service Reference
OpenAPI
URL: https://example.com/swagger/v1/swagger.json
Namespace: Example.Api
Class name: ExampleClient
You can then write a Main like this:
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
var httpClient = new System.Net.Http.HttpClient();
// httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
var mintPlayerClient = new MintPlayer.Api.MintPlayerClient("https://mintplayer.com", httpClient);
var artists = await mintPlayerClient.ApiArtistListAsync(false);
}
Now when you debug the console app
navigate to the ApiArtistListAsync method in the swaggerClient class
put a breakpoint at the client_.SendAsync call
you can now inspect what the swaggerClient is sending to the webservice
Usually it's like this:
SOAP = XML
REST = JSON
Even when adding a DefaultRequestHeader on the HttpClient the response from the HttpClient is an XML, because it's explicitly added inside the SwaggerClient method:
And here is how the code is generated + the line where the Accept header is explicitly set (swaggerClient:430). This is auto-generated code from adding the service-reference.
So why is the Accept header in the generated code explicitly set to text/plain? Why isn't the default accept header value application/json, since this is a REST service?

I think you might want to check this on both side, client(your console) and server(your api project).
We all know that usually
SOAP = XML
REST = JSON
But you're coding the whole things, total in-control of what being send and what being response.
Let's assume you client send Accept-Header which support both text/xml and text/plain (which as i understand here, you expect a response as text/plain).
Then the server realize that your console is happy with either text/xml and text/plain, and the server itself support all kind of common format.
So it'll have to electing the most convenient format to response to the client.
Which in this case is text/xml.
That's so, the console received and happy with text/xml response format either way
And if that's the case, that I get you right, you want to receive text/plain on the console, then make sure the only Accept header sending out is text/plain or do some custom logic on your API to choose the text/plain format over others when sending response.

Related

Moqui REST API call fail with error code 403

From my Angular 2 application I am trying to get data from Moqui but the request always fails with the error code 403.
Here is the REST API call implementation
getExample() {
let url = 'http://localhost:8080/rest/s1/example/examples'
let headers = new Headers({ 'Authorization': 'Basic
am9obi5kb2U6bW9xdWk='});
headers.append('Accept', 'application/json, text/plain, */*');
headers.append('Content-Type', 'application/json; charset=UTF-8');
let options = new RequestOptions({ headers: headers });
let response = this.http.get(url, options).map(res => res.json());
return response;
}
The Moqui logs :-
REST Access Forbidden (no authz): User null is not authorized for View on REST Path /example/examples
There is also a similar question Moqui Rest Nginx but from the answer I do not know that where I have to change the settings in Moqui.
On the client console the error is :-
XMLHttpRequest cannot load http://localhost:8080/rest/s1/example/examples. Response for preflight has invalid HTTP status code 403
But with a rest client like YARC it works :-
You must authenticate for REST API calls except for Service REST API paths that are configured to not require authentication (like the /mantle/my end points in the mantle.rest.xml file in the mantle-usl component).
You have authentication but then there is one other step: authorization. In general if authc is required then authorization is also required. This is done with database records usually either in seed data and can also be added using the System app that is included in the default Moqui runtime (ie the moqui/moqui-runtime repository).
There is an example of authorization setup for Service REST API calls in the MantleSetupData.xml file. The main difference from screen authorization is that the artifact type to use is 'AT_REST_PATH'. Here is that file on GitHub (right near the top of the file):
https://github.com/moqui/mantle-usl/blob/master/data/MantleSetupData.xml
The best documentation for most things to do with REST requests in Moqui, is currently in the comments in the 'rest.xml' file that actually processes the incoming requests (ie handles the /rest path). You can see this on GitHub here:
https://github.com/moqui/moqui-runtime/blob/master/base-component/webroot/screen/webroot/rest.xml

Azure Mobile Services .NET Client with Custom Authentication Missing X-ZUMO-AUTH Header and Failing API Authentication

I have the following scenario:
1) Azure Mobile Services API
2) Custom Authentication
3) Web and Xamarin Clients
4) Small test harness (.NET app using MSTest)
With the custom authentication, I can login from both the browser and the unit test and get back the appropriate userID and token.
Something like this:
var user = await Connect.MobileClient
.InvokeApiAsync<LoginRequest, MobileServiceUser>(
"CustomLogin", new LoginRequest()
{
username = username,
password = password
});
I then do the following:
Connect.MobileClient.CurrentUser = user;
Debugging makes it clear that the userId and the MobileServicesAuthenticationToken are correctly set.
When, I invoke the POST on my controller, however, I'm getting tossed out with unauthorized after the Initialize method on the controller executes.
A few notes:
1) Everything works swimmingly if I do the POST from the "try this out" (after registering, logging in, and pasting the token value in the X-ZUMO-AUTH header).
2) Likewise, no issue if I remove the
[AuthorizeLevel(AuthorizationLevel.User)]
from the controller class or method (tried both).
Here is the failing post (from Fiddler):
POST https://anapi.azure-mobile.net/tables/Organization__systemproperties=__createdAt%2C__updatedAt%2C__version%2C__deleted HTTP/1.1
X-ZUMO-FEATURES: TT
X-ZUMO-INSTALLATION-ID: dfddf760-ecef-49cb-8197-2faaaaa11502
X-ZUMO-APPLICATION: aaaaaauJOUyfFjjPmZpobTybtaaaaaa
Accept: application/json
User-Agent: ZUMO/1.3 (lang=Managed; os=Windows; os_version=6.2.0.9200; arch=Win32NT; version=1.3.30324.0)
X-ZUMO-VERSION: ZUMO/1.3 (lang=Managed; os=Windows; os_version=6.2.0.9200; arch=Win32NT; version=1.3.30324.0)
Content-Type: application/json; charset=utf-8
Host: anapi.azure-mobile.net
Content-Length: 110
Expect: 100-continue
Accept-Encoding: gzip
{"OrganizationId":0,"Name":"International Bozo, Inc.","Address":"Dallas, Texas","Accounts":null,"Courses":null}
The "try this" HTTP POST includes the X-ZUMO-AUTH header with the appropriate token.
You need to set the client.currentUser property before calling the client.invokeAPI method (it appears from your question you are doing the other order)
The X-ZUMO-AUTH header is then populated from the user.MobileAuthenticationToken
as shown here:
https://github.com/Azure/azure-mobile-services/blob/master/sdk/Managed/src/Microsoft.WindowsAzure.MobileServices/Http/MobileServiceHttpClient.cs#L566-L569

Unable to programmatically set Content-Type in BizTalk Wcf-Custom response port

I am attempting to receive JSON messages into BizTalk using the bLogical REST Start Kit for BizTalk (http://biztalkrest.codeplex.com/).
I am able to successfully receive a message, transform it, and return a response from my Orchestration, but when I transform the response back out through the BizTalkRESTResponseHandler, the HTTP Content-Type is being forced back to 'application/xml', even though I'm explicitly setting it to 'application/json'. The Content-Type is confirmed by tracing the Request and Response in Fiddler, as well as SoapUI.
The Accept: value on the Request is 'application/json'
Any ideas how I can trace further into the Wcf-Custom adapter stack to see where the Content-Type is being reset?
You can solve this by adding a HttpResponseMessageProperty before returning the message in the IDispatchMessageInspector. You can either do this directly in the BizTalkRESTResponseHandler IDispatchMessageInspector or in a separate one.
To do it in the BizTalkRESTResponseHandler get the source and add the following 3 lines of code in the BeforeSendReply method just above the "reply = newReply" in the end.
HttpResponseMessageProperty prop = new HttpResponseMessageProperty();
newReply.Properties.Add(HttpResponseMessageProperty.Name, prop);
prop.Headers.Add("Content-Type", "application/json;charset=utf-8");
Now instead of getting:
You will get this:

REST request doesn't work outside of Explorer

I'm using Restler's API Explorer (a fork of Swagger UI) and when I test a service call there it works fine but when I cut and paste the same URL into Chrome's Advanced REST Client I get a "403 Forbidden" error. How can that be? Is there some sort of required header parameter that needs to be passed with the request?
Here are the screen shots:
You need to get the content type right. Make sure you are sending the data as JSON (application/json)
You need to add a header
Content-Type application/json

WCF GZip Compression Request/Response Processing

How do I get a WCF client to process server responses which have been GZipped or Deflated by IIS?
On IIS, I've followed the instructions here on how to make IIS 6 gzip all responses (where the request contained "Accept-Encoding: gzip, deflate") emitted by .svc wcf services.
On the client, I've followed the instructions here and here on how to inject this header into the web request: "Accept-Encoding: gzip, deflate".
Fiddler2 shows the response is binary and not plain old Xml.
The client crashes with an exception which basically says there's no Xml header, which ofcourse is true.
In my IClientMessageInspector, the app crashes before AfterReceiveReply is called.
Some further notes:
(1) I can't change the WCF service or client as they are supplied by a 3rd party. I can however attach behaviors and/or message inspectors via configuration if this is the right direction to take.
(2) I don't want to compress/uncompress just the soap body, but the entire message.
Any ideas/solutions?
* SOLVED *
It was not possible to write a WCF extension to achieve these goals. Instead I followed this CodeProject article which advocate a helper class:
public class CompressibleHttpRequestCreator : IWebRequestCreate
{
public CompressibleHttpRequestCreator()
{
}
WebRequest IWebRequestCreate.Create(Uri uri)
{
HttpWebRequest httpWebRequest =
Activator.CreateInstance(typeof(HttpWebRequest),
BindingFlags.CreateInstance | BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.Instance,
null, new object[] { uri, null }, null) as HttpWebRequest;
if (httpWebRequest == null)
{
return null;
}
httpWebRequest.AutomaticDecompression =DecompressionMethods.GZip |
DecompressionMethods.Deflate;
return httpWebRequest;
}
}
and also, an addition to the application configuration file:
<configuration>
<system.net>
<webRequestModules>
<remove prefix="http:"/>
<add prefix="http:"
type="Pajocomo.Net.CompressibleHttpRequestCreator, Pajocomo" />
</webRequestModules>
</system.net>
</configuration>
What seems to be happening is that WCF eventually asks some factory or other deep down in system.net to provide an HttpWebRequest instance, and we provide the helper that will be asked to create the required instance.
In the WCF client configuration file, a simple basicHttpBinding is all that is required, without the need for any custom extensions.
When the application runs, the client Http request contains the header "Accept-Encoding: gzip, deflate", the server returns a gzipped web response, and the client transparently decompresses the http response before handing it over to WCF.
When I tried to apply this technique to Web Services I found that it did NOT work. Although the helper class was executed in the same was as when used by the WCF client, the http request did not contain the "Accept-Encoding: ..." header.
To make this work for Web Services, I had to edit the Web Proxy class, and add this method:
protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
System.Net.HttpWebRequest rq = (System.Net.HttpWebRequest)base.GetWebRequest(uri);
rq.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
return rq;
}
Note that it did not matter whether the CompressibleHttpRequestCreator and block from the application config file were present or not. For web services, only overriding GetWebRequest in the Web Service Proxy worked.
Thanks for your WCF tip! We're going to be enabling IIS compression for services at my shop, and I'm hoping your solution will work.
By "To make this work for Web Services" - did you mean old school SoapHttpProtocol clients?
Because the SoapHttpProtocol class has a built-in EnableDecompression property, which will automatically handle the Compression header and response handling.
Here's an answer I gave to another question on the subject. That questio was asked from the perspective of ADO.NET Data Services, but my answer was purely about WCF.