I have the following class:
class Loader
{
private string accessToken { get; set; }
private string refreshToken { get; set; }
private void Auth()
{
// ... retrieving access and refresh tokens or updating the access token
}
public Task<T> Get(string id)
{
try
{
// ... send get request and return the result
}
catch (HttpRequestException exception)
{
if (e.statusCode == HttpStatusCode.Unauthorized)
{
await this.Auth();
return await this.Get(id);
}
throw exception;
}
}
}
I wrote only Get method, but there will be more, like Put, Delete and so on. In every method I have this repeating part: if the request returned 403 status code, try again. Writing it in every place is a lot of copypaste. Is there a better way to achieve this functionality?
In JS I would simply create a function wrapper, in Java I would use decorators for it. But no idea, how to achieve this in c# without copypaste.
Related
I am trying to receive an access token from Facebook's graph API using the received code from Facebook. This is my first time working with APIs in .NET CORE. The code seems to work fine however I am not sure if I am handling the response and catching exceptions in the right way. Although I can use TRY CATCH still do not feel very comfortable in that. I would like to make this method as robust as it can be.
Your help is greatly appreciated.
Handling Class
public class FacebookService : IFacebookService
{
private readonly HttpClient _httpClient;
private readonly IConfiguration configuration;
public FacebookService(HttpClient httpClient, IConfiguration iConfig)
{
_httpClient = httpClient;
configuration = iConfig;
}
public async Task<string> GetShortLiveToken(string code)
{
try
{
string appId = configuration.GetSection("FacebookApp").GetSection("AppID").Value;
string appSecret = configuration.GetSection("FacebookApp").GetSection("AppSecret").Value;
var getTokenUri = $"oauth/access_token?client_id={appId}&client_secret={appSecret}&code={code}&redirect_uri=https://localhost:44373/Home/HandleFbAccess";
var response = await _httpClient.GetAsync(getTokenUri);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
else
{
return response.ReasonPhrase;
}
}
catch(Exception ex)
{
throw ex;
}
}
public async Task<string> GetLongLiveToken(string shortlivedtoken)
{
throw new NotImplementedException();
}
}
Invoking Method
public async Task<IActionResult> HandleFbAccess(string code, string granted_scopes)
{
try
{
var result = await _facebookService.GetShortLiveToken(code);
// more things to do here
//.....
return View("Index");
}
catch(Exception ex)
{
throw ex;
}
}
try{}catch(Exception ex){} worked when your code burst into exception, such as nullpointexception, but you are now worrying about the httpclient send request, you should know that this method only could throw these exceptions and all these exceptions are not related to facebook api.
My idea on your scenario is keeping using try catch here but you need to set operations according to the exception type in catch{} and write custom logic code here, to deal with http response such as 404,400,503 issues etc:
if (response.IsSuccessStatusCode){
return await response.Content.ReadAsStringAsync();
}else{
return response.ReasonPhrase;
}
Context - I am using Azure CosmosDB GraphAPI - and azure doesn't have logging for it, Sad!
So we came up with using our own logging of queries and its metaproperties
I implemented a Singleton class that is used by other services for DB Calls and I want to have a fire and forget the call to the logger.
public class GraphDatabaseQuery : IGraphDatabaseQuery
{
private readonly GremlinClient gremlinClient;
public GraphDatabaseQuery()
{
if (gremlinClient == null)
{
gremlinClient = CosmosDbClient.Instance;
}
else
{
if (gremlinClient.NrConnections == 0)
{
gremlinClient = CosmosDbClient.Instance;
}
}
}
public async Task<ResultSet<dynamic>> SubmitQueryAsync(string query, string fullName)
{
var response = await gremlinClient.SubmitAsync<dynamic>(query);
_ = SendQueryAnalytics(response, query, fullName);
return response;
}
public async Task<dynamic> SubmitQueryWithSingleSingleResultAsync(string query, string fullName)
{
var response = await gremlinClient.SubmitWithSingleResultAsync<dynamic>(query);
return response;
}
private async Task<bool> SendQueryAnalytics(ResultSet<dynamic> response, string query, string fullName)
{
try
{
await new QueryAnalytics().pushData(response, query, fullName); // breakpoint here
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return true;
}
}
in startup.cs
services.AddSingleton<IGraphDatabaseQuery, GraphDatabaseQuery>();
and the interface
public interface IGraphDatabaseQuery
{
public Task<ResultSet<dynamic>> SubmitQueryAsync(string query, string fullName);
public Task<dynamic> SubmitQueryWithSingleSingleResultAsync(string query, string fullName);
}
Whenever I test it the breakpoint hits "SendQueryAnalytics" but i assume at that moment the response from the calling function would be sent back
is this not working because I am using the singleton pattern?
Whenever I test it the breakpoint hits "SendQueryAnalytics" but i assume at that moment the response from the calling function would be sent back
No. All asynchronous methods begin executing synchronously.
So what happens is that SendQueryAnalytics is called and begins executing, enters the try block, creates a new QueryAnalytics instance, and calls pushData. pushData also begins executing and (presumably) returns an incomplete Task. Then the await in SendQueryAnalytics observes that that Task is incomplete and returns an incomplete Task to SubmitQueryAsync, which discards that Task instance and then returns the response.
Thus, seeing the response returned after that breakpoint is normal.
I don't think that the singleton pattern has something to do with it. Since you want a 'fire and forget' why don't you use something like hangfire?
im trying to setup the Fetch API which should call a method on the server side.
Javascript
fetch("/Admin/Server/ac0a45b9-3183-45c9-b4fc-65e37679f1110?handler=StartServer", {
method: "get",
headers: {"Content-Type": "application/json"},
credentials: 'include'
}).then(response => {
if (!response.ok) {
throw response;
}
return response.json();
}).then(() => {
console.log("Done");
});
Server class
private readonly ServerManager ServerManager;
[BindProperty]
public Data.Server.Server Server { get; set; }
public ServerViewModel(ServerContext context, UserContext userContext) {
this.ServerManager = new ServerManager(context, userContext);
}
public async Task<IActionResult> OnGetAsync(string serverId) {
if (string.IsNullOrEmpty(serverId)) {
return NotFound();
}
this.Server = await ServerManager.GetServerAsync(serverId);
return Page();
}
public async Task<JsonResult> OnGetStartServer() {
// all objects here are null
return await this.ServerManager.StartServer(this.Server.serverId); // throw npe
}
The javascript method call the OnGetStartServer method and throw this error: "Object reference not set to an instance of an object"
All objects are null - how I can solve it without reinitializeit?
Regards
Timo
The page model is instantiated and disposed with each request. Therefore anything set on a property, field, etc. goes away at the end of the request. Whatever code initializes the member must be run for each handler that needs to use it, plain and simple.
If you need to persist something like the serverId that was used to create it previously, then you can use Session for that, and then pull it from Session on the next request to re-initialize your Server member. For example:
public async Task<IActionResult> OnGetAsync(string serverId) {
if (string.IsNullOrEmpty(serverId)) {
return NotFound();
}
HttpContext.Session.SetString(ServerIdKey, serverId);
return Page();
}
public async Task<JsonResult> OnGetStartServer() {
var serverId = HttpContext.Session.GetString(ServerIdKey);
if (serverId == null)
{
// Do something, like redirect to a page where the serverId can be set by the user or whatever
}
return await this.ServerManager.StartServer(serverId);
}
I am returning a Custom object when WebAPI call is success and in failure.How to convert to proper response object on client side for this WebAPI? In case of an exception.
[HttpPost]
public ActionResult<MyRespObject> PostTest([FromBody] MyPostObject obj)
{
try
{
MyRespObject response = SomeMethod(obj);
return this.ToActionResult(response);
}
catch (Exception ex) {
return this.ToActionResult(this.LoadMyRespObject(ex));
}
}
protected ActionResult<TResponse> ToActionResult<TResponse>(TResponse response)
where TResponse : IResponse
{
switch (response.Status)
{
case ResponseStatus.Success:
return this.Ok(response);
case ResponseStatus.InvalidRequest:
return this.BadRequest(response);
case ResponseStatus.NotFound:
return this.NotFound(response);
}
return this.StatusCode(500, response);
}
In case of an exception how to convert ex to MyRespObject on the client side? I am generating client using autorest for the API?
Return Type change to
HttpResponseMessage
then client side can Check the status of Data
your code will be
public HttpResponseMessage PostTest([FromBody] MyPostObject obj)
{
try
{
ResponseModel _objResponseModel = new ResponseModel();
MyRespObject response = SomeMethod(obj);
return this.ToActionResult(response);
_objResponseModel.Data = response;
_objResponseModel.Status = response.Status;
_objResponseModel.Message = "success";
}
catch (Exception ex) {
_objResponseModel.Data = null;
_objResponseModel.Status = false;
_objResponseModel.Message = "failed";
}
return Request.CreateResponse(HttpStatusCode.OK, _objResponseModel);
}
}
public class ResponseModel
{
public string Message { set; get; }
public bool Status { set; get; }
public object Data { set; get; }
}
How to convert to proper response object on client side for this WebAPI? In case of an exception.
Thats where middleware comes into picture. Below is the general approach you need to follow which will handle the request efficiently in case of error as well.
Define ErrorHandlingMiddleware which is a async custom class .
Then register this class as middleware in Startup.cs ,so that each and every request will pass through this middleware.
In case of error ,write a model generally like this
Class Error {
int errorCode {get ;set;}
string message {get ;set;}
}
so you can modify this class as well for more properties like hint etc.
Below links have more clear and good explanation.
https://code-maze.com/global-error-handling-aspnetcore/
ASP.NET Core Web API exception handling
Hey (1st time posting here so I may break some rules, tell me if I do),
I'm trying to create a Rest API and I have some problems with.
In fact, the post function is not triggered on the C# API and I don't know why while the GET function used with a getAll() function is running good.
Angular Service :
public GetAll = (): Observable<Expertise[]> => {
return this.http.get(this.actionUrl, '').map((response: Response) => <Expertise[]>response.json());
}
public Add = (thingToAdd: Expertise): Observable<Expertise> => {
thingToAdd.Id = 1;
let toAdd = JSON.stringify(thingToAdd);
console.log(toAdd);
console.log(this.actionUrl);
return this.http.post(this.actionUrl, toAdd, { headers: this.headers
}).map((response: Response) => response.json());
}
C# API :
// GET api/values
[HttpGet]
public async Task<IEnumerable<Expertise>> Get()
{
try
{
System.Console.WriteLine("Test get all");
//var result = await cvService.Get("toto#azeo.com");
return new List<Expertise>();
}
catch (System.Exception ex)
{
logger.LogError(ex.Message, ex);
throw;
}
}
// POST api/values
[HttpPost]
public Expertise Post([FromBody]Expertise expertise)
{
try
{
System.Console.WriteLine("Test post");
context.Expertises.Add(expertise);
context.SaveChanges();
logger.LogInformation("New expertise added !");
return expertise;
}
catch (System.Exception ex)
{
logger.LogError(ex.Message, ex);
throw;
}
}
Expertise (EF model) :
public class Expertise
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public ICollection<ExpCV> CVs { get; set; }
}
If anyone has an idea to "link" the service and my API tell me, I'm stuck on it for a since a long time.
Thank you in advance
since your service returns an observable the actual GET and POST requests will NOT happen until you subscribe to that observable.
take a look at this plunker I set up:
plunker: https://plnkr.co/edit/r6aBaB3BjJzdIS4myAd8?p=preview
code:
#Component({
selector: 'app',
template: `
<button (click)="searchNoSub()">searchNoSub</button>
<button (click)="search()">search</button>
`
})
export class App {
constructor(private searchService:SearchService) {
}
search(){
this.searchService.search().subscribe(console.log,console.log)
}
searchNoSub(){ this.searchService.search(); }
}
searhService:
#Injectable()
export class SearchService {
constructor(private http: Http) {}
search(term: string) {
// get artists named john from spotify API
return this.http.get('https://api.spotify.com/v1/search?q=john&type=artist')
.map((response) => response.json());
}
}
now open your devtools and network tab in chrome to look at new requests:
when clicking the searchNoSub you'll notice no requests are registered in network tab.
when clicking search button, you'll see a request because we subscribed.
In short, you need to subscribe to the request observable.