WCF service contract to receive file upload from ExtJs front end - wcf

I looked through existing questions but could not find exact match. Maybe I'm missing something obvious, if so - please direct me in the proper place. Here is my problem:
I have ExtJs front and application that needs to upload binary data to the WCF back end. On the front end I have the following code (I use Ext.form.field.File control to let user select a file)
// Create a dummy form in the controller after user selected a file
var form = Ext.create('Ext.form.Panel', {
items: [ my_file_field ]
});
form.getForm().submit({
method: 'POST',
url: 'myservice.url',
...
});
On the backend I have the following contract:
namespace MyApp
{
[ServiceContract]
public interface ITransferService
{
[OperationContract]
[WebInvoke(UriTemplate = "UploadImage", Method = "POST")]
void SaveImage(Stream buffer);
}
}
It works 'fine' except one little thing: in the stream inside SaveImage() I get not only binary data from the file user selected but also bunch of headers and encoded fields:
------WebKitFormBoundary26wAkvwGTnAMELFM
Content-Disposition: form-data; name="ext-gen1654"; filename="photo.png"
Content-Type: application/octet-stream
..... binary data goes here ....
------WebKitFormBoundary26wAkvwGTnAMELFM--
What am I missing? How do I change the contract of the service so I get clean binary data?

I found this post: http://antscode.blogspot.com/2009/11/parsing-multipart-form-data-in-wcf.html
Does anybody know more simple solution? Not that proposed parsing logic is complicated, but I was under impression that this is a common task and there are should be easier way of doing it. I can't believe MSFT doesn't understand standard web pages POSTs in the WCF out-of-the-box...

This should return to you the files attached to the current request accessed but file key or index. Hope this helps!
Stream file = HttpContext.Current.Request.Files.Get(<file_key>).InputStream;

Related

Can't perform HTTP Post Action from Logic App to Asp.net Core Web API

I've built many Logic Apps. I've also integrated with the Logic App API. For some reason, a Post request to an Asp.net Core Web API won't work. It works in Postman, but I can't get Logic Apps to complete the request.
The request arrives at my Web API. I can step through it during a remote debug session. I'm using the [FromBody] decorator on the API method. All the string values in the object are null.
Logic App Headers
Accept = "application/json"
ContentType = "application/json"
ContentLength = "35"
Host = "****.centralus.logic.azure.com"
API method
[HttpPost]
[Route("CreateSomething")]
public async Task<IActionResult> CreateSomething([FromBody] MyObject object)
{
//Create something great
}
I think it might have something to do with the Headers. I noticed that the Postman request won't succeed unless I check the Host and Content-Length box in the Headers section. According to this article, Logic Apps ignores those Headers.
https://learn.microsoft.com/en-us/azure/connectors/connectors-native-http
I've built the HTTP Post Action using the API as well as configured it manually using the Logic App UI in Azure.
By the way, does anyone know the Expression that will automatically calculate the ContentLength?
UPDATE:
I finally figured this out. I had to do some Ninja coding crap to make this work. I'll post my solution tomorrow.
Does anyone know how to make this work? Thanks in advance!
When you use the Logic App API to programmatically create Logic Apps, you have to specify the Body class for when you do something like an HTTP Post. When the Body JSON displayed in the designer, it contained a single object with the objects properties. My API method could not handle this. The key was to simply post the properties in the JSON Body. To make matters worse, I'm doing two HTTP Posts in this particular Logic App. When I tried to add my object properties to the existing Body class, it caused my other HTTP Post to stop working. To overcome this, I had to create a Body2 class with the objects properties. I then had to use the following line of code to replace body2 with body before adding the JSON to the Logic App API call.
This did not work.
body = new Body()
{
object = new Object()
{
//Properties
}
}
This worked.
body2 = new Body2()
{
Type = 0,
Description = "#{items('For_each_2')?['day']?['description']}",
Locations = item.Locations,
Cold = "#{items('For_each_2')?['temperature']?['cold']?['value']}",
Hot = "#{items('For_each_2')?['temperature']?['hot']?['value']}",
Hide = 0
}
Notice I used Replace on body2.
var options = new JsonSerializerOptions { WriteIndented = true, IgnoreNullValues = true};
string jsonString = ReplaceFirst(JsonSerializer.Serialize(myApp, options), "schema", "$schema").Replace("_else", "else").Replace("_foreach", "foreach").Replace("body2", "body");

Enabling binary media types breaks Option POST call (CORS) in AWS Lambda

New to AWS..
We have a .NET Core Microservice running on a serverless aws instance as lambda functions.
Our Controller looks like this
[Route("api/[controller]")]
[ApiController]
public class SomeController : ControllerBase
{
[HttpGet()]
[Route("getsomedoc")]
public async Task<IActionResult> GetSomeDoc()
{
byte[] content;
//UI needs this to process the document
var contentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
contentDisposition.FileName = "File Name";
Response.Headers[HeaderNames.ContentDisposition] = contentDisposition.ToString();
return File(content, "application/octet-stream");
}
[HttpPost()]
[Route("somepost")]
public async Task<IActionResult> SomePost()
{
return null;
}
}
URL's
{{URL}}/getsomedoc
{{URL}}/somepost
We have enabled 'Binary Media Types' in AWS package settings to / for the getsomedoc to work otherwise it was returning the byte array back instead of the file.
But this is breaking our 'somepost' call when UI is accessing the API using
Method: OPTIONS & Access-Control-Request-Method as POST
When we remove the binary media type the 'somepost' starts working.
Looking for suggestions as why this might be happening? and what can we add/remove from gateway to get this fixed.
Well we ended up resolving this in a strange way.
Added two gateways for the lambda
- on one of them have binary enabled
- Disabled on the other one.
For
getsomedoc - Using the one where binary media types are enabled
postsomedoc - Using the other one
Wish there was a better way!!
I have found this same behavior with my API. While looking everywhere for some help, I found a few things that address the issue:
Basically, this bug report says the problem is having CORS enabled while also using the generic Binary Media Type "*/*". Apparently the OPTIONS method gets confused by this. They discuss this in terms of using Serverless, but it should apply to using the console or other ways of interacting with AWS.
They link to a possible solution: you can modify the Integration Response of the OPTIONS method - change the Mapping Template's Content-Type to an actual binary media type, like image/jpeg. They say this allows you to leave the binary media type in Settings as "*/*". This is a little hacky, but at least it is something.
There also was this alternate suggestion in the issues section of this GitHub repo that is a little less hacky. You can set the content handling parameter of the OPTIONS Integration Request to "CONVERT_TO_TEXT"... but you can only do this via CloudFormation or the CLI (not via the console). This is also the recommended solution by some AWS Technicians.
Another possible workaround is to setup a custom Lambda function to handle the OPTIONS request, this way the API gateway may have the "*/*" Binary Media Type.
Create a new lambda function for handling OPTIONS requests:
exports.handler = async (event) => {
const response = {
statusCode: 200,
headers:{
'access-control-allow-origin':'*',
'Access-Control-Allow-Headers': 'access-control-allow-origin, content-type, access-control-allow-methods',
'Access-Control-Allow-Methods':"GET,POST,PUT,DELETE,OPTIONS"
},
body: JSON.stringify("OK")
};
return response;
};
In your API Gateway OPTION method, change the integration type from Mock to Lambda Function.
Make sure to check 'Use Lambda proxy integration'
Select the correct region and point to the created Lambda Function
This way any OPTIONS request made from the browser will trigger the Lambda function and return the custom response.
Be aware this solution might involve costs.

Read raw Request.Body in Asp.Net Core MVC Action with Route Parameters

I need to process the raw request body in and MVC core controller that has route parameters
[HttpPut]
[Route("api/foo/{fooId}")]
public async Task Put(string fooId)
{
reader.Read(Request.Body).ToList();
await _store.Add("tm", "test", data);
}
but It seems like the model binder has already consumed the request stream by the time it gets to the controller.
If I remove the route parameters, I can then access the request stream (since the framework will no longer process the stream to look for parameters).
How can I specify both route parameters and be able to access Request Body without having to manually parse request URI etc.?
I have tried decorating my parameters with [FromRoute] but it had no effect.
Please note I cannot bind the request body to an object and have framework handle the binding, as I am expecting an extremely large payload that needs to be processed in chunks in a custom manner.
There are no other controller, no custom middle-ware, filters, serialzier, etc.
I do not need to process the body several times, only once
storing the stream in a temp memory or file stream is not an options, I simply want to process the request body directly.
How can I get the framework to bind paramters from Uri, QueryString, etc. but leave the request body to me?
Define this attribute in your code:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
var factories = context.ValueProviderFactories;
factories.RemoveType<FormValueProviderFactory>();
factories.RemoveType<JQueryFormValueProviderFactory>();
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
}
If you're targeting .Net Core 3 you also need to add
factories.RemoveType<FormFileValueProviderFactory>();
Now decorate your action method with it:
[HttpPut]
[Route("api/foo/{fooId}")]
[DisableFormValueModelBinding]
public async Task Put(string fooId)
{
reader.Read(Request.Body).ToList();
await _store.Add("tm", "test", data);
}
The attribute works by removing Value Providers which will attempt to read the request body, leaving just those which supply values from the route or the query string.
HT #Tseng for the link Uploading large files with streaming which defines this attribute
As I suspected the root cause was MVC inspecting the request body in order to try to bind route parameters. This is how model binding works by default for any routes that are not parameter-less, as per documentation.
The framework however does this only when the request content type is not specified, or when it is form data (multipart or url-encoded I assume).
Changing my request content-type to any thing other than form data (e.g. application/json) I can get the framework to ignore the body unless specifically required (e.g. with a [FromBody] route parameter). This is an acceptable solution for my case since I am only interested accepting JSON payloads with content-type application/json.
Implementation of DisableFormValueModelBindingAttribute in Uploading large files with streaming pointed out by #Tseng seems to be a better approach however, so I will look into using that instead, for complete

Grails how to post out to someone else's API

I am writing a Grails app, and I want the controller to hit some other API with a POST and then use the response to generate the page my user sees. I am not able to Google the right terms to find anything about posting to another page and receiving the response with Grails. Links to tutorials or answers like "Thats called..." would me much appreciated.
Seems like you are integrating with some sort of RESTful web service. There is REST client plugin, linked here.
Alternatively, its quite easy to do this without a plugin, linked here.
I highly recommend letting your controller just be a controller. Abstract your interface with this outside service into some class like OtherApiService or some sort of utility. Keep all the code that communicates with this outside service in one place; that way you can mock your integration component and make testing everywhere else easy. If you do this as a service, you have room to expand, say in the case you want to start storing some data from the API in your own app.
Anyway, cutting and posting from the linked documentation (the second link), the following shows how to send a GET to an API and how to set up handlers for success and failures, as well as dealing with request headers and query params -- this should have everything you need.
#Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.5.0-RC2' )
import groovyx.net.http.*
import static groovyx.net.http.ContentType.*
import static groovyx.net.http.Method.*
def http = new HTTPBuilder( 'http://ajax.googleapis.com' )
// perform a GET request, expecting JSON response data
http.request( GET, JSON ) {
uri.path = '/ajax/services/search/web'
uri.query = [ v:'1.0', q: 'Calvin and Hobbes' ]
headers.'User-Agent' = 'Mozilla/5.0 Ubuntu/8.10 Firefox/3.0.4'
// response handler for a success response code:
response.success = { resp, json ->
println resp.statusLine
// parse the JSON response object:
json.responseData.results.each {
println " ${it.titleNoFormatting} : ${it.visibleUrl}"
}
}
// handler for any failure status code:
response.failure = { resp ->
println "Unexpected error: ${resp.statusLine.statusCode} : ${resp.statusLine.reasonPhrase}"
}
}
You might also want to check out this, for some nifty tricks. Is has an example with a POST method.

Setting the HTTP Accept header for JsonRestStore

I'm using JsonRestStore but would like to add a custom Accept header to it. What's the best way to go about this?
This is similar to how the dijit.layout.ContentPane allows you to affect the underlying XHR by setting ioArgs. So the question could be "what is JsonRestStore's ioArgs?"
I'm using declarative syntax, but would gladly like to see both methods...
(Please note: I'm not interested in hacking my way around this by modifying the base XHR.)
Your best bet is providing a custom service to JsonRestStore. The easiest way I found of doing this is building the service from dojox.rpc.Rest. In the constructor you can provide a function to create the request arguments for all XHR requests. E.g.
function getRequest(id, args) {
return {
url: '/service/' + id,
handleAs: 'json',
sync: false,
headers: {
Accept: 'your custom header'
}
}
}
var service = new dojo.rpc.Rest('/service/', true /*isJson*/,
undefined /*schema*/, getRequest);
var store = new dojox.data.JsonRestStore({ service: service });
This completely ignores the args parameter that can include sorting and range arguments to your service.
These links will provide more information:
Use Dojo's JsonRestStore with your REST services: IBM developerWorks article with a more advanced and customizable solution
RESTful JSON + Dojo Data: Sitepen post
dojox.rpc.Rest source file (look for service._getRequest)