I've created a REST API with Django and I want to use it in Fluter. I've created the endpoints and tested them; they work fine, I've also created the models in flutter. I've done the get all endpoint. I'm now struggling with how to decode the details endpoint and use it in Flutter.
Anyone's thoughts ?
When you receive the response from API, you can use json.decode to convert the body of your request from a string to a json, and then you can parse the json to your model, like the example below:
// Model Example
class Model {
final String name;
Model.fromJson(dynamic json)
: name = json['name'];
}
...
// Do the request, receive a [response]
final body = response.body;
final jsonBody = json.decode(body);
final model = Model.fromJson(jsonBody);
If you want a more detailed example, please send a piece of your code and I can explain based on your context
It depends on what are you using if you are using http package this is an example
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
void main(List<String> arguments) async {
// This example uses the Google Books API to search for books about http.
// https://developers.google.com/books/docs/overview
var url =
Uri.https('www.googleapis.com', '/books/v1/volumes', {'q': '{http}'});
// Await the http get response, then decode the json-formatted response.
var response = await http.get(url);
if (response.statusCode == 200) {
var jsonResponse =
convert.jsonDecode(response.body) as Map<String, dynamic>;
var itemCount = jsonResponse['totalItems'];
print('Number of books about http: $itemCount.');
} else {
print('Request failed with status: ${response.statusCode}.');
}
}
if you are using Dio package you don't need to decode dio will do it for you this is an example
import 'package:dio/dio.dart';
void getHttp() async {
try {
var response = await Dio().get('http://www.google.com');
print(response);
} catch (e) {
print(e);
}
}
Related
Having the following HttpPost action
[AllowAnonymous]
[Route("api/Test")]
[ApiController]
public class TestController : ControllerBase
{
[Route("Something")]
[HttpPost]
//[IgnoreAntiforgeryToken]
public async Task<IActionResult> Something()
{
return Ok(new
{
Result = true
});
}
}
If I enable the [IgnoreAntiforgeryToken] tag, it works fine.
Then It seems that my post (from Postman) should sent the CSRF token, in that case:
I configure the __RequestVerificationToken (as a Header or as a Body with x-www-form-urlencoded)
Making sure that the token get updated
But I still get a 400
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "Bad Request",
"status": 400,
"traceId": "00-4b7d669686a083fbba09be86b6841e42-847918b0b6ca656b-00"
}
I tried to debug the request in order to discover what is happening?
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
var initialBody = context.Request.Body;
using (var bodyReader = new System.IO.StreamReader(context.Request.Body))
{
string body = await bodyReader.ReadToEndAsync();
Console.WriteLine(body);
context.Request.Body = new System.IO.MemoryStream(Encoding.UTF8.GetBytes(body));
await next.Invoke();
context.Request.Body = initialBody;
}
//await next.Invoke();
});
}
But I haven't found anything special.
How can I find out what is generating this 400 Bad Request?
You may misunderstand what is the process of testing endpoints protected with an XSRF token in Postman.
Pre-request script to set the value of the xsrf-token environment variable. And from the script, you need be sure the {url} you called is a view which contains input with name="__RequestVerificationToken".
Then you send post request to the api/test.
Here is my working code:
1.Index.cshtml:
<form method="post">
//if it does not generate the token by default
//you can manually add:
//#Html.AntiForgeryToken()
</form>
2.Set the environment:
VARIABLE
INITIALVALUE
member-url
https://localhost:portNumber
xsrf-token
3.Be sure apply the enviroment:
4.Pre-request script:
5.API:
[Route("Something")]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Something()
{
return Ok(new
{
Result = true
});
}
After you get 200 response, you can see the token value in your created environment.
Okay, I have the bad feeling that I'm missing a key concept in what I'm doing. Hope someone can help me out with a hint.
I'm using Nuxt and Vuex Store Modules. Every fetch a Module Action does is wrapped in a helper Function (saveFetch) that I imported to decrease repetitive code, like this:
export const actions = {
async sampleAction(context, data){
...
await saveFetch(context, 'POST', '/pages', data)
...
}
}
The helper simple checks if the users accessToken is still valid, refreshes it if not and then sends the request:
export const saveFetch = async (context, method = 'POST', path, data) => {
const accessTokenExpiry = context.rootGetters['auth/getAccessTokenExpiry']
let accessToken = context.rootGetters['auth/getAccessToken']
// If the client provides an accessToken and the accessToken is expired,
// refresh the token before making the "real" fetch.
if (accessToken && accessTokenExpiry < new Date() && path !== '/auth/refresh-token') {
if (process.client) {
// Works fine
await window.$nuxt.$store.dispatch('auth/refreshToken')
} else {
// This is where the trouble starts
await context.dispatch('auth/refreshToken', null, { root: true })
}
accessToken = rootGetters['auth/getAccessToken']
}
return fetch(path, {
method,
headers: { ... },
body: JSON.stringify(data),
}
}
If the accessToken is expired the helper function dispatches a Vuex Action to refresh it. This works well on the client side, but not if that process happens on the server side.
The Problem that's coming up on the server side is, that the user has to provide a refreshToken to get a refreshed accessToken from the API. This refreshToken is stored as a HttpOnly Cookie in the Client. When logging the Nuxt request details on the API side of things I noticed, that Nuxt is not sending that cookie.
My current workaround looks like this:
export const actions = {
async refreshToken(context){
...
let refreshToken
if (process?.server && this?.app?.context?.req?.headers?.cookie) {
const parsedCookies = cookie.parse(
this.app.context.req.headers.cookie
)
refreshToken = parsedCookies?.refreshToken
}
const response = await saveFetch(context, 'POST', '/auth/refresh-token', {
refreshToken,
})
...
}
...
}
If on server side, access the req object, get the cookies from it and send the refreshToken Cookie Content in the requests body.
This looks clearly bad to me and I would love to get some feedback on how to do this better. Did I maybe miss some key concepts that would help me not get into this problem in the first place?
I have an emberJS application where I can make a POST AJAX call to a Django backend. A function in Django creates an xlsx file for a bunch of queried items based on IDs coming in the POST request. It goes through the Django view function without any issues, but when the HTTP response is returned to ember, I get the error
SyntaxError: Unexpected token P in JSON at position 0
at parse (<anonymous>)
at ajaxConvert (jquery.js:8787)
at done (jquery.js:9255)
at XMLHttpRequest.<anonymous> (jquery.js:9548)
at XMLHttpRequest.nrWrapper (base-content:20)
I'm setting the response content type to application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, so I'm unsure as to why its trying to read the response as JSON.
Python Code
file_path = '/User/path_to_spreadsheet/content.xlsx'
fsock = open(file_path, "rb")
response = HttpResponse(fsock, content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = 'attachment; filename="content.xlsx"'
return response
EmberJS Code
export default Controller.extend({
actions: {
storeProductId(products) {
let product_ids = []
products.forEach(function(product){
product_ids.push(product.id)
});
let adapter = this.store.adapterFor('product-export');
adapter.export_products(product_ids).then(function(response){
console.log(response)
}).catch(function(response) {
console.log('ERROR')
console.log(response)
})
}
}
});
Product-Export Adapter Code
export default ApplicationAdapter.extend(FormDataAdapterMixin, {
export_products(products) {
let url = this.buildURL('unified-product');
url = `${url}export/`;
return this.ajax(url, 'POST', { data: {'products': products} });
}
});
By default, Ember Data makes some assumptions around how things should be handled (including that you’ll be receiving JSON data back). Is there a reason you are using Ember Data instead of using a direct Ajax call to your backend? Seems like that would greatly simplify things here ...
I am trying to use microsoft face recognition for my app. I have signed up for an account and started to implement into my app but cannot get any response except for 404 resource not found. Any ideas as to where I should start with this one?
import Foundation
import Alamofire
class CognitiveService {
static let instance = CognitiveService()
static let apiKey = API_KEY /// set in constants file
static let apiUrl = FACE_DETECT_URL /// set in constants file
func test() {
var header = [String : String]()
header["Ocp-Apim-Subscription-Key"] = CognitiveService.apiKey
let url = "any web address to image here"
let params:[String: String] = ["url": url]
let request = Alamofire.request(CognitiveService.apiUrl, parameters: params, headers: header)
print("\(request)")
request.responseJSON { (response) in
print(response)
}
}
}
Assuming FACE_DETECT_URL is set correctly, the issue is you're making a HTTP GET request (the default for Alamofire) when you wanted a POST. So you'll want:
let request = Alamofire.request(CognitiveService.apiUrl, method: .post, parameters: params, encoding: JSONEncoding.default, headers: header)
I'm building a web app with Django on back-end and Backbone.js on front-end
I have problems with IE when I'm trying to fetch data from the server. When I run my HTML page in IE, the collection fetch always invoke the error func.
My code:
$(function(){
var Chapter = Backbone.Model.extend({});
var Chapters = Backbone.Collection.extend({
model: Chapter,
url: 'http://ip.olya.ivanovss.info/chapters'
});
var chapters = new Chapters();
var Router = new (Backbone.Router.extend({
routes: {
"": "choose_activity",
"/": "choose_activity"
},
choose_activity: function () {
chapters.fetch({
success: function () {
AppView.render();
},
error: function() {
alert('error');
}
});
}
}))();
var AppView = new (Backbone.View.extend({
el: '.popup',
templates: {
choose_activity: Handlebars.compile($('#tpl-activities').html())
},
render: function () {
this.$el.html(this.templates["choose_activity"]({ chapters: chapters.toJSON()}));
}
}))();
Backbone.history.start();
});
Django's View:
def chapters(request):
chapters = list(Chapter.objects.order_by('id'))
response = HttpResponse(json.dumps(chapters, default=encode_myway), mimetype='text/plain')
if request.META.get('HTTP_ORIGIN', None) in ('http://localhost', 'http://html.olya.ivanovss.info', 'http://10.0.2.2'):
response['Access-Control-Allow-Origin'] = request.META['HTTP_ORIGIN']
return response
Thank you in advance
IE7 doesn't support CORS.
There are 2 ways around this. The EASY way is Proxy over your API. My Python is rusty (I'm a Node/PHP dev), but I'm sure that there are a million and one resources on how do do this. The good thing about this is you don't have to touch the API. But it means your local server has to CURL and return every single request from your API server.
And second (and much less server intensive way) is JSONP! The idea of JSONP is that it appends a <script> to the document with the URL you specify. jQuery appends a ?callback=jQueryNNN where NNN is a random number. So effectively when the <script> loads, it calls jQueryNNN('The Response Text') and jQuery knows to parse the response from there. The bad thing about this is you have to wrap all of your responses on the API side (which is super easy if you're just starting, not so easy if you already have an infrastructure built out).
The annoying things about JSONP is that by it's nature you can't do a POST/PUT/DELETE. BUT you can emulate it if you have access to the API:
Backbone.emulateHTTP = true;
model.save(); // POST to "/collection/id", with "_method=PUT" + header.
To integrate JSONP with Backbone is pretty simple (little secret Backbone.sync uses jQuery's $.ajax() and the options parameters forwards over to jQuery ;)).
For each one of your models/collections which access a cross origin you can add a su
var jsonpSync = function (method, model, options) {
options.timeout = 10000; // for 404 responses
options.dataType = "jsonp";
return Backbone.sync(method, model, options);
};
In each collection and model what does cross-origin:
var MyCollection = Backbone.Collection.extend({
sync : jsonpSync
});
Or just overwrite the whole Backbone sync
Backbone.__sync = Backbone.sync;
var jsonpSync = function (method, model, options) {
options.timeout = 10000; // for 404 responses
options.dataType = "jsonp";
return Backbone.__sync(method, model, options);
};
Backbone.sync = jsonpSync;
On the server side you can do: this to return a JSONP response (copy pasted here):
def randomTest(request):
callback = request.GET.get('callback', '')
req = {}
req ['title'] = 'This is a constant result.'
response = json.dumps(req)
response = callback + '(' + response + ');'
return HttpResponse(response, mimetype="application/json")