Why is FedEx API returning encoded characters? - api

FedEx Address Validation API is returning encoded characters and a 400 bad request error. Here is my request body with the secret key blocked out for obvious reasons:
POST /address/v1/addresses/resolve HTTP/1.1
Content-Type: application/json
x-customer-transaction-id:
x-locale: en_US
authorization: Bearer {my secret key}
{
"addressesToValidate": [
{
"address":{
"streetLines":[
"1234 Example Rd.",
""
],
"city":"Morris",
"stateOrProvinceCode":"AL",
"postalCode":"35116",
"countryCode":"US"
}
}]
}
This is what the encoded response looks like:
HTTP/1.1 400 BadRequest
Content-Type: application/json;charset=UTF-8
Content-Encoding: gzip
Connection: close
Server-Timing: cdn-cache; desc=MISS,edge; dur=115,origin; dur=62
Content-Length: 175
Date: Tue, 28 Jun 2022 18:47:56 GMT
Server: Layer7-API-Gateway
�ʱ�0�_��l�a�Ƞ���p�6�6�#B�������;2�d���+�)�$�4�B%b�L�3
W"�%�N�L��9�<���U9n7��,���REY����{j�Z�=��í�A��Z�8�L���J�Lvp��=���M#
i������ϱ
Here is the API documentation for reference: https://developer.fedex.com/api/en-us/catalog/address-validation/v1/docs.html#operation/Validate%20Address

The answer was looking up how to decompress gzip in the language I was using. With Outsystems, I downloaded a forge component called gzip that compresses and decompresses binary to and from plaintext.

Sample syntax to decompress fedex api error messages in java
Public apiCall()
{
try{
// api call
}catch(Exception e)
{
String errorMessage = deCompress(e.getResponseBodyAsByteArray());
}
}
private static String deCompress(byte[] str) throws IOException{
String decodedString= null;
ByteArrayInputStream byteA = new ByteArrayInputStream(str);
final GZIPInputStream gzipInput = new GZIPInputStream(byteA);
InputStreamReader reader = new InputStreamReader(gzipInput);
BufferedReader in = new BufferedReader(reader);
String readed="";
String errormessage = "";
while ((readed = in.readLine()) != null) {
System.out.println(readed);
errormessage=readed;
}
return errormessage;
}

Related

Spring WebFlux: Stream raw HTTP response string with headers for Mixed-Replace HTTP Response

