Bad Request -Post method - JSON DateTime issue - json

I have a Visual Studio (2015) project that includes a client part (Xamarin.Forms PCL) and a web service part ( WCF Rest). The web services use edmx to communicate with the database ( SQL Server 2016). JSON is used to exchange the data.
I'm new to creating/consuming WCF Rest services. I have no issue using the GET method but I'm stuck with an issue with a POST method.
This method is part of a service that works well: no issue for a GET based method. It works well when I test it from a URL or from my client ( PCL Xamarin.Forms).
The POST method (my first ever) is a bit more problematic.
It's supposed to create a new record in a table in SQL Server (2016).
When I use Postman (https://www.getpostman.com/) to test it, it already has an issue: it creates a record in the table but the object has two dates and the two dates are replaced by 1970-01-01.
When I use my client to contact the web service:I get 'Bad Request'.
I looked for a solution and found that instead of placing the Datetime value, it was best to place the number of milliseconds from 1970-01-01.
I used this advice in Postman and noticed the creation of a new line worked fine.
Body of the Postman request :
{
"Reservation_Utilisateur_Id" : "4",
"Reservation_Velo_Id" : "2",
"Reservation_DateDebut" : "\/Date(1245398693390)\/",
"Reservation_PeriodeDebut" : "matin",
"Reservation_DateFin" :"\/Date(1245398693390)\/",
"Reservation_PeriodeFin" : "matin"
}
Now, I'd like to know how to get that object to send to the server . How can my object be serialized like above?
I looked for a solution unsuccessfully.
I keep on getting "There was an error deserializing the object of type BikeSharingService.Reservation. DateTime content '2016-08-22T00:00:00+02:00' does not start with '/Date(' and end with ')/' as required for JSON."
Could someone please give the newbie that I am an explanation and maybe some code that works?
Here is my code:
My contract:
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "create",
ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
Reservation create(Reservation reservation);
The service method:
public Reservation create(Reservation reservation)
{
using (MyEntities bse = new MyEntities())
{
Reservation re = new Reservation
{
Reservation_Utilisateur_Id = reservation.Reservation_Utilisateur_Id,
Reservation_Velo_Id = reservation.Reservation_Velo_Id,
Reservation_DateDebut = reservation.Reservation_DateDebut,
Reservation_PeriodeDebut = reservation.Reservation_PeriodeDebut,
Reservation_DateFin = reservation.Reservation_DateFin,
Reservation_PeriodeFin = reservation.Reservation_PeriodeFin,
Reservation_DemandeRecupDomCli = reservation.Reservation_DemandeRecupDomCli
};
bse.Reservations.Add(re);
bse.SaveChanges();
return re;
}
}
On the client side :
const string Url1 = "http://localhost:51843/ServiceReservation.svc/create";
public async Task<Reservation> create(Reservation reservation)
{
string json = JsonConvert.SerializeObject(reservation);
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Accept", "application/json");
var response = await client.PostAsync(Url1,
new StringContent(
json,
Encoding.UTF8, "application/json"));
return JsonConvert.DeserializeObject<Reservation>(
await response.Content.ReadAsStringAsync());
}
Then calling the method on the client side :
Reservation re =new Reservation();
re.Reservation_Utilisateur_Id = 4;
re.Reservation_Velo_Id = 2;
re.Reservation_DateDebut = DateTime.Now.Date;
re.Reservation_PeriodeDebut = "matin";
re.Reservation_DateFin = DateTime.Now.Date;
re.Reservation_PeriodeFin = "matin";
re.Reservation_DemandeRecupDomCli = 1;
Reservation resultat = await reManager.create(re);
What I get :
False Bad Request Method: POST, RequestUri:
'http://localhost:51843/ServiceReservation.svc /create', Version: 2.0,
Content: System.Net.Http.StringContent, Headers: { Accept:
application/json Content-Type: application/json; charset=utf-8
Content-Length: 407 } BadRequest
1.1
There was an error deserializing the object of type
BikeSharingService.Reservation. DateTime content
'2016-08-22T00:00:00+02:00' does not start with '/Date(' and end with
')/' as required for JSON.

[Promoted from a comment]
Json doesn't define a standard date format, but it's worth noting that Json.Net (which is used by most of the web-facing parts of the .Net framework) supports multiple formats out of the box (and even custom ones).
If you can decide on a standard which works for all your clients, you can configure the Json (en/de)coding in .Net to use it natively.
See http://newtonsoft.com/json/help/html/datesinjson.htm for more information and details on how to specify a date format handler.
[Example code from link]
public void WriteJsonDates()
{
LogEntry entry = new LogEntry
{
LogDate = new DateTime(2009, 2, 15, 0, 0, 0, DateTimeKind.Utc),
Details = "Application started."
};
// default as of Json.NET 4.5
string isoJson = JsonConvert.SerializeObject(entry);
// {"Details":"Application started.","LogDate":"2009-02-15T00:00:00Z"}
JsonSerializerSettings microsoftDateFormatSettings = new JsonSerializerSettings
{
DateFormatHandling = DateFormatHandling.MicrosoftDateFormat
};
string microsoftJson = JsonConvert.SerializeObject(entry, microsoftDateFormatSettings);
// {"Details":"Application started.","LogDate":"\/Date(1234656000000)\/"}
string javascriptJson = JsonConvert.SerializeObject(entry, new JavaScriptDateTimeConverter());
// {"Details":"Application started.","LogDate":new Date(1234656000000)}
}

What you are trying to use is the Epoch DateTime or Unix DateTime.
To convert the DateTime object to epoch datetime you can create a helper method. It is either milliseconds or seconds from 1/1/1970.
Also if needed you can use the Noda DateTime instead of the .NET which has the method in it to convert.
You can create a New class with string data type for DateTime and have a Casting specified. Or you can write your on custom Serialize method.
By default DateTime format after serialization would be ISO 8601 Format.
Code to convert to Unix or Epoch Date time :
private static readonly DateTime EpochDateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public static long ConvertDateTimeToUnixTime(DateTime date, bool isDatarequiredInMilliSeconds = false, DateTimeKind dateTimeKind = DateTimeKind.Local)
{
return Convert.ToInt64((DateTime.SpecifyKind(date.Value, dateTimeKind).ToUniversalTime() - EpochDateTime).TotalSeconds) * (isDatarequiredInMilliSeconds ? 1000 : 1);
}
You can use the following if you need to convert back(source):
var milliseconds = "/Date(1245398693390)/".replace(/\/Date\((-?\d+)\)\//, '$1');
var actualDate = new Date(parseInt(milliseconds));

Related

API restSharp JSON payload remove Parameter

I am trying to create a 400 bad request, by removing an existing parameter from the payload in my step definitions. So, far i have tried various scenarios but not been successful in removing a parameter from the payload object. The payload object has been created a in separate class, at run time i just want to remove a single parameter (paymentAmount). How do i do this?
[Given(#"the person has not sent the payment amount")]
public void GivenTheValuerHasNotSentThePaymentAmount()
{
var requestHandler = scenarioContext.Get<RestRequestHandler>("requestHandler");
var invalidPaymentRequest = StatusRequest.PaymentStatus();
requestHandler.APIEndPoint = string.Format(requestHandler.APIEndPoint, paymentnumber);
requestHandler.Body = JsonConvert.SerializeObject(invalidPaymentRequest);
var response = requestHandler.MakeAPICall();
scenarioContext.Add("response", response);
}

How to parse a JSON response for APEX REST Service

I have written a REST class that sets a url as an endpoint to call an API. The API returns a timezone value. The url passes longitude and latitude as a parameter. The response I am getting is a complicated JSON list and I just need the Id value of the Timezone object. This is what I get as a JSON response for a lat/long value I passed:
{
"Version":"2019c",
"ReferenceUtcTimestamp":"2019-12-10T21:14:23.7869064Z",
"TimeZones":[
{
"Id":"America/Los_Angeles",
"Aliases":[
"US/Pacific",
"US/Pacific-New"
],
"Countries":[
{
"Name":"United States",
"Code":"US"
}
],
"Names":{
"ISO6391LanguageCode":"No supported language code supplied",
"Generic":"",
"Standard":"",
"Daylight":""
},
"ReferenceTime":{
"Tag":"PST",
"StandardOffset":"-08:00:00",
"DaylightSavings":"00:00:00",
"WallTime":"2019-12-10T13:14:23.7869064-08:00",
"PosixTzValidYear":2019,
"PosixTz":"PST+8PDT,M3.2.0,M11.1.0",
"Sunrise":"2019-12-10T07:42:22.383-08:00",
"Sunset":"2019-12-10T16:18:49.095-08:00"
},
"RepresentativePoint":{
"Latitude":34.05222222222222,
"Longitude":-118.24277777777777
},
"TimeTransitions":[
{
"Tag":"PST",
"StandardOffset":"-08:00:00",
"DaylightSavings":"00:00:00",
"UtcStart":"2019-11-03T09:00:00Z",
"UtcEnd":"2020-03-08T10:00:00Z"
},
{
"Tag":"PDT",
"StandardOffset":"-08:00:00",
"DaylightSavings":"01:00:00",
"UtcStart":"2020-03-08T10:00:00Z",
"UtcEnd":"2020-11-01T09:00:00Z"
},
{
"Tag":"PST",
"StandardOffset":"-08:00:00",
"DaylightSavings":"00:00:00",
"UtcStart":"2020-11-01T09:00:00Z",
"UtcEnd":"2021-03-14T10:00:00Z"
}
]
}
]
}
Here is my REST Service in APEX:
public class LPP_AccountTimeZone {
public static List<String> getTimeZone(String latitude, String longitude){
Http http = new Http();
HttpRequest req=new HttpRequest();
String url = 'https://atlas.microsoft.com/timezone/byCoordinates/json?subscription-key=XXXXXXXXXXXXXXXXXXXXXXXXXXXX&api-version=1.0&options=all&query='+latitude+','+longitude;
req.SetEndPoint(url);
req.setMethod('GET');
HttpResponse res=http.send(req);
if (res.getStatusCode() == 200) {
List<String> TimeZone = new List<String>();
TimeZoneJSON result = TimeZoneJSON.parse(res.getBody());
for(TimeZoneJSON.TimeZones t : result.timeZones){
System.debug('TimeZone is' + t.Id);
TimeZone.add(t.Id);
}
}
else{
System.debug('The status code returned was not expected: ' + res.getStatusCode() + ' ' + res.getStatus());
}
return TimeZone[0];
}
The response I got with this code (when I ran it in anonymous window) was:
TimeZone is{Aliases=(US/Pacific, US/Pacific-New), Countries=({Code=US, Name=United States}), Id=America/Los_Angeles, Names={Daylight=Pacific Daylight Time, Generic=Pacific Time, ISO6391LanguageCode=en, Standard=Pacific Standard Time}, ReferenceTime={DaylightSavings=00:00:00, PosixTz=PST+8PDT,M3.2.0,M11.1.0, PosixTzValidYear=2019, StandardOffset=-08:00:00, Sunrise=2019-12-12T07:44:13.44-08:00, Sunset=2019-12-12T16:18:47.934-08:00, Tag=PST, WallTime=2019-12-12T11:49:25.0802593-08:00}, Representativ
That is a lot of info. I just want the America/Los_Angeles part which is what Id equals (I have that bold in the response).
Another problem with this code is that it is not returning anything/is void class.
I need t return that value because a trigger is calling that method and will use this value to update a field.
Can anyone please correct my code so that it passes the correct json value and returns the value?
EDIT/UPDATE: The error I am now getting is "Variale does not exist: TimeZone (where the return statement is)
You could use JSON2APEX to easily generate an apex class from your JSON response. Just paste your full response in and click 'Create Apex'. This creates a class that represents your response so that you can easily retrieve fields from it (Keep in mind this will only really work if your response is static meaning the structure and naming stay the same). Have a look through the class that it generates and that will give you an idea of what to do. The class has a parse(String JSON) method which you can call passing in your JSON response to retrieve an instance of that class with your response values. Then it's just a matter of retrieving the fields you want as you would with any object.
Here is how getting the timezone id code would look if you take this route.
(Note: This assumes you keep the name of the class the standard 'JSON2Apex')
if (res.getStatusCode() == 200) {
JSON2Apex result = JSON2Apex.parse(res.getBody());
for(JSON2Apex.TimeZones t: result.timeZones){
System.debug('TimeZone is' + t.id);
tId.add(t);
}
}
To return a value just change void in the method signature to List<String> and return the tId list as follows return tId;

FullCalendar (v4) not working with valid JSON

I'm hoping that someone can please help with FullCalendar (v4) not loading events when using the Json option. It works fine when the same data is hard coded as an event.
The Json produced is valid - i.e. it validates with https://jsoncompare.com
I've spent an enormous amount of time trying to figure this out myself and I've hit a wall - so time to ask for help.
I've tried using the built in Net Json serializer - but this produces the wrong date format, so I've also tried newtonsoft Json.net, which does produce the correct date format for FullCallendar but still will not load events.
There are no JS console errors when using the JSON, it simply does not load into the calendar. The JSON is coming from the same domain (i.e. not affected by cross domain issue).
Any help/advice would be most welcome, thank you.
THIS WORKS PERFECTLY, WHEN THE EVENTS ARE HARDCODED:
var calendar = new FullCalendar.Calendar(calendarEl,
{
plugins: ['interaction', 'dayGrid', 'timeGrid'],
defaultDate: new Date(),
defaultView: 'timeGridWeek',
minTime: '07:00:00',
maxTime: '22:00:00',
timeZone: 'local',
header: {
left: 'prev,next today',
center: 'title',
right: 'timeGridDay,timeGridWeek,dayGridMonth'
},
events: [ //hardcoded events load just fine
{
id: '12',
title: 'Event Name',
start: '2019-08-28T08:00:00',
end: '2019-08-28T08:30:00'
}
]
});
calendar.render();
}
WHEN USING THE JSON OPTION, IT DOES NOT WORK:
//JSON provided events do not load
events: {
url:'/CourseTimetable/GetAllEventsAsJson'
}
ALTERNATIVE WAY OF PROVIDING FEED (without braces and "url:" prefix):
events:'/CourseTimetable/GetAllEventsAsJson'
THE URL WORKS FINE - validates as JSON - AND DOES NOT GENERATE ANY ERRORS - IT PRODUCES:
"[{\"id\":12,\"title\":\"Event 1\",\"start\":\"2019-08-29T08:00:00\",\"end\":\"2019-08-29T08:30:00\"}]"
HEADER and cors info:
Content-Type: application/json; charset=utf-8
Referer: http://localhost:54928/CourseTimetable
Request Method: GET
Status Code: 200 OK
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Thanks in advance for any help/advice :)
Here are the two alternative controller versions (with standard .net and then with json.net)
Standard .net
public JsonResult GetAllEventsAsJson(DateTime? start = null, DateTime? end = null)
{
var events = db.CourseTimetables.Where(p => p.StartTime >= start && p.EndTime <= end)
.Select(s => new
{
id = s.Id,
title = s.Title,
start = s.StartTime,
end = s.EndTime
}).ToList();
//using built in .NET serialiser
return new JsonResult { Data = events, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
OUTPUT FROM ABOVE ACTION (main difference being the outputted dates and escaping):
[{"id":12,"title":"Event 1","start":"\/Date(1567062000000)\/","end":"\/Date(1567063800000)\/"},{"id":13,"title":"Event 2","start":"\/Date(1567148400000)\/","end":"\/Date(1567150200000)\/"}]
Json.Net version
public ActionResult GetAllEventsAsJson(DateTime? start = null, DateTime? end = null)
{
var events = db.CourseTimetables.Where(p => p.StartTime >= start && p.EndTime <= end)
.Select(s => new
{
id = s.Id,
title = s.Title,
start = s.StartTime,
end = s.EndTime
}).ToList();
//USING JSON.NET
string jsonData = JsonConvert.SerializeObject(events);
return Json(jsonData, JsonRequestBehavior.AllowGet);
}
OUTPUT FROM ABOVE ACTION (dates are in correct iso format with this):
"[{\"id\":12,\"title\":\"Event 1\",\"start\":\"2019-08-29T08:00:00\",\"end\":\"2019-08-29T08:30:00\"},{\"id\":13,\"title\":\"Event 2\",\"start\":\"2019-08-30T08:00:00\",\"end\":\"2019-08-30T08:30:00\"}]"
This format: "[{\"id\":12,\"title\":\"Event 1\",\"start\":\ ...etc is the problem. You're double-serialising the data. See those outer quote marks (") and slashes \? The outer quote marks indicate that you are returning a simple string as the response (which is valid JSON, but will be treated as plain text by the parser), and the slashes are the result of having to escape the quote marks within the string. Thus the inner data, while it looks like JSON, is not treated as JSON by the client-side parser. Instead the whole thing is just treated as one big string, with no objects, properties etc within it.
In ASP.NET MVC, return Json... expects you to supply it with an object. It will then automatically serialise that to JSON for you. You don't need to serialise it before you pass it in. Just remove that code and send events directly to the Json() method:
public ActionResult GetAllEventsAsJson(DateTime? start = null, DateTime? end = null)
{
var events = db.CourseTimetables.Where(p => p.StartTime >= start && p.EndTime <= end)
.Select(s => new
{
id = s.Id,
title = s.Title,
start = s.StartTime,
end = s.EndTime
}).ToList();
return Json(events, JsonRequestBehavior.AllowGet);
}
P.S. What version of MVC are you using? The more recent, supported versions use JSON.NET as the default serialiser anyway, so there should be no reason to have to do it manually. If you have an older version of MVC (which it looks like you might have since it's producing those odd date formats e.g. Date(1567062000000)\) and cannot upgrade for some reason, there are ways to make it use JSON.NET by default. Alternatively, you could do the serialisation yourself using JSON.NET, and then just return a plain string from your action method.
P.P.S. Another alternative to the above is to use the momentJS plugin in fullCalendar which then allows you to use momentJS to parse your dates - and momentJS includes the ability to parse ASP.NET's old date format.

WCF GET returning XML instead of JSON on server

I have a WCF service with a webHttpBinding defined. The interface has a single method:
[OperationContract(Action = "*")]
[WebGet(UriTemplate = "/",RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
Stream GetServerInfo();
Which returns a stream with encoded JSON:
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
ServerData r = ServerData.Instance;
r.upTime = (DateTime.Now - r._startupTime).ToString(#"dd\.hh\:mm\:ss");
using (Process proc = Process.GetCurrentProcess())
{
r.usedMemory = ((double)proc.PrivateMemorySize64) / 1024 / 1024;
}
r.activeSessions = getServiceData().Count();
string jsCode = "displayData" + "("+
new JavaScriptSerializer().Serialize(r)
+")";
WebOperationContext.Current.OutgoingResponse.ContentType = "application/javascript";
Console.WriteLine(jsCode);
return new MemoryStream(Encoding.UTF8.GetBytes(jsCode));
When used on my devel PC it works fine. I get sth like:
displayData({"_startupTime":"\/Date(1435867525056)\/","serverVersion":"1.0.0.8","startUpTime":"2. 7. 2015 22:05:25","acceptedConnections":0,"upTime":"00.00:00:00","usedMemory":21.265625,"activeSessions":0})
However, after deploy to a remote server I get only following response and I want to get a JSON:
<GetServerInfoResponse xmlns="http://tempuri.org/"><GetServerInfoResult>ZGlzcGxheURhdGEoeyJfc3RhcnR1cFRpbWUiOiJcL0RhdGUoMTQzNTg2ODUwMjc5NClcLyIsInNlcnZlclZlcnNpb24iOiIxLjAuMC44Iiwic3RhcnRVcFRpbWUiOiI3LzIvMjAxNSAxOjIxOjQyIFBNIiwiYWNjZXB0ZWRDb25uZWN0aW9ucyI6MCwidXBUaW1lIjoiMDAuMDA6MDA6MDAiLCJ1c2VkTWVtb3J5IjoyNy40NzY1NjI1LCJhY3RpdmVTZXNzaW9ucyI6MH0p</GetServerInfoResult></GetServerInfoResponse>
Note I call for the request locally directly on the server. But on remote call over network the response is the same. If I put a log output of the string to console I can see the output string is correct.
The config files are identical (except for addresses).
--edit
With the try-fail method I found out the string inside the XML response is the JSON string encoded in Base64.
Can somebody please help me whats wrong?
After some research I did not find any solution. However, after restarting OS (Win Server 2012) and rebuilding it just works like a charm.

Posting a File and Associated Data to a RESTful WebService preferably as JSON

In an application I am developing RESTful API and we want the client to send data as JSON. Part of this application requires the client to upload a file (usually an image) as well as information about the image.
I'm having a hard time tracking down how this happens in a single request. Is it possible to Base64 the file data into a JSON string? Am I going to need to perform 2 posts to the server? Should I not be using JSON for this?
As a side note, we're using Grails on the backend and these services are accessed by native mobile clients (iPhone, Android, etc), if any of that makes a difference.
I asked a similar question here:
How do I upload a file with metadata using a REST web service?
You basically have three choices:
Base64 encode the file, at the expense of increasing the data size by around 33%, and add processing overhead in both the server and the client for encoding/decoding.
Send the file first in a multipart/form-data POST, and return an ID to the client. The client then sends the metadata with the ID, and the server re-associates the file and the metadata.
Send the metadata first, and return an ID to the client. The client then sends the file with the ID, and the server re-associates the file and the metadata.
You can send the file and data over in one request using the multipart/form-data content type:
In many applications, it is possible for a user to be presented with
a form. The user will fill out the form, including information that
is typed, generated by user input, or included from files that the
user has selected. When the form is filled out, the data from the
form is sent from the user to the receiving application.
The definition of MultiPart/Form-Data is derived from one of those
applications...
From http://www.faqs.org/rfcs/rfc2388.html:
"multipart/form-data" contains a series of parts. Each part is
expected to contain a content-disposition header [RFC 2183] where the
disposition type is "form-data", and where the disposition contains
an (additional) parameter of "name", where the value of that
parameter is the original field name in the form. For example, a part
might contain a header:
Content-Disposition: form-data; name="user"
with the value corresponding to the entry of the "user" field.
You can include file information or field information within each section between boundaries. I've successfully implemented a RESTful service that required the user to submit both data and a form, and multipart/form-data worked perfectly. The service was built using Java/Spring, and the client was using C#, so unfortunately I don't have any Grails examples to give you concerning how to set up the service. You don't need to use JSON in this case since each "form-data" section provides you a place to specify the name of the parameter and its value.
The good thing about using multipart/form-data is that you're using HTTP-defined headers, so you're sticking with the REST philosophy of using existing HTTP tools to create your service.
I know that this thread is quite old, however, I am missing here one option. If you have metadata (in any format) that you want to send along with the data to upload, you can make a single multipart/related request.
The Multipart/Related media type is intended for compound objects consisting of several inter-related body parts.
You can check RFC 2387 specification for more in-depth details.
Basically each part of such a request can have content with different type and all parts are somehow related (e.g. an image and it metadata). The parts are identified by a boundary string, and the final boundary string is followed by two hyphens.
Example:
POST /upload HTTP/1.1
Host: www.hostname.com
Content-Type: multipart/related; boundary=xyz
Content-Length: [actual-content-length]
--xyz
Content-Type: application/json; charset=UTF-8
{
"name": "Sample image",
"desc": "...",
...
}
--xyz
Content-Type: image/jpeg
[image data]
[image data]
[image data]
...
--foo_bar_baz--
Here is my approach API (i use example) - as you can see, you I don't use any file_id (uploaded file identifier to the server) in API:
Create photo object on server:
POST: /projects/{project_id}/photos
body: { name: "some_schema.jpg", comment: "blah"}
response: photo_id
Upload file (note that file is in singular form because it is only one per photo):
POST: /projects/{project_id}/photos/{photo_id}/file
body: file to upload
response: -
And then for instance:
Read photos list
GET: /projects/{project_id}/photos
response: [ photo, photo, photo, ... ] (array of objects)
Read some photo details
GET: /projects/{project_id}/photos/{photo_id}
response: { id: 666, name: 'some_schema.jpg', comment:'blah'} (photo object)
Read photo file
GET: /projects/{project_id}/photos/{photo_id}/file
response: file content
So the conclusion is that, first you create an object (photo) by POST, and then you send second request with the file (again POST). To not have problems with CACHE in this approach we assume that we can only delete old photos and add new - no update binary photo files (because new binary file is in fact... NEW photo). However if you need to be able to update binary files and cache them, then in point 4 return also fileId and change 5 to GET: /projects/{project_id}/photos/{photo_id}/files/{fileId}.
I know this question is old, but in the last days I had searched whole web to solution this same question. I have grails REST webservices and iPhone Client that send pictures, title and description.
I don't know if my approach is the best, but is so easy and simple.
I take a picture using the UIImagePickerController and send to server the NSData using the header tags of request to send the picture's data.
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"myServerAddress"]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:UIImageJPEGRepresentation(picture, 0.5)];
[request setValue:#"image/jpeg" forHTTPHeaderField:#"Content-Type"];
[request setValue:#"myPhotoTitle" forHTTPHeaderField:#"Photo-Title"];
[request setValue:#"myPhotoDescription" forHTTPHeaderField:#"Photo-Description"];
NSURLResponse *response;
NSError *error;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
At the server side, I receive the photo using the code:
InputStream is = request.inputStream
def receivedPhotoFile = (IOUtils.toByteArray(is))
def photo = new Photo()
photo.photoFile = receivedPhotoFile //photoFile is a transient attribute
photo.title = request.getHeader("Photo-Title")
photo.description = request.getHeader("Photo-Description")
photo.imageURL = "temp"
if (photo.save()) {
File saveLocation = grailsAttributes.getApplicationContext().getResource(File.separator + "images").getFile()
saveLocation.mkdirs()
File tempFile = File.createTempFile("photo", ".jpg", saveLocation)
photo.imageURL = saveLocation.getName() + "/" + tempFile.getName()
tempFile.append(photo.photoFile);
} else {
println("Error")
}
I don't know if I have problems in future, but now is working fine in production environment.
FormData Objects: Upload Files Using Ajax
XMLHttpRequest Level 2 adds support for the new FormData interface.
FormData objects provide a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest send() method.
function AjaxFileUpload() {
var file = document.getElementById("files");
//var file = fileInput;
var fd = new FormData();
fd.append("imageFileData", file);
var xhr = new XMLHttpRequest();
xhr.open("POST", '/ws/fileUpload.do');
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
alert('success');
}
else if (uploadResult == 'success')
alert('error');
};
xhr.send(fd);
}
https://developer.mozilla.org/en-US/docs/Web/API/FormData
Since the only missing example is the ANDROID example, I'll add it.
This technique uses a custom AsyncTask that should be declared inside your Activity class.
private class UploadFile extends AsyncTask<Void, Integer, String> {
#Override
protected void onPreExecute() {
// set a status bar or show a dialog to the user here
super.onPreExecute();
}
#Override
protected void onProgressUpdate(Integer... progress) {
// progress[0] is the current status (e.g. 10%)
// here you can update the user interface with the current status
}
#Override
protected String doInBackground(Void... params) {
return uploadFile();
}
private String uploadFile() {
String responseString = null;
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("http://example.com/upload-file");
try {
AndroidMultiPartEntity ampEntity = new AndroidMultiPartEntity(
new ProgressListener() {
#Override
public void transferred(long num) {
// this trigger the progressUpdate event
publishProgress((int) ((num / (float) totalSize) * 100));
}
});
File myFile = new File("/my/image/path/example.jpg");
ampEntity.addPart("fileFieldName", new FileBody(myFile));
totalSize = ampEntity.getContentLength();
httpPost.setEntity(ampEntity);
// Making server call
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
int statusCode = httpResponse.getStatusLine().getStatusCode();
if (statusCode == 200) {
responseString = EntityUtils.toString(httpEntity);
} else {
responseString = "Error, http status: "
+ statusCode;
}
} catch (Exception e) {
responseString = e.getMessage();
}
return responseString;
}
#Override
protected void onPostExecute(String result) {
// if you want update the user interface with upload result
super.onPostExecute(result);
}
}
So, when you want to upload your file just call:
new UploadFile().execute();
I wanted send some strings to backend server. I didnt use json with multipart, I have used request params.
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public void uploadFile(HttpServletRequest request,
HttpServletResponse response, #RequestParam("uuid") String uuid,
#RequestParam("type") DocType type,
#RequestParam("file") MultipartFile uploadfile)
Url would look like
http://localhost:8080/file/upload?uuid=46f073d0&type=PASSPORT
I am passing two params (uuid and type) along with file upload.
Hope this will help who don't have the complex json data to send.
You could try using https://square.github.io/okhttp/ library.
You can set the request body to multipart and then add the file and json objects separately like so:
MultipartBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("uploadFile", uploadFile.getName(), okhttp3.RequestBody.create(uploadFile, MediaType.parse("image/png")))
.addFormDataPart("file metadata", json)
.build();
Request request = new Request.Builder()
.url("https://uploadurl.com/uploadFile")
.post(requestBody)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
logger.info(response.body().string());
#RequestMapping(value = "/uploadImageJson", method = RequestMethod.POST)
public #ResponseBody Object jsongStrImage(#RequestParam(value="image") MultipartFile image, #RequestParam String jsonStr) {
-- use com.fasterxml.jackson.databind.ObjectMapper convert Json String to Object
}
Please ensure that you have following import. Ofcourse other standard imports
import org.springframework.core.io.FileSystemResource
void uploadzipFiles(String token) {
RestBuilder rest = new RestBuilder(connectTimeout:10000, readTimeout:20000)
def zipFile = new File("testdata.zip")
def Id = "001G00000"
MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>()
form.add("id", id)
form.add('file',new FileSystemResource(zipFile))
def urld ='''http://URL''';
def resp = rest.post(urld) {
header('X-Auth-Token', clientSecret)
contentType "multipart/form-data"
body(form)
}
println "resp::"+resp
println "resp::"+resp.text
println "resp::"+resp.headers
println "resp::"+resp.body
println "resp::"+resp.status
}