Trying to set headers for a http call but running into issues. Need guidance for both Authorization and a custom header x-api-key.
let url = "http://example.com"
let token = requestToken()
let request = WebRequest.Create(url) :?> HttpWebRequest
request.Method <- "GET"
request.Accept <- "application/json;charset=UTF-8"
request.Headers.Authorization <- sprintf "%s %s" token.token_type token.access_token
request.Headers["x-api-key"] <- "api-key" // custom headers
// or this
request.Headers["Authorization"] <- sprintf "%s %s" token.token_type token.access_token
The error I'm getting is
error FS3217: This expression is not a function and cannot be applied. Did you intend to access the indexervia expr.[index] instead?
The error message that you are getting actually tells you what the problem is. In F#, the syntax for using an indexer is obj.[idx] - you need to have a . between the object and the square bracket. The correct syntax in your specific case would be:
request.Headers.["x-api-key"] <- "api-key"
request.Headers.["Authorization"] <- sprintf "%s %s" token.token_type token.access_token
Related
I'm trying to build an Azure Function in F# with an Event Grid output binding. The code I have builds fine, but whenever I try and run it, I get the runtime exception: InvalidOperationException: 'Can't convert from type 'Azure.Messaging.EventGrid.EventGridEvent.
This is my code (note, I'm not currently using the outputEvents argument in the function, as I first wanted to make sure I could run the function before adding events to it):
[<FunctionName("EventListener")>]
let run ([<HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)>]req: HttpRequest)
([<EventGrid(TopicEndpointUri = "TopicEndpoint", TopicKeySetting = "TopicKey")>]outputEvents: IAsyncCollector<EventGridEvent>)
(log: ILogger) =
async {
log.LogInformation("F# HTTP trigger function processed a request.")
let nameOpt =
if req.Query.ContainsKey(Name) then
Some(req.Query.[Name].[0])
else
None
use stream = new StreamReader(req.Body)
let! reqBody = stream.ReadToEndAsync() |> Async.AwaitTask
let data = JsonConvert.DeserializeObject<NameContainer>(reqBody)
let name =
match nameOpt with
| Some n -> n
| None ->
match data with
| null -> ""
| nc -> nc.Name
let responseMessage =
if (String.IsNullOrWhiteSpace(name)) then
"This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
else
"Hello, " + name + ". This HTTP triggered function executed successfully."
return OkObjectResult(responseMessage) :> IActionResult
} |> Async.StartAsTask
What could be causing this issue?
I am using F# with HttpFs.Client and Hopac.
I am able to get Response body and value of each node of JSON/XML response by using code like:
[<Test>]
let ``Test a Create user API``() =
let response = Request.createUrl Post "https://reqres.in/api/users"
|> Request.setHeader (Accept "application/json")
|> Request.bodyString ReadFile
|> Request.responseAsString
|> run
printfn "Response of get is %s: " response
let info = JsonValue.Parse(response)
let ID = info?id
printfn "ID in Response is %i: " (ID.AsInteger())
But how do I get a response code, response headers, and response cookies? I need to get this inside the same method as shown above so that I can do the assertion on these items too.
I did try response.StatusCode, response.Cookies.["cookie1"] but there are no such methods comes up when I add period after response.
let response =
Request.createUrl Post "https://reqres.in/api/users"
|> Request.setHeader (ContentType (ContentType.create("application", "json")))
|> Request.bodyString token //Reading content of json body
|> HttpFs.Client.getResponse
|> run
Please read the doc https://github.com/haf/Http.fs
Point 3 shows how to access cookies and headers in the response.
response.StatusCode
response.Body // but prefer the above helper functions
response.ContentLength
response.Cookies.["cookie1"]
response.Headers.[ContentEncoding]
response.Headers.[NonStandard("X-New-Fangled-Header")]
I am new to F# and trying to automate APIs using FSharp.Data but I am not able to get basic auth working. I know there is some syntax issue that I am not able to crack. Please suggest how to get rid of this error on the "(BasicAuth "username_api" "pwd_api")" part of the code: This expression was expected to have type 'string' but here has type 'string * string'.
Also, I have attached an image for the quick reference.
Here is my test:
[<Test>]
let getBasicAuthUsingData () =
let Authorization (credentials:string) = "Authorization", credentials
let BasicAuth (username:string) (password:string) =
let base64Encode (s:string) =
let bytes = Encoding.UTF8.GetBytes(s)
Convert.ToBase64String(bytes)
sprintf "%s:%s" username password |> base64Encode |> sprintf "Basic %s" |> Authorization
let apiURL = "http://sampleapi"
let response = Http.RequestString( apiURL, httpMethod = "GET", headers = [ "Authorization", (BasicAuth "username_api" "pwd_api")])
printfn "Response of get is %s: " response
You're adding the name of the header twice: first in the Authorization function, and then in the list of headers for some reason.
As a result, the element of the header list ends up being ("Authorization", ("Authorization", "user:pass")) instead of just ("Authorization", "user:pass")
Just get rid is one of the "Authorization" strings:
let response = Http.RequestString( apiURL, httpMethod = "GET", headers = [BasicAuth "username_api" "pwd_api"])
I am parsing http responses from my server (Phoenix 1.3) on my Elm 0.18 frontend.
The response looks like this:
error: BadStatus { status = { code = 422, message = "Unprocessable Entity" }, headers = Dict.fromList [("cache-control","max-age=0, private, must-revalidate"),("content-type","application/json; charset=utf-8")], url = "http://localhost:4000/api/v1/sessions", body = "{\"error\":\"No user could be found\"}" }
I would like to extract the three-digit HTTP code as a String....in this case, "422".
What is the best way to parse this in Elm? I am using a very hacky method and I'd like to know what tools are best applied here.
errorCode : String -> String
errorCode =
error
|> Debug.log "error"
|> toString
|> String.split "code = "
|> List.drop 1
|> String.join ""
|> String.split ","
|> List.take 1
|> String.join ""
|> Debug.log "Error"
It looks like you have an Error from the elm-lang/http package. The string you quoted is just how Errors are rendered as strings in the console / debugger, I believe -- I don't think there's any parsing to be done, as such.
In other words, I think the function you want doesn't operate on Strings at all:
errorCode : Error -> Maybe String
errorCode err =
case err of
BadStatus response ->
Just response.status.code
_ ->
Nothing
(The elm compiler should of course tell you whether your String -> String or my Error -> Maybe String signature is correct.)
I wrote the following code to execute a SQLServer StoredProc in F#
module SqlUtility =
open System
open System.Data
open System.Data.SqlClient
SqlUtility.GetSqlConnection "MyDB"
|> Option.bind (fun con -> SqlUtility.GetSqlCommand "dbo.usp_MyStordProc" con)
|> Option.bind (fun cmd ->
let param1 = new SqlParameter("#User", SqlDbType.NVarChar, 50)
param1.Value <- user
cmd.Parameters.Add(param1) |> ignore
let param2 = new SqlParameter("#PolicyName", SqlDbType.NVarChar, 10)
param2.Value <- policyName
cmd.Parameters.Add(param2) |> ignore
Some(cmd)
)
|> Option.bind (fun cmd -> SqlUtility.ExecuteReader cmd)
|> Option.bind (fun rdr -> ExtractValue rdr)
let GetSqlConnection (conName : string) =
let conStr = ConfigHandler.GetConnectionString conName
try
let con = new SqlConnection(conStr)
con.Open()
Some(con)
with
| :? System.Exception as ex -> printfn "Failed to connect to DB %s with Error %s " conName ex.Message; None
| _ -> printfn "Failed to connect to DB %s" conName; None
let GetSqlCommand (spName : string) (con : SqlConnection) =
let cmd = new SqlCommand()
cmd.Connection <- con
cmd.CommandText <- spName
cmd.CommandType <- CommandType.StoredProcedure
Some(cmd)
let AddParameters (cmd : SqlCommand) (paramList : SqlParameter list) =
paramList |> List.iter (fun p -> cmd.Parameters.Add p |> ignore)
let ExecuteReader (cmd : SqlCommand ) =
try
Some(cmd.ExecuteReader())
with
| :? System.Exception as ex -> printfn "Failed to execute reader with error %s" ex.Message; None
I have multiple problems with this code
First and foremost the repeated use of Option.bind is very irritating... and is adding noise. I need a more clearer way to check if the output was None and if not then proceed.
At the end there should be a cleanupfunction where I should be able to close + dispose the reader, command and connection. But currently at the end of the pipeline all I have is the reader.
The function which is adding parameters... it looks like it is modifying the "state" of the command parameter because the return type is still the same command which was sent it... with some added state. I wonder how a more experienced functional programmer would have done this.
Visual Studio gives me a warning at each of the place where i do exception handling. what's wrong with that" it says
This type test or downcast will always hold
The way I want this code to look is this
let x : MyRecord seq = GetConnection "con" |> GetCommand "cmd" |> AddParameter "#name" SqlDbType.NVarchar 50 |> AddParameter "#policyname" SqlDbType.NVarchar 50 |> ExecuteReader |> FunctionToReadAndGenerateSeq |> CleanEverything
Can you recommend how can I take my code to the desired level and also any other improvement?
I think that using options to represent failed computations is more suitable to purely functional langauges. In F#, it is perfectly fine to use exceptions to denote that a computation has failed.
Your code simply turns exceptions into None values, but it does not really handle this situation - this is left to the caller of your code (who will need to decide what to do with None). You may as well just let them handle the exception. If you want to add more information to the exception, you can define your own exception type and throw that instead of leaving the standard exceptions.
The following defines a new exception type and a simple function to throw it:
exception SqlUtilException of string
// This supports the 'printf' formatting style
let raiseSql fmt =
Printf.kprintf (SqlUtilException >> raise) fmt
Using plain .NET style with a few simplifications using F# features, the code looks a lot simpler:
// Using 'use' the 'Dispose' method is called automatically
let connName = ConfigHandler.GetConnectionString "MyDB"
use conn = new SqlConnection(connName)
// Handle exceptions that happen when opening the connection
try conn.Open()
with ex -> raiseSql "Failed to connect to DB %s with Error %s " connName ex.Message
// Using object initializer, we can nicely set the properties
use cmd =
new SqlCommand( Connection = conn, CommandText = "dbo.usp_MyStordProc",
CommandType = CommandType.StoredProcedure )
// Add parameters
// (BTW: I do not think you need to set the type - this will be infered)
let param1 = new SqlParameter("#User", SqlDbType.NVarChar, 50, Value = user)
let param2 = new SqlParameter("#PolicyName", SqlDbType.NVarChar, 10, Value = policyName)
cmd.Parameters.AddRange [| param1; param2 |]
use reader =
try cmd.ExecuteReader()
with ex -> raiseSql "Failed to execute reader with error %s" ex.Message
// Do more with the reader
()
It looks more like .NET code, but that is perfectly fine. Dealing with databases in F# is going to use imperative style and trying to hide that will only make the code confusing. Now, there is a number of other neat F# features you could use - especially the support for dynamic operators ?, which would give you something like:
let connName = ConfigHandler.GetConnectionString "MyDB"
// A wrapper that provides dynamic access to database
use db = new DynamicDatabase(connName)
// You can call stored procedures using method call syntax
// and pass SQL parameters as standard arguments
let rows = db.Query?usp_MyStordProc(user, policy)
// You can access columns using the '?' syntax again
[ for row in rows -> row?Column1, row?Column2 ]
For more information about this, see the following MSDN series:
How to: Dynamically Invoke a Stored Procedure
Step 1: Create a Database and Show the Poll Options
Step 2: Implement Voting for an Option