Post events to Microsoft Graph c#
Asked Answered
J

1

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!!

Jubilation answered 4/7, 2018 at 11:16 Comment(20)
You don't need the token in the request body (just in the header). Try removing the "Token" property from your "ToOutlookCalendar" class.Unhouse
I completely forgot about that :) however I still get a error 400 Bad RequestJubilation
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).Unhouse
@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 necessaryJubilation
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?Unhouse
but it just set the Content-Type, dosen't the httpclient need to accept content-type: application/json?Jubilation
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^^).Unhouse
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}"Jubilation
invalig version, does it mean "/v1.0/"? I will test with "/beta/"Jubilation
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}Jubilation
Both should work for getting a calendar event. Just tried it myself for: "graph.microsoft.com/beta/users{myEmail}/calendar/events" and "graph.microsoft.com/v1.0/users/{myEmail}/calendar/events". Both seem to work fine.Unhouse
Oh in that case your var stringPayload is probably empty.Unhouse
hang on don't you need "**domain**/users/email/..."?Jubilation
Nope don't need it^^.Unhouse
yes the payload is empty for some reason, I will look up why :)Jubilation
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; }Unhouse
No that's not it, I will upload the classes now, I reused some classes from when i fetch events from microsoft graphJubilation
"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 ^^.Unhouse
"res is not empty, var res = await ToOutlookKalendrar(); returns this linkJubilation
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 typeJubilation
U
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.

Unhouse answered 4/7, 2018 at 15:12 Comment(8)
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?Jubilation
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();"Jubilation
disregard the last comment :)Jubilation
Don't really know want you mean by client_secret grant_type (maybe client credentials grant flow) 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^^.Unhouse
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<object> 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 declarationJubilation
oh nothing was done with the httpcontent, should be ``equest.Content = httpContent;` and now I'm back to the empty payload situation XDJubilation
Oh yeah my bad request.Content = httpContent; is missing like you said. Fixed it in the answer^^.Unhouse
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 :)Jubilation

© 2022 - 2024 — McMap. All rights reserved.