Using Spring WebFlux I would like to return Mixed-Replace HTTP Response that looks something like this:
HTTP/1.1 200 Ok
Content-Type: multipart/x-mixed-replace; boundary=--icecream
--icecream
Content-Type: image/jpeg
Content-Length: [length]
[data]
--icecream
Content-Type: image/jpeg
Content-Length: [length]
[data]
where data is streamed from Flux (think Flux.interval(1000).map(fetchImageFrame)), but I can't find a way how to stream raw HTTP response data, most of the examples gives me access to HTTP body only, but not whole response where I can control HTTP headers.
Have you tried wrapping your Flux response in a ResponseEntity and setting the required headers on the ResponseEntity?
Something like:
#GetMapping(value = "/stream")
ResponseEntity<Flux<byte[]>> streamObjects() {
Flux<byte[]> flux = Flux.fromStream(fetchImageFrame()).delayElements(Duration.ofSeconds(5));
HttpHeaders headers = HttpHeaders.writableHttpHeaders(HttpHeaders.EMPTY);
headers.add("Content-Type", "multipart/x-mixed-replace; boundary=--icecream");
return new ResponseEntity<>(flux, headers, HttpStatus.OK);
}
private Stream<byte[]> fetchImageFrame() {
return List.of(
load("image1.jpg"),
load("image2.jpg"),
load("image3.jpg"),
load("image4.jpg")
).stream();
}
private byte[] load(String name) {
try {
byte[] raw = Files.readAllBytes(Paths.get(name));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
String headers =
"--icecream\r\n" +
"Content-Type: image/jpeg\r\n" +
"Content-Length: " + raw.length + "\r\n\r\n";
bos.write(headers.getBytes());
bos.write(raw);
return bos.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

Web Api 2 custom IHttpActionResult CORS. No 'Access-Control-Allow-Origin' header is present on the requested resource

I have a problem with CORS from my WEB API 2 to Angular application.
Everything is working fine till now and all the response headers are receiving the following:
Access-Control-Allow-Origin:*
Cache-Control:no-cache
Content-Length:24
Content-Type:application/json; charset=utf-8
Date:Mon, 13 Nov 2017 08:15:32 GMT
Expires:-1
Pragma:no-cache
Server:Microsoft-IIS/10.0
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET
X-SourceFiles:=?UTF-8?B?...
Now I created a custom IHttpActionResult like this:
public class ZipFileActionResult : IHttpActionResult
{
private const long BufferLength = 65536;
public ZipFileActionResult(string file)
{
this.Filepath = file;
}
public string Filepath { get; private set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage result = new HttpResponseMessage();
var zipf = new FilesStream(this.Filepath);
Action<Stream, HttpContent, TransportContext> writeToStream = zipf.WriteToStream;
result.Content = new PushStreamContent(writeToStream, new MediaTypeHeaderValue("application/" + "zip"));
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "filename.zip"
};
return Task.FromResult(result);
}
private async void OnStreamConnected(Stream outputStream, HttpContent content, TransportContext context)
{
try
{
var buffer = new byte[BufferLength];
using (var nypdVideo = File.Open(this.Filepath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
var videoLength = (int)nypdVideo.Length;
var videoBytesRead = 1;
while (videoLength > 0 && videoBytesRead > 0)
{
videoBytesRead = nypdVideo.Read(buffer, 0, Math.Min(videoLength, buffer.Length));
await outputStream.WriteAsync(buffer, 0, videoBytesRead);
videoLength -= videoBytesRead;
}
}
}
catch (HttpException ex)
{
return;
}
finally
{
// Close output stream as we are done
outputStream.Close();
}
}
}
and I use this in my DownloadCOntroller like this:
[HttpPost]
[Route("PaperCuts")]
public IHttpActionResult PaperCuts(List<SelectionObject> selections)
{
try
{
string sFileName = <filename> + ".zip";
return new ZipFileActionResult(sFileName);
}
}
catch (Exception ex)
{
throw ex;
}
}
I'm receiving the following error when I call this function correctly:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
And I'm receiving this as response header:
HTTP/1.1 500 Internal Server Error
Cache-Control: private
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcUHJvamVjdHNcS25pcHNlbGtyYW50XEtuaXBzZWxrcmFudEFQSVxLbmlwc2Vsa3JhbnRBUElcYXBpXGRvd25sb2FkXFBhcGVyQ3V0cw==?=
X-Powered-By: ASP.NET
Date: Mon, 13 Nov 2017 08:35:33 GMT
I also have this stated in my WebApiConfig.cs (It works for all my other requests except for this one).
var corsAttr = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(corsAttr);
So the problem is that the Access-Control-Allow-Origin header etc are not same as in ALL the other requests. So how is this possible and how can I fix this?
I hope I provided enough information for my question.
Kind regards,
D.
I found the answer and got reminded by #ICantSeeSharp
I added following code in my global exception handler:
public override bool ShouldHandle(ExceptionHandlerContext context)
{
return true;
}

Sending Multipart form data from windows phone to web api

Hi I want to send(post/put) some data(containing string, int and Stream) from windows phone 8.1 using HttpClient to web api. what is the best way to do that.
public async void Put(string uri)
{
var httpClient = new System.Net.Http.HttpClient();
MultipartFormDataContent content = new MultipartFormDataContent();
var stringContent = new StringContent("FirstName=MUH&LastName=Test", Encoding.UTF8, "multipart/form-data");
var test = new StreamContent(new MemoryStream());
content.Add(test);
content.Add(stringContent);
var message = await httpClient.PutAsync(url+"/UpdateTest", content);
message.EnsureSuccessStatusCode();
string content1 = await message.Content.ReadAsStringAsync();
}
api method in my mvc app
[AllowAnonymous]
[Route("~/api/account/UpdateTest")]
[HttpPut]
public async Task<object> UpdateTest()
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
try
{
var requestParts = await Request.Content.ReadAsMultipartAsync();
foreach (var part in requestParts.Contents)
{
//part is always StreamContent
var test = await part.ReadAsStreamAsync();
var test1 = await part.ReadAsStringAsync();
}
}
catch (Exception ex)
{ }
}
In my windows phone project I have passed 2 HttpContent, one is StreamContent where as other is StringContent. but in my web api put method both are StreamContent I do't know why.
and other problem is I have to parse the string key value in StingContnet. My question is what is the best way of sending/receiving multipart form data from windows phone 8.1 to web api,
Thanks
Following is an example(change this accordingly to your scenario):
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(baseAddress);
HttpRequestMessage request = new HttpRequestMessage();
MultipartFormDataContent mfdc = new MultipartFormDataContent();
mfdc.Add(new StringContent("Michael"), name: "FirstName");
mfdc.Add(new StringContent("Jordan"), name: "LastName");
mfdc.Add(new StreamContent(content: new MemoryStream(Encoding.UTF8.GetBytes("This is from a file"))),
name: "Data",
fileName: "File1.txt");
HttpResponseMessage response = await client.PostAsync(baseAddress + "/api/values", mfdc);
public async Task Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
MultipartFormDataStreamProvider prov = await Request.Content.ReadAsMultipartAsync<MultipartFormDataStreamProvider>(new MultipartFormDataStreamProvider(#"C:\uploadedfiles"));
// example of how you can read the form data
string firstName = prov.FormData["FirstName"];
// Get list of all files that have been uploaded and stored in the above provided root folder
Collection<MultipartFileData> files = prov.FileData;
}
Following is how request looks like in Fiddler tool:
POST http://localhost:9095/api/values HTTP/1.1
Content-Type: multipart/form-data; boundary="7560a854-a71a-4e55-9571-5c2de520f45f"
Host: kirandesktop:9095
Content-Length: 474
Expect: 100-continue
Connection: Keep-Alive
--7560a854-a71a-4e55-9571-5c2de520f45f
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=FirstName
Michael
--7560a854-a71a-4e55-9571-5c2de520f45f
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=LastName
Jordan
--7560a854-a71a-4e55-9571-5c2de520f45f
Content-Disposition: form-data; name=Data; filename=File1.txt; filename*=utf-8''File1.txt
This is from a file
--7560a854-a71a-4e55-9571-5c2de520f45f--
Also note that you can read the StreamContent anyway you want...in the following examples, I am simulating a request's body stream and reading it as a simple string or deserializing into an object of type Person.
StreamContent requestStream = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes("Hello World!")));
string data = await requestStream.ReadAsStringAsync();
//---------------------
StreamContent requestStream = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes("{ \"FirstName\" : \"Michael\" }")));
requestStream.Headers.ContentType = new MediaTypeHeaderValue("application/json");
Person person = await requestStream.ReadAsAsync<Person>();
I use a custom media type formatter that's based on the code in this article.
ASP.NET WebApi: MultipartDataMediaFormatter
One of the advantages over the MultipartFormDataStreamProvider is that I don't need to specify a folder to save the file data, so I can inspect the contents in memory. You might not want to do this with huge files though. There's alot a github repo for it too if you want to look at the code. Also, I get strongly typed objects for file and form data

File Upload with Additonal Form Data to Web Api from MVC

I am trying to upload a file with additional form data and post to Web API via MVC but i couldn't accomplish.
MVC Side
Firstly i got the submitted form at MVC. Here is the action for this.
[HttpPost]
public async Task<ActionResult> Edit(BrandInfo entity) {
try {
byte[] logoData = null;
if(Request.Files.Count > 0) {
HttpPostedFileBase logo = Request.Files[0];
logoData = new byte[logo.ContentLength];
logo.InputStream.Read(logoData, 0, logo.ContentLength);
entity.Logo = logo.FileName;
entity = await _repo.Update(entity.BrandID, entity, logoData);
}
else
entity = await _repo.Update(entity,entity.BrandID);
return RedirectToAction("Index", "Brand");
}
catch(HttpApiRequestException e) {
// logging, etc
return RedirectToAction("Index", "Brand");
}
}
Below code post the Multipartform to Web API
string requestUri = UriUtil.BuildRequestUri(_baseUri, uriTemplate, uriParameters: uriParameters);
MultipartFormDataContent formData = new MultipartFormDataContent();
StreamContent streamContent = null;
streamContent = new StreamContent(new MemoryStream(byteData));
streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") {
FileName = "\"" + fileName + "\"",
Name = "\"filename\""
};
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
formData.Add(streamContent);
formData.Add(new ObjectContent<TRequestModel>(requestModel, _writerMediaTypeFormatter), "entity");
return _httpClient.PutAsync(requestUri, formData).GetHttpApiResponseAsync<TResult>(_formatters);
As you can see i am trying to send file data and object with same MultipartFormDataContent. I couldn't find better way to send my entity as ObjectContent. Also i am using JSON.Net Serializer
Regarding to fiddler, post seems successfull.
PUT http://localhost:12836/api/brand/updatewithlogo/13 HTTP/1.1
Content-Type: multipart/form-data; boundary="10255239-d2a3-449d-8fad-2f31b1d00d2a"
Host: localhost:12836
Content-Length: 4341
Expect: 100-continue
--10255239-d2a3-449d-8fad-2f31b1d00d2a
Content-Disposition: form-data; filename="web-host-logo.gif"; name="filename"
Content-Type: application/octet-stream
GIF89a��L������X�������wW����������xH�U�)�-�k6�������v6�������̥�v�J���������7����V:�=#�ի�I(�xf�$�������
// byte data
// byte data
'pf�Y��y�ؙ�ڹ�(�;
--10255239-d2a3-449d-8fad-2f31b1d00d2a
Content-Type: application/json; charset=utf-8
Content-Disposition: form-data; name=entity
{"BrandID":13,"AssetType":null,"AssetTypeID":2,"Logo":"web-host-logo.gif","Name":"Geçici Brand","Models":null,"SupplierBrands":null}
--10255239-d2a3-449d-8fad-2f31b1d00d2a--
Web API Side
Finally i am catching post at Web API side and trying to parse but i couldn't. Because MultipartFormDataStreamProvider's FileData and FormData collections are allways empty.
[HttpPut]
public void UpdateWithLogo(int id) {
if(Request.Content.IsMimeMultipartContent()) {
var x = 1; // this code has no sense, only here to check IsMimeMultipartContent
}
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
try {
// Read the form data.
Request.Content.ReadAsMultipartAsync(provider);
foreach(var key in provider.FormData.AllKeys) {
foreach(var val in provider.FormData.GetValues(key)) {
_logger.Info(string.Format("{0}: {1}", key, val));
}
}
// This illustrates how to get the file names.
foreach(MultipartFileData file in provider.FileData) {
_logger.Info(file.Headers.ContentDisposition.FileName);
_logger.Info("Server file path: " + file.LocalFileName);
}
}
catch(Exception e) {
throw new HttpApiRequestException("Error", HttpStatusCode.InternalServerError, null);
}
}
I hope you can help to find my mistake.
UPDATE
I also realized that, if i comment out StreamContent or ObjectContent and only add StringContent, still i can't get anything from MultipartFormDataStreamProvider.
Finally i resolved my problem and it was all about async :)
As you can see at API action method i had called ReadAsMultipartAsync method synchrously but this was a mistake. I had to call it with ContinueWith so after i changed my code like below my problem solved.
var files = Request.Content.ReadAsMultipartAsync(provider).ContinueWith<HttpResponseMessage>(task => {
if(task.IsFaulted)
throw task.Exception;
// do additional stuff
});

Getting if-modified-since header to work with WCF

I am trying to get the "if-modified-since" header to work with my WCF web service.
When a user makes a request to my service, I add an ETag to the outgoing response that contains the timestamp of the request as follows:
var tag = String.Format("\"{0:o}\"", new DateTimeOffset(DateTime.Now));
This results in the following ETag header:
ETag: "2011-10-27T13:09:39.6242263-04:00"
I then take this value and echo it back as the if-modified-since header for subsequent requests like this:
If-Modified-Since:2011-10-27T13:09:39.6242263-04:00
When I examine WebOperationContext.Current.Headers.IfModifiedSince, I never get the value provided. The value is fixed at "12/31/1969 7:00:00 PM".
What am I doing wrong?
UPDATE
I should add that using Fiddler, I can set any value to the If-Modified-Since header and still get the same 1969 value in code.
First off, If-Modified-Since is about conditional GETs regarding the time of the last modification of the resource, while ETag is about conditional GETs regarding an identifier of the resources, so please be careful with mixing the two concepts.
The correct way of implementing support for If-Modified-Since in a WCF service is to use the CheckConditionalRetrieve passing a DateTime value in the WebOperationContext.Current.IncomingRequest object - see the code below for that. If the value of the IMS header is before the date you pass to CheckConditionalRetrieve, the method will exit at that point returning a 304 (Not Modified) response. Otherwise it will just continue. The code below shows that.
Another issue: even through the date format you're using (ISO 8601) works, but it's not correct based on the specification (section 14.25 in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html, and section 3.3.1 in http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1), so you should consider using a valid format to prevent future problems.
You can find a good post about conditional GET support in WCF at http://blogs.msdn.com/b/endpoint/archive/2010/02/25/conditional-get-and-etag-support-in-wcf-webhttp-services.aspx.
public class StackOverflow_7919718
{
[ServiceContract]
public class Service
{
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public string GetData()
{
Console.WriteLine("If-Modified-Since header (1): {0}", WebOperationContext.Current.IncomingRequest.IfModifiedSince);
WebOperationContext.Current.IncomingRequest.CheckConditionalRetrieve(DateTime.UtcNow);
return "Data";
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
host.Open();
Console.WriteLine("Host opened");
Console.WriteLine("Not sending If-Modified-Since header (should return 200):");
Util.SendRequest(baseAddress + "/GetData", "GET", null, null);
Console.WriteLine("Sending data in the past, ISO 8601 format (should return 200):");
Util.SendRequest(baseAddress + "/GetData", "GET", null, null,
new Dictionary<string, string> { { "If-Modified-Since", "2011-10-25T13:09:39.6242263-04:00" } });
Console.WriteLine("Sending data in the future, ISO 8601 format (should return 304):");
Util.SendRequest(baseAddress + "/GetData", "GET", null, null,
new Dictionary<string, string> { { "If-Modified-Since", "2021-10-25T13:09:39.6242263-04:00" } });
Console.WriteLine("Sending data in the past, RFC 1123 format (should return 200):");
Util.SendRequest(baseAddress + "/GetData", "GET", null, null,
new Dictionary<string, string> { { "If-Modified-Since", "Wed, 26 Oct 2011 01:00:00 GMT" } });
Console.WriteLine("Sending data in the future, RFC 1123 format (should return 304):");
Util.SendRequest(baseAddress + "/GetData", "GET", null, null,
new Dictionary<string, string> { { "If-Modified-Since", "Mon, 27 Oct 2031 10:00:00 GMT" } });
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
public static class Util
{
public static string SendRequest(string uri, string method, string contentType, string body)
{
return SendRequest(uri, method, contentType, body, null);
}
public static string SendRequest(string uri, string method, string contentType, string body, Dictionary<string, string> headers)
{
string responseBody = null;
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri);
req.Method = method;
if (headers != null)
{
foreach (string headerName in headers.Keys)
{
switch (headerName)
{
case "If-Modified-Since":
req.IfModifiedSince = DateTime.Parse(headers[headerName]);
break;
default:
req.Headers[headerName] = headers[headerName];
break;
}
}
}
if (!String.IsNullOrEmpty(contentType))
{
req.ContentType = contentType;
}
if (body != null)
{
byte[] bodyBytes = Encoding.UTF8.GetBytes(body);
req.GetRequestStream().Write(bodyBytes, 0, bodyBytes.Length);
req.GetRequestStream().Close();
}
HttpWebResponse resp;
try
{
resp = (HttpWebResponse)req.GetResponse();
}
catch (WebException e)
{
resp = (HttpWebResponse)e.Response;
}
if (resp == null)
{
responseBody = null;
Console.WriteLine("Response is null");
}
else
{
Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
foreach (string headerName in resp.Headers.AllKeys)
{
Console.WriteLine("{0}: {1}", headerName, resp.Headers[headerName]);
}
Console.WriteLine();
Stream respStream = resp.GetResponseStream();
if (respStream != null)
{
responseBody = new StreamReader(respStream).ReadToEnd();
Console.WriteLine(responseBody);
}
else
{
Console.WriteLine("HttpWebResponse.GetResponseStream returned null");
}
}
Console.WriteLine();
Console.WriteLine(" *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* ");
Console.WriteLine();
return responseBody;
}
}