1

sorry for the multiple posts, I can't get a post request to Microsoft Graph to work, I'm following this documentation, I'm trying to create an event in

https://graph.microsoft.com/beta/myDomain/users/myEmail/calendar/events

I have a function and some helper classes to create the json object like so:

List<ToOutlookCalendar> toOutlook = new List<ToOutlookCalendar>();    
toOutlook.Add(new ToOutlookCalendar
        {
            Start = new End
            {
                DateTime = DateTimeOffset.UtcNow,
                TimeZone = "Pacific Standard Time"
            },
            End = new End
            {
                DateTime = DateTimeOffset.UtcNow,
                TimeZone = "Pacific Standard Time"
            },
            Body = new Body
            {
                ContentType = "HTML",
                Content = "testar for att se skit"
            },
            Subject = "testin",
            Attendees = new List<Attendee>
            {
                new Attendee
                {
                    EmailAddress = new EmailAddress
                    {
                        Address = "myEmail",
                        Name = "name"
                    },
                    Type = "Required"

                }

            },
            Token = tokenn

        });

        return new JsonResult
        {
            Data = toOutlook

        };

previously I was posting to: https://outlook.office.com/api/v2.0/myDomain/users/myEmail/calendar/events

which gave me an error 401, complaining about the token being to week. I created an x509 certificate but had no luck finding a way to upload it to my directory in azure and since I want to do everything programmatically and have succeeded so far and decided to take another approach and came upon the Microsoft graph documentation again.

I get my calendar events from after having authorized the application permissions for Calendars.ReadWrite: https://graph.microsoft.com/beta/myDomain/users/myEmail/calendar/events.

Anyhow my request looks like this and gives me a 400 Bad Request:

htttpclient.DefaultRequestHeaders.Add("Authorization", "Bearer " + tokenn);
htttpclient.DefaultRequestHeaders.Add("Host", "graph.microsoft.com");
htttpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

var stringPayload = await Task.Run(() => JsonConvert.SerializeObject(res));

var response = await htttpclient.PostAsync($"https://graph.microsoft.com/beta/{myDomain}/users/{myEmail}/calendar/events",
new StringContent(stringPayload, Encoding.UTF8, "application/json"));

Does anyone have any idea why? I'm following the documentation to the letter i believe but still get a 400 bad request.

Edit 1

I use these classes to create the event based on the documentation

 public class ToOutlookCalendar
    {
        [JsonProperty("Subject")]
        public string Subject { get; set; }

        [JsonProperty("Body")]
        public Body Body { get; set; }

        [JsonProperty("Start")]
        public End Start { get; set; }

        [JsonProperty("End")]
        public End End { get; set; }

        [JsonProperty("Attendees")]
        public List<Attendee> Attendees { get; set; }
    }

    public class Attendee
    {
        [JsonProperty("EmailAddress")]
        public EmailAddress EmailAddress { get; set; }

        [JsonProperty("Type")]
        public string Type { get; set; }
    }

    public class EmailAddress
    {
        [JsonProperty("Address")]
        public string Address { get; set; }

        [JsonProperty("Name")]
        public string Name { get; set; }
    }

    public class Body
    {
        [JsonProperty("ContentType")]
        public string ContentType { get; set; }

        [JsonProperty("Content")]
        public string Content { get; set; }
    }

    public class End
    {
        [JsonProperty("DateTime")]
        public DateTimeOffset DateTime { get; set; }

        [JsonProperty("TimeZone")]
        public string TimeZone { get; set; }
    }

Any help is appreciated!!

