I'm trying to use the Microsoft Azure OCR API found here for a React Native app.
I can get the API to work fine on local images with Postman, but for some reason, I get an "Unsupported Media Type" when I try using fetch within my app.
I originally called the api with this code:
_analyzeImage = () => {
const { image } = this.state;
const url = 'https://westcentralus.api.cognitive.microsoft.com/vision/v1.0/ocr';
const data = new FormData();
data.append(image);
fetch(url, {
method: 'post',
body: data,
headers: {
'Ocp-Apim-Subscription-Key': '***********************',
}
}).then(res => {
console.log(res)
});
}
Where image looks like:
That, when ran using the XCode simulator, yields:
And the response:
{
"code":"UnsupportedMediaType",
"requestId":"6ff43374-e5f9-4992-9657-82ec1e95b238",
"message": "Supported media types: application/octet-stream, multipart/form-data or application/json"
}
Weirdly, the content-type seemed to be test/plain. So, even though I thought that the FormData object was supposed to take care of content type, I tried adding 'content-type': 'multipart/form-data', but got the same response (although the content-type header in the network inspector did change to multipart/form-data.
I used create-react-native-app to set up the project, and want to to work on iOS and android. If anyone has any ideas - or any other ways to do OCR, if there's a better native solution - I'd appreciate it!
As stated in the doc page you link to, if you send
application/json, your payload must look like this:
{"url": "http://example.com/images/test.jpg"}
if application/octet-stream,
[Binary image data]
if multipart/form-data,
[Binary image data]
Right now you're not sending anything that matches expectations.
Example POST
The image,
Pass image by URL:
$ curl -v -X POST -H 'Ocp-Apim-Subscription-Key: 2exxxxxxxxxxxxxxxxxxxxxx' \
-H 'Content-type: application/json' \
--data-ascii '{ "url": "https://i.stack.imgur.com/RM7B3.png" }' \
https://westus.api.cognitive.microsoft.com/vision/v1.0/ocr
> POST /vision/v1.0/ocr HTTP/1.1
> Content-type: application/json
> Content-Length: 44
...
< HTTP/1.1 200 OK
< Content-Length: 196
< Content-Type: application/json; charset=utf-8
{
"language": "en",
...
"regions": [
{
...
"words": [
{
"boundingBox": "61,49,303,108",
"text": "hello."
}
...
or pass image by raw bytes:
$ curl -v -X POST -H 'Ocp-Apim-Subscription-Key: 2exxxxxxxxxxxxxxxxxxxxxx' \
-H 'Content-type: application/octet-stream' \
--data-binary #hello.png \
https://westus.api.cognitive.microsoft.com/vision/v1.0/ocr
> POST /vision/v1.0/ocr HTTP/1.1
> Content-type: application/octet-stream
> Content-Length: 11623
...
< HTTP/1.1 200 OK
< Content-Length: 196
< Content-Type: application/json; charset=utf-8
{
"language": "en",
...
"regions": [
{
...
"words": [
{
"boundingBox": "61,49,303,108",
"text": "hello."
}
...
Related
i transform a payload need to send as as multipart/form to request.
the payload is like below
{
"goods_id": "12345,
"color_id_1": 20,
"image_id_1": vars.png1, //this is binary ,image data
"color_id_2": 11,
"image_id_2": vars.png1, //this is binary ,image data
}
here is sample file which we need to transform, the file is base on postman script.
in postman we can choose image file from notebook. in payload, the image is an object
which is stored in variable .
POST /cooperate/RegistAllImage HTTP/1.1
Content-Type: multipart/form-data; boundary=--------------------------549967118512544277394909
User-Agent: PostmanRuntime/7.29.0
Accept: */*
Cache-Control: no-cache
Postman-Token: ddddd
Host: ddddd
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: *****
Content-Length: 40980
----------------------------549967118512544277394909
Content-Disposition: form-data; name="goods_id"
Content-Type: application/x-www-form-urlencoded
12345
----------------------------549967118512544277394909
Content-Disposition: form-data; name="color_id_1"
Content-Type: application/x-www-form-urlencoded
20
----------------------------549967118512544277394909
Content-Disposition: form-data; name="image_1"; filename="064df7cefee0421990ccb74a5c1f9ccf.jpg"
Content-Type: application/octet-stream
<064df7cefee0421990ccb74a5c1f9ccf.jpg>
----------------------------549967118512544277394909
Content-Disposition: form-data; name="color_id_2"
Content-Type: application/x-www-form-urlencoded
2
----------------------------549967118512544277394909
Content-Disposition: form-data; name="image_2"; filename="064df7cefee0421990ccb74a5c1f9ccf.jpg"
Content-Type: application/octet-stream
<064df7cefee0421990ccb74a5c1f9ccf.jpg>
You can very well use the Multipart form module or can construct the parts using the script below.
%dw 2.0
import dw::module::Multipart
output multipart/form-data boundary="--------------------------549967118512544277394909"
---
{
parts: {
part1: Multipart::field({
name: "goods_id",
value: payload.goods_id,
mime: "appliation/x-www-form-urlencoded"
}),
part2: Multipart::field({
name: "color_id_1",
value: payload.color_id_1,
mime: "appliation/x-www-form-urlencoded"
})
,
part3: Multipart::field({
name: "image_id_1",
value: payload.image_id_1,
mime: "appliation/octet-stream",
fileName: "064df7cefee0421990ccb74a5c1f9ccf.jpg"
})
,
part4: Multipart::field({
name: "color_id_2",
value: payload.color_id_2,
mime: "appliation/x-www-form-urlencoded"
})
,
part5: Multipart::field({
name: "image_id_2",
value: payload.image_id_2,
mime: "appliation/octet-stream",
fileName: "064df7cefee0421990ccb74a5c1f9ccf.jpg"
})
}
}
Answer to the question in comment:
%dw 2.0
import dw::module::Multipart
output multipart/form-data boundary="--------------------------549967118512544277394909"
---
{
parts: payload mapObject ((value, key, index) -> {
("part" ++ ((index)+1)): Multipart::field({
"name": key as String,
"value": value as String,
"mime": if(key startsWith "image_id_") "application/octet-stream" else "application/x-www-form-urlencoded",
("fileName": "filenmae.png" )if(key startsWith "image_id_")
})
})
}
you can use the Multipart dataweave module that will make it fairly simple.
You will need Multipart::form function to create the form, and pass an array of all the fields, that you have in your payload. I have used pluck for it and then mapped it using Multipart:field function to get an Array of type MultipartPart. Your transform module will look like this
%dw 2.0
import dw::module::Multipart
output multipart/form-data
---
Multipart::form(
payload pluck ((value, key) ->
if(key as String startsWith "image_id_")
Multipart::field(key as String, value, "application/octet-stream")
else Multipart::field( key as String, value as String)
)
)
I have been trying to use React to make API calls, I am able to successfully run GET and POST requests but when i ran PATCH, it return 400 error which is bad request. But i am able to run the same request successfully on Postman.
I have a feeling I am not passing the data in the right format, but the body and the headers are being passed in the same form as in the POST method which is working fine.
Also, the preflight request is being passed as OK (200) but the connection closes at PATCH request
This is my request, where the Id is being received from another component which is then passed to the API.
class AddPatients extends React.Component {
constructor(props)
{
super(props);
this.state = {message :''};
console.log(this.props.list_id);
}
addPatients(url)
{
let xhr = new XMLHttpRequest();
xhr.open('PATCH',url, true);
xhr.setRequestHeader("Authorization", "Auth_key");
xhr.setRequestHeader("X-OHP-Developer-API-Key", "API_Key ");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("Accept", "application/json");
let body = {
data:
[
{
//record 1
},
{
//record 2
}
]
}
const res = xhr.send(JSON.stringify(body));
console.log('the response is', res);
}
onButtonClick()
{
var url = 'the url link/' + this.props.list_id;
this.addPatients(url);
}
render() {
return(
<div>
<button onClick={this.onButtonClick.bind(this)}> Add patients to the list </button>
{this.props.list_id}
</div>
);
}
}
export default AddPatients;
It has nothing to do with the OPTIONS request i think because it returns OPTIONS IN Allow-Methods options like:
Request Method: OPTIONS
Status Code: 200 OK
Remote Address: 18.236.241.179:443
Referrer Policy: no-referrer-when-downgrade
HTTP/1.1 200 OK
Date: Tue, 31 Dec 2019 01:03:22 GMT
Server: Apache
Strict-Transport-Security: max-age=63072000; includeSubDomains
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,HEAD,DELETE,PUT,OPTIONS,PATCH
Access-Control-Allow-Headers: authorization,x-requested-with,x-ohp-developer-api-key,content-type
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 336
Keep-Alive: timeout=15, max=98
Connection: Keep-Alive
Content-Type: text/html; charset=iso-8859-1
Sorry, I resolved it. Was passing wring syntax in the JSON body, stupid mistake.
Goal
With nock, I am seeking a solution in mocking an a tiny PNG file upload via POST multipart/form-data.
curl: Box API upload PNG file
Following curl script presents how to upload a file through Box API, file name: 'dummy.png' in root directory '0'.
curl 'https://upload.box.com/api/2.0/files/content' \
--request POST \
--verbose \
--silent \
--header 'authorization: Bearer [** Access Token **]' \
--header 'Content-Type: multipart/form-data' \
--form attributes='{ "name": "dummy.png", "parent": { "id": "0" } }' \
--form file=#'./files/dummy.png'
Condensed response:
Success [HTTP status: 201]
{
"total_count": 1,
"entries": [
{
"type": "file",
"name": "dummy.png",
"id": "584886508967"
}
]
}
nock attempt: Box API upload PNG file
The next code snippet is using npm nock works, however, this mocking is incomplete:
const accessToken = v4();
const randomFileId = v4();
let boundary = '';
const scope = nock('https://upload.box.com/api/2.0/')
.log((m, d) => logger.debug(m, d))
.matchHeader('authorization', `Bearer ${accessToken}`);
scope
.matchHeader('content-type', val => {
const matches = val.match(/^multipart\/form-data; boundary=([a-zA-Z0-9\-]+)$/);
if (matches && matches.length > 1) {
boundary = matches[1];
}
return !!matches;
})
.post('/files/content', body => {
return true;
})
.reply(201, {
entries: [
{
id: randomFileId,
name: 'dummy.png',
type: 'file'
}
]
});
nock attempt: Missing form attributes and file binary
It is not clear to me how to include with nock code what is included in curl POST request:
--header 'Content-Type: multipart/form-data' \
--form attributes='{ "name": "dummy.png", "parent": { "id": "0" } }' \
--form file=#'./files/dummy.png'
I would like to include in nock POST request:
File dummy.png binary as defined in --form file=#'./files/dummy.png'
File upload metadata as defined by --form attributes='{ "name": "dummy.png", "parent": { "id": "0" } }'
Thank you, appreciate the assistance.
As you alluded to in your question, Nock does not require the form data in order to intercept the request and mock the response. However, if you're testing that your code is sending the correct request body, doing the assertion is good practice.
The --form flag in cURL is a helper that does different thing for different protocols.
For HTTP protocol family, this lets curl emulate a filled-in form in which a user has pressed the submit button. This causes curl to POST data using the Content-Type multipart/form-data according to RFC 2388.
Not overly helpful, but the gist is that the data you're looking for in Nock will be in the body of the POST request. The Nock code in your question is on the right track. The post method using a callback as the second argument is how you can dig into the raw data being intercepted by Nock. The not-so-straight-forward part is that the body argument passed to the callback is hex encoded because the body includes the binary data of the png file.
.post('/files/content', body => {
const decoded = Buffer.from(body, 'hex');
console.log(decoded);
return true;
})
Adding the snippet above to your existing post method should output something similar to:
----------------------------493956036364114509087826
Content-Disposition: form-data; name="attributes"
{"name":"dummy.png","parent":{"id":"0"}}
----------------------------493956036364114509087826
Content-Disposition: form-data; name="content"; filename="unused"
Content-Type: image/png
�PNG
IHDRĉ
IDATx�c��������IEND�B`�
----------------------------493956036364114509087826--
It would be up to you at that point to determine if the body includes the data you expect and return true if so.
Referencing RFC 2388 for what multipart form-data should look like could help.
So I have been looking around. Obviously there's a fair amt of on the web about these "Download Network Failded" problems. I feel our problem is somewhat unique in that we have one file. a 160kb tiff file (really it's a blog that we append a tiff extension too). I just stumbled on this when testing. It's a random image on my machine. I have much bigger and smaller files that process fine through the app. When debugging, the response looks good in fiddler, like any other good response. Also tracking the response through our React app it looks good all the way through. So the problem happens somewhere in Chrome and just for this one file. We've tried all standard stuff found here.
https://productforums.google.com/forum/#!topic/chrome/7XBU6g6_Ktc
Mainly fiddling with extensions (disabling them), download locations, reinstalling, etc. But the idea that is one smaller jpg file we are sending for conversion (the app is a basic convertor) has me perplexed. Has anyone ever seen something like this??
So here is how we handle the file in our redux action.
WE use these packages
import dataURLtoBlob from 'dataurl-to-blob';
import FileSaver from 'file-saver';
And we have a dispatch function we pass in for a response in our thunk (the fetch)
export function saveFile(data, fileName) {
return (dispatch) => {
var ie = navigator.userAgent.match(/MSIE\s([\d.]+)/),
ie11 = navigator.userAgent.match(/Trident\/7.0/) && navigator.userAgent.match(/rv:11/),
ieEDGE = navigator.userAgent.match(/Edge/g),
ieVer = (ie ? ie[1] : (ie11 ? 11 : (ieEDGE ? 12 : -1)));
if (ie && ieVer < 10) {
console.log("No blobs on IE ver<10");
return;
}
var mimeType = data.split(',')[0].split(':')[1].split(';')[0];
var extension = '';
if (mimeType.includes("zip")) {
extension = "zip"
}
else {
extension = mimeType.substr(mimeType.lastIndexOf('/') + 1);
}
var npmBlob = dataURLtoBlob(data);
if (ieVer > -1) {
FileSaver.saveAs(npmBlob, fileName + "." + extension);
} else {
var downloadLink = document.createElement("a");
document.body.appendChild(downloadLink);
downloadLink.style.display = "none";
downloadLink.href = data;
downloadLink.download = fileName;
downloadLink.click();
}
}
}
Relevant part of the fetch itself
}).then(response => {
//debugger;
var responseObj = JSON.parse(response);
//handle multi-retrieve
if (targetExtension.includes("/File/Retrieve")) {
for (let array of responseObj) {
if (array.ReturnDocument) {
if (responseObj.length > 1) {
dispatch(saveFile(responseObj[0].ReturnDocument, "testFiles_download"));
} else {
dispatch(saveFile(responseObj[0].ReturnDocument, responseObj[0].ticketID));
}
}
}
}
var returnObject = { returnResult: responseObj, loading: false };
return callback(returnObject);
Everything looks good. http status codes are 200 and all other files are working. There is really nothing special about this jpg we send in as far as we can tell. And it looks good coming back.
Here is the request sent in:
POST http://redacted/api/File/Convert HTTP/1.1
Host: redacted-dev
Connection: keep-alive
Content-Length: 168078
Origin: http://redacted-dev
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryhgZddb45UOHBhsgs
Accept: */*
Referer: http://redacted-dev/ui/Convert
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Here is the raw response
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.5
X-AspNet-Version: 4.0.30319
Persistent-Auth: true
X-Powered-By: ASP.NET
Date: Tue, 02 Jan 2018 14:12:17 GMT
Content-Length: 3707173
Here is what the blob looks like when we get it back(abbreviated):
ReturnDocument=data:image/tiff;base64,SUkqAAg+............
You can file saver package to download a blob object.
Usage Example is as below:
// FileSaver Usage
import FileSaver from 'file-saver';
fetch('/records/download', {
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
}).then(function(response) {
return response.blob();
}).then(function(blob) {
FileSaver.saveAs(blob, 'fileName.zip');
})
One more way to download a file is that you make a get request which sends file from the server.
Then you can simply do the following:
window.open('full server link');
Then your file will get start downloading.
I'm trying to call a REST API running locally using AngularJS. Here is the AngularJS code :
$http.defaults.headers.common = {"Access-Control-Request-Headers": "accept, origin, authorization"};
$http.defaults.headers.common['Authorization'] = 'Basic amF5M2RlYzpqYXk=';
$http({method: 'GET', url: 'http://127.0.0.1:5000/user/jay3dec'}).
success(function(data, status, headers, config) {
}).
error(function(data, status, headers, config) {
alert(data);
});
But I'm getting an errors in the browser console :
Refused to set unsafe header "Access-Control-Request-Headers"
I tried to query call the REST API running at http://127.0.0.1:5000/user/jay3dec using CURL.
curl -H "Origin: http://127.0.0.1:8000" -H "Authorization: Basic amF5M2RlYzpqYXk=" http://127.0.0.1:5000/user/jay3dec --verbose
And it gave the following output :
> GET /user/jay3dec HTTP/1.1
> User-Agent: curl/7.35.0
> Host: 127.0.0.1:5000
> Accept: */*
> Origin: http://127.0.0.1:8000
> Authorization: Basic amF5M2RlYzpqYXk=
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: application/json
< Content-Length: 454
< ETag: bff7b7db33baedb612276861e84faa8f7988efb1
< Last-Modified: Tue, 30 Dec 2014 14:32:31 GMT
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Headers: Authorization
< Access-Control-Allow-Methods: HEAD, OPTIONS, GET
< Access-Control-Allow-Max-Age: 21600
< Server: Eve/0.4 Werkzeug/0.9.6 Python/2.7.6
< Date: Sun, 25 Jan 2015 20:00:29 GMT
<
* Closing connection 0
{"username": "jay3dec", "_updated": "Tue, 30 Dec 2014 14:32:31 GMT", "password": "jay", "firstname": "jay", "lastname": "raj", "phone": "9895590754", "_links": {"self": {"href": "/user/54a2b77f691d721ee170579d", "title": "User"}, "parent": {"href": "", "title": "home"}, "collection": {"href": "/user", "title": "user"}}, "_created": "Tue, 30 Dec 2014 14:32:31 GMT", "_id": "54a2b77f691d721ee170579d", "_etag": "bff7b7db33baedb612276861e84faa8f7988efb1"}
Can any one spot what may be the issue ??
The code behind $http.defaults.headers.common is
var xhr = createXhr();
xhr.open(method, url, true);
forEach(headers, function(value, key) {
if (isDefined(value)) {
xhr.setRequestHeader(key, value);
}
});
...
function createXhr() {
return new window.XMLHttpRequest();
}
Referring to XMLHttpRequest specification , browser will terminate if header is a case-insensitive match for one of the following headers
Accept-Charset
Accept-Encoding
Access-Control-Request-Headers
Access-Control-Request-Method
Connection
Content-Length
...
That's why you can't use $http.defaults.headers.common to set Access-Control-Request-Headers header. Browser will handle request headers for you instead.
The problem is in CORS
You should make configure your server side to allow Authorization in header. I don't know what you are used for server but for my asp.net web api 2 server it look like:
Web.config
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type,Authorization" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>
......
For security reasons, browsers do not allow head has set some security risks, such as cookie, host, referer, etc. So, do not use the browser to parse the line head. It can be used on the server side set of agency sent.
Reference: Cross origin resource sharing