Ako
  • 93
  • 2
  • 13
  • 1
    You don't need the token in the request body (just in the header). Try removing the "Token" property from your "ToOutlookCalendar" class. – Michael Hufnagel Jul 04 '18 at 12:08
  • I completely forgot about that :) however I still get a error 400 Bad Request – Ako Jul 04 '18 at 12:24
  • You can try and remove `htttpclient.DefaultRequestHeaders.Add("Host", "graph.microsoft.com"); htttpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));`. These should not be necessary (application/json in `StringContent` is already enough). – Michael Hufnagel Jul 04 '18 at 12:30
  • @MichaelHufnagel you are right about htttpclient.DefaultRequestHeaders.Add("Host", "graph.microsoft.com");, it's not necessary, only something that i added but according to the documentation: htttpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); is necessary – Ako Jul 04 '18 at 12:34
  • Yes but the StringContent in PostAsync should already set the Content-Type header to application/json. Is there any error message delivered with the 400 bad request? – Michael Hufnagel Jul 04 '18 at 12:38
  • but it just set the Content-Type, dosen't the httpclient need to accept content-type: application/json? – Ako Jul 04 '18 at 12:40
  • `var postResponseString = await response.Content.ReadAsStringAsync()` Try reading the response in a string there should be a message in the string (you can delete the Comment with all the headers^^). – Michael Hufnagel Jul 04 '18 at 12:49
  • ah my bad :) : `"{\r\n \"error\": {\r\n \"code\": \"BadRequest\",\r\n \"message\": \"Invalid version\",\r\n \"innerError\": {\r\n \"request-id\": \"10df953d*\",\r\n \"date\": \"2018-07-04T13:12:25\"\r\n }\r\n }\r\n}"` – Ako Jul 04 '18 at 13:13
  • invalig version, does it mean "/v1.0/"? I will test with "/beta/" – Ako Jul 04 '18 at 13:13
  • after switching to "/beta/" it now says that the payload is empty: `{\r\n \"error\": {\r\n \"code\": \"BadRequest\",\r\n \"message\": \"Empty Payload. JSON content expected.\",\r\n \"innerError\": {\r\n \"request-id\": \"4704e708-5748-\",\r\n \"date\": \"2018-07-04T13:15:13\"\r\n }\r\n }\r\n}` – Ako Jul 04 '18 at 13:16
  • Both should work for getting a calendar event. Just tried it myself for: "https://graph.microsoft.com/beta/users/{myEmail}/calendar/events" and "https://graph.microsoft.com/v1.0/users//{myEmail}/calendar/events". Both seem to work fine. – Michael Hufnagel Jul 04 '18 at 13:17
  • Oh in that case your `var stringPayload` is probably empty. – Michael Hufnagel Jul 04 '18 at 13:19
  • hang on don't you need "**domain**/users/email/..."? – Ako Jul 04 '18 at 13:19
  • Nope don't need it^^. – Michael Hufnagel Jul 04 '18 at 13:20
  • yes the payload is empty for some reason, I will look up why :) – Ako Jul 04 '18 at 13:22
  • Just a guess but did you by chance forget to mark the properties in your ToOutlookCalendar class with e.g.`[JsonProperty("start")] public String Start { get; set; }` – Michael Hufnagel Jul 04 '18 at 13:34
  • No that's not it, I will upload the classes now, I reused some classes from when i fetch events from microsoft graph – Ako Jul 04 '18 at 13:37
  • "res" in `JsonConvert.SerializeObject(res)` is just one Element from the "toOutlook" list correct? If not are you sure it is not empty? Everythin else seems fine. You can make the [JsonProperty("start")] lowercase but thats not gonna fix an empty object ^^. – Michael Hufnagel Jul 04 '18 at 13:54
  • "res is not empty, var res = await ToOutlookKalendrar(); returns this [link](https://imgur.com/a/54Y7ptz) – Ako Jul 04 '18 at 14:14
  • now its complaining about "conentencoding" which is a property of the json object, `" \"message\": \"The property 'ContentEncoding' does not exist on type 'Microsoft.OutlookServices.Event'. Make sure to only use property names that are defined by the type or mark the type as open type` – Ako Jul 04 '18 at 14:35

1 Answers1

1

To make this a little shorter/summarize this is what i use to create an event and is necessary.

using (HttpClient c = new HttpClient())
{
     String requestURI = "https://graph.microsoft.com/v1.0/users/"+userEmail+"/calendar/events.";

     //with your properties from above except for "Token"
     ToOutlookCalendar toOutlookCalendar = new ToOutlookCalendar();

     HttpContent httpContent = new StringContent(JsonConvert.SerializeObject(toOutlookCalendar), Encoding.UTF8, "application/json");

     HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestURI);
     request.Content = httpContent;
     //Authentication token
     request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

     var response = await c.SendAsync(request);
     var responseString = await response.Content.ReadAsStringAsync();
}

You don't need these headers:

htttpclient.DefaultRequestHeaders.Add("Host", "graph.microsoft.com");
htttpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

And Token is not Part of an Event object so you should remove it from ToOutlookCalendar.

If you don't want to write all these JSONObjects yourself all the time you can use the Graph SDK for c# (also eliminates the risk that you forget a property/add a wrong one). They already have an object for Appointments called Event.

Edit

You also don't need the "myDomain" part in your request URL.

Michael Hufnagel
  • 537
  • 1
  • 3
  • 13
  • sry for the late reply, I kinda gave up on it yesterday and started working with googles calender :) I'll test it out but I'm pretty sure i do something stupid before the actual request, btw can I use an accesstoken that I got from client_secret grant_type? – Ako Jul 05 '18 at 08:11
  • one more thing my `ToOutlookCalendar` is a funktion, not a class, I'm confused bout what you are trying to say here "`ToOutlookCalendar toOutlookCalendar = new ToOutlookCalendar();`" – Ako Jul 05 '18 at 08:14
  • disregard the last comment :) – Ako Jul 05 '18 at 08:19
  • Don't really know want you mean by client_secret grant_type (maybe [client credentials grant flow](https://developer.microsoft.com/en-us/graph/docs/concepts/auth_v2_service)) but i guess if your request is returning a BadRequest error the token you are using should be fine (if not you are gonna get another error regarding the token). About the to OutlookCalendar i thought the `new ToOutlookCalendar` in your code probably means it's an object^^. – Michael Hufnagel Jul 05 '18 at 08:41
  • oh my bad when i retrive the accesstoken i use `"grant_type", "client_credentials"`, I know that I can use client_assertion grant_type with a x509 certificate but this will be a hassel for people using my application, I was getting 401 before but i was making a request to the wrong endpoint :). `new ToOutlookCalendar` is a list now, good news is that the payload is no longer empty but now it complains about `write requests (excluding delete) must contain the content-type header declaration` – Ako Jul 05 '18 at 08:53
  • oh nothing was done with the httpcontent, should be ``equest.Content = httpContent;` and now I'm back to the empty payload situation XD – Ako Jul 05 '18 at 09:08
  • Oh yeah my bad `request.Content = httpContent;` is missing like you said. Fixed it in the answer^^. – Michael Hufnagel Jul 05 '18 at 09:31
  • I knew it, I was doing something stupid before the actual request, your code that you posted works! Thank you so much, it's actually the first real project that I'm working with outside of school :) – Ako Jul 05 '18 at 09:50