I'm starting to play around with OAUTH2 in spring boot. I'm currently trying to edit the simple example on this website: https://spring.io/guides/tutorials/spring-boot-oauth2/
But I would like to display more of the person who logs in like email, age and stuff that can be found here:
https://developers.facebook.com/docs/facebook-login/permissions/
Could you guys give me any advice to get any of the information that's described in the url above?
This part is used as login:
<div class="container" ng-show="!home.authenticated">
With Facebook: click here
</div>
Then there is this part in the angular code:
<script type="text/javascript">
angular
.module("app", [])
.config(
function($httpProvider) {
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
}).controller("home", function($http, $location) {
var self = this;
$http.get("/user").success(function(data) {
self.user = data.userAuthentication.details.name;
// i added the following line self.details =
self.details = data.userAuthentication.details
self.authenticated = true;
$http.get("")
}).error(function() {
self.user = "N/A";
self.authenticated = false;
});
self.logout = function() {
$http.post('logout', {}).success(function() {
self.authenticated = false;
$location.path("/");
}).error(function(data) {
console.log("Logout failed")
self.authenticated = false;
});
};
});
</script>
Then I put home.details on the screen and it shows this:{"name":"firstname lastname","id":"some-numbers"}
I think /user refers to a method in the socialapplication.java. So I'm wondering where do I edit what information get's retrieved from facebook.
#SpringBootApplication
#RestController
#EnableOAuth2Client
public class SocialApplication extends WebSecurityConfigurerAdapter {
#Autowired
OAuth2ClientContext oauth2ClientContext;
#RequestMapping("/user")
public Principal user(Principal principal) {
return principal;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.antMatcher("/**").authorizeRequests().antMatchers("/", "/login**", "/webjars/**").permitAll().anyRequest()
.authenticated().and().exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/")).and().logout()
.logoutSuccessUrl("/").permitAll().and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
// #formatter:on
}
public static void main(String[] args) {
SpringApplication.run(SocialApplication.class, args);
}
#Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
private Filter ssoFilter() {
OAuth2ClientAuthenticationProcessingFilter facebookFilter = new OAuth2ClientAuthenticationProcessingFilter(
"/login/facebook");
OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(facebook(), oauth2ClientContext);
facebookFilter.setRestTemplate(facebookTemplate);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(facebookResource().getUserInfoUri(),
facebook().getClientId());
tokenServices.setRestTemplate(facebookTemplate);
facebookFilter.setTokenServices(
new UserInfoTokenServices(facebookResource().getUserInfoUri(), facebook().getClientId()));
return facebookFilter;
}
#Bean
#ConfigurationProperties("facebook.client")
public AuthorizationCodeResourceDetails facebook() {
return new AuthorizationCodeResourceDetails();
}
#Bean
#ConfigurationProperties("facebook.resource")
public ResourceServerProperties facebookResource() {
return new ResourceServerProperties();
}
Here is the YAML file.
facebook:
client:
clientId: xxxxxxxxxx
clientSecret: xxxxxxxxxxxxxxxxxxxxxx
accessTokenUri: https://graph.facebook.com/oauth/access_token
userAuthorizationUri: https://www.facebook.com/dialog/oauth
tokenName: oauth_token
authenticationScheme: query
clientAuthenticationScheme: form
resource:
userInfoUri: https://graph.facebook.com/me
logging:
level:
org.springframework.security: DEBUG
I'm really at a loss here and hope someone can help me to retrieve more information from facebook.
Thanks for any kind of help!
Related
I want the user to be auto logged after registration to the MVC app (email verification is not needed now), I have followed this sample for IdSrv3 and edited some parts:
http://benfoster.io/blog/identity-server-post-registration-sign-in
Here is my Setup:
MVC:
public class AuthController : Controller
{
[HttpGet]
[AllowAnonymous]
public async Task LogIn()
{
var properties = new AuthenticationProperties
{
RedirectUri = Url.Action("index", "home", HttpContext.Request.GetUri().Scheme)
};
await HttpContext.Authentication.ChallengeAsync(properties);
}
[HttpGet]
[AllowAnonymous]
public IActionResult Register()
{
var returnUrl = $"http://localhost:5002/auth/login";
return Redirect($"http://localhost:5000/Account/Register?returnUrl={returnUrl}");
}
}
Identity Server 4 using ASp.Net Core Identity
[Authorize]
public class AccountController : Controller
{
//
// GET: /Account/Register
[HttpGet]
[AllowAnonymous]
public IActionResult Register(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var otac = user.GenerateOTAC(TimeSpan.FromMinutes(1));
await _userManager.UpdateAsync(user);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(3, "User created a new account with password.");
if (string.IsNullOrEmpty(returnUrl))
{
return RedirectToAction(nameof(HomeController.Index), "Home");
}
else
{
return Redirect(returnUrl);
}
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
}
I could not implement the OTAC handshake as the sample shows becaus there is no User Service any more in IdSrv 4.
The scenario works as follows:
1 - User clicks 'Register' in MVC app whish redirects to the IdSrv Account->Register passing the MVC Auth->login as a return URI.
2 - After the user completed the registration in IdSrv another redirect executes to return back to the MVC Auth->Login.
3 - The MVC Auth->Login creates a challenge and since the user is already signed in in the registration process using the cookie, the authentication cookie gets merged (as per the IdSrv log), and there is no login screen appears and now the user is logged in and landed to the MVC app.
Does this setup have any secutity flaw?, is there any way I can implement the OTAC handshake in IdSrv4?
thanks,
In your case maybe this could work
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
I'm using Spring Security for securing my Spring Data REST webapplication with AngularJS.
My SecurityConfig is declared like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().and()
.authorizeRequests()
// public ressources
.antMatchers("/index.html", "/templates/public/**", "/js/**", "/").permitAll()
.antMatchers(HttpMethod.GET, "/api/security/user").permitAll()
.antMatchers("/api/**").hasAnyRole("ADMIN", "NORMALUSER")
.anyRequest().authenticated()
.and().exceptionHandling().authenticationEntryPoint(basicAuthenticationEntryPointHandler)
.and().logout().logoutUrl("/logout").logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()).deleteCookies("JSESSIONID").permitAll().and()
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
I generally followed this link for reaching my goal.
Now, if I would login myself for the first time, a angular-request for $cookies.get("XSRF-TOKEN") returns undefined and every request to my database isn't blocked.
But after logging out and logging in again, it returns me the cookie and every database-request is allowed.
And this happens only every second login, so:
undefined
cookie
undefined
cookie
undefined
...
So now I hope that there's anyone who can help me without going deeper into my structure, but if it's required, I will do so.
Thanks in advance.
Edit 1: Here are the requests:
In relation to the process: I'm logging the tokens in my CokieCsrfTokenRepository().loadToken(), and there all tokens are shown..
Edit 2: My Angular Service, which I'm calling by every login:
function authenticationService($rootScope, $http, $location, $filter, $cookies){
function authenticate(credentials, callback) {
var headers = credentials ? {authorization : "Basic "
+ btoa(credentials.username + ":" + credentials.password)
} : {};
$http.get('api/security/user', {headers : headers}).success(function(data, status, get, header) {
if (data.name) {
$rootScope.authenticated = true;
$rootScope.session = data;
console.log($cookies.get("XSRF-TOKEN")) // -> returns every second login cookie
} else {
$rootScope.authenticated = false;
$rootScope.session = null;
}
console.log($rootScope.session)
callback && callback();
}).error(function() {
$rootScope.authenticated = false;
callback && callback();
});
}
return {
authenticate: function(credentials){
authenticate(credentials, function() {
if ($rootScope.authenticated == true) {
$rootScope.authenticationError = false;
if ($rootScope.session.principal.admin == true){
$location.path("/admin/manage");
} else{
$location.path("/user");
}
} else {
$rootScope.authenticationError = true;
$location.path("/login");
}
});
},
// called by refreshing browser
checkLoggedIn: function(){
authenticate();
},
logout: function(){
$http.post('/logout', {})["finally"](function() {
$rootScope.authenticated = false;
$rootScope.session = null;
$location.path("/login");
});
}
};
}
Edit 3: I mentioned now, that if the cookie is undefined, the method loadToken() from this link is called only after logging out and refreshing the browser (first login). Then the token is shown and I'm logged in, again. But after every second try it still works great..
Edit 4: So no I recognized, that after the first forbidden POST-request (in Edit3 it's the /logout) the token arrives my template. After refreshing the browser, all my requests are allowed, because now the cookie will be send for every request. But how to fix this?
My solution:
//WebSecurityConfigurerAdapter:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
....
.addFilterAfter(new CsrfCustomFilter(), CsrfFilter.class).and()
.csrf().csrfTokenRepository(csrfTokenRepository());
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
public class CsrfCustomFilter extends OncePerRequestFilter{
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie==null || token!=null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
}
I have two web apps running on a single tomcat instance. One of them is Spring MVC Rest app which has basic structure, a rest controller, a service layer and DAO layer which is interacting with postgresql .
Below you can see my RestController
package com.hizir.acil.main.controller;
import com.hizir.acil.main.model.Donor;
import com.hizir.acil.main.service.DonorService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
import org.joda.time.*;
import javax.validation.Valid;
import java.util.List;
/**
* Created by TTTDEMIRCI on 12/29/2015.
*/
#RestController
#RequestMapping("/")
public class AppController {
#Autowired
DonorService donorService;
#Autowired
MessageSource messageSource;
/*
* This method will list all existing Donors in for JSP .
*/
#RequestMapping(value = { "/", "/listAllDonors" }, method = RequestMethod.GET)
public String listDonors(ModelMap model) {
List<Donor> donors = donorService.findAllDonors();
model.addAttribute("donors", donors);
return "alldonors";
}
/*
* This method will list all existing Donors in json format.
*/
#RequestMapping(value = { "/listjson" }, method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<List<Donor>> listDonors() {
List<Donor> donors = donorService.findAllDonors();
if (donors.isEmpty()) {
return new ResponseEntity<List<Donor>>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<List<Donor>>(donors, HttpStatus.OK);
}
/*
* This method will provide the medium to add a new donor.
*/
#RequestMapping(value = { "/new" }, method = RequestMethod.GET)
public String newDonor(ModelMap model) {
Donor donor = new Donor();
model.addAttribute("donor", donor);
model.addAttribute("edit", false);
return "registration";
}
//-------------------Create a Donor--------------------------------------------------------
#RequestMapping(value = "/listjson", method = RequestMethod.POST)
public ResponseEntity<Void> createUser(#RequestBody Donor donor, UriComponentsBuilder ucBuilder) {
System.out.println("Creating Donor " + donor.getName());
// if (donorService.isUserExist(user)) {
// System.out.println("A User with name " + user.getUsername() + " already exist");
// return new ResponseEntity<Void>(HttpStatus.CONFLICT);
// }
donor.setCreationDate(new LocalDate());
donorService.saveDonor(donor);
System.out.println("donor created.............."+donor.getId());
HttpHeaders headers = new HttpHeaders();
headers.setLocation(ucBuilder.path("/listjson/{id}").buildAndExpand(donor.getId()).toUri());
return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
}
//------------------- Update a donor --------------------------------------------------------
#RequestMapping( method = RequestMethod.PUT)
public ResponseEntity<Donor> updateUser(#PathVariable("id") int id, #RequestBody Donor donor) {
System.out.println("Updating donor " + id);
Donor currentDonor = donorService.findById(id);
// if (currentUser==null) {
// System.out.println("User with id " + id + " not found");
// return new ResponseEntity<User>(HttpStatus.NOT_FOUND);
// }
currentDonor.setName(donor.getName());
currentDonor.setSurname(donor.getSurname());
currentDonor.setBloodType(donor.getBloodType());
donorService.updateDonor(currentDonor);
return new ResponseEntity<Donor>(currentDonor, HttpStatus.OK);
}
/*
* This method will be called on form submission, handling POST request for
* saving donor in database. It also validates the user input
*/
#RequestMapping(value = { "/new" }, method = RequestMethod.POST)
public String saveDonor(#Valid Donor donor, BindingResult result,
ModelMap model) {
if (result.hasErrors()) {
return "registration";
}
donorService.saveDonor(donor);
model.addAttribute("success", "Donor " + donor.getName() + " registered successfully");
return "success";
}
/*
* This method will provide the medium to update an existing Donor.
*/
#RequestMapping(value = { "/edit-{id}-donor" }, method = RequestMethod.GET)
public String editDonor(#PathVariable int id, ModelMap model) {
Donor donor= donorService.findById(id);
model.addAttribute("donor", donor);
model.addAttribute("edit", true);
return "registration";
}
/*
* This method will be called on form submission, handling POST request for
* updating donor in database. It also validates the user input
*/
#RequestMapping(value = { "/edit-{id}-donor" }, method = RequestMethod.POST)
public String updateDonor(#Valid Donor donor, BindingResult result,
ModelMap model, #PathVariable int id) {
if (result.hasErrors()) {
return "registration";
}
// if(!service.isEmployeeSsnUnique(employee.getId(), employee.getSsn())){
// FieldError ssnError =new FieldError("employee","ssn",messageSource.getMessage("non.unique.ssn", new String[]{employee.getSsn()}, Locale.getDefault()));
// result.addError(ssnError);
// return "registration";
// }
donorService.updateDonor(donor);
model.addAttribute("success", "Donor " + donor.getName() + " updated successfully");
return "success";
}
/*
* This method will delete a donor by it's id value.
*/
#RequestMapping(value = { "/delete-{id}-donor" }, method = RequestMethod.GET)
public String deleteDonorById(#PathVariable int id) {
donorService.deleteDonorById(id);
return "redirect:/listAllDonors";
}
}
As you can see there are several request mappings. Listing donors and creating donor is working fine with frontend app and I can create new donor and list them. But when I try to update any request is not accessing this rest controller method.
Below is my angular service of frontedn app.
App.factory('User', [
'$resource',
function($resource) {
return $resource(
'http://localhost:8080/HizirAcilBackendApp/listjson/:id', {id: '#id'}, {
update : {
method : 'PUT'
}
},
{
stripTrailingSlashes: false
});
} ]);
and below is my angular controller
/**
*
*/
'use strict';
App.controller('UserController', ['$scope', 'User', function($scope, User) {
var self = this;
self.user= new User();
self.users=[];
self.fetchAllUsers = function(){
self.users = User.query();
};
self.createUser = function(){
self.user.$save(function(){
self.fetchAllUsers();
});
};
self.updateUser = function(){
self.user.$update(function(){
self.fetchAllUsers();
});
};
self.deleteUser = function(identity){
var user = User.get({id:identity}, function() {
user.$delete(function(){
console.log('Deleting user with id ', identity);
self.fetchAllUsers();
});
});
};
self.fetchAllUsers();
self.submit = function() {
if(self.user.id==null){
console.log('Saving New User', self.user);
self.createUser();
}else{
console.log('Upddating user with id ', self.user.id);
self.updateUser();
console.log('User updated with id ', self.user.id);
}
self.reset();
};
self.edit = function(id){
console.log('id to be edited', id);
for(var i = 0; i < self.users.length; i++){
if(self.users[i].id === id) {
self.user = angular.copy(self.users[i]);
break;
}
}
};
self.remove = function(id){
console.log('id to be deleted', id);
if(self.user.id === id) {//If it is the one shown on screen, reset screen
self.reset();
}
self.deleteUser(id);
};
self.reset = function(){
self.user= new User();
$scope.myForm.$setPristine(); //reset Form
};
}]);
I am trying to learn Angular,rest and spring all in one place and I think I have made a good progress but I stuck with this PUT request problem.
Any help and comment would be appreciated.
Regards
Turkmen
Looks like your RequestMapping is wrong, you did not specify a path there :
#RequestMapping( method = RequestMethod.PUT)
You need to set a apth and add {id} so spring can map it as #PathVariable
#RequestMapping(value = "/listjson/{id}", method = RequestMethod.POST)
Im trying to combine those technologies, but nothing good comes, as entity framework meta-datas doesn't get consumed by breeze.js, even all configurations where setup, it's a bit tricky situation, there is literally no examples of that, so this is my sample code which doesn't work properly, but somehow maybe someone will find my mistake and eventually help to solve this puzzle or will find it as starting point.
OdataService.ts
'use strict';
module twine.components {
class MetadataStoreOptions implements breeze.MetadataStoreOptions{
namingConvention:breeze.NamingConvention = breeze.NamingConvention.defaultInstance;
}
class Manager implements breeze.EntityManagerOptions {
metadataStore: breeze.MetadataStore;
constructor( public dataService: breeze.DataService) {
}
}
class DataServiceOptions implements breeze.DataServiceOptions {
serviceName = 'http://twine.azurewebsites.net/odata';
hasServerMetadata = true;
}
export class ODataService {
options: Manager;
manager: breeze.EntityManager;
metadataStore: breeze.MetadataStore;
storeOptions: MetadataStoreOptions;
static $inject: string[] = ['$http', '$rootScope'];
cache: twine.Model.IEntity[];
constructor(private $http: ng.IHttpService, private $rootScope: ng.IRootScopeService){
this.storeOptions = new MetadataStoreOptions();
this.metadataStore = new breeze.MetadataStore(this.storeOptions);
this.options = new Manager( new breeze.DataService( new DataServiceOptions() ));
this.options.metadataStore = this.metadataStore;
this.manager = new breeze.EntityManager( this.options );
breeze.config.initializeAdapterInstance('dataService', 'webApiOData', true);
//this.manager.fetchMetadata((meta) => {
// this.metadataStore.importMetadata(meta);
//});
}
All( query:breeze.EntityQuery, successCallback: Function, failCallback?: Function ): void {
this.manager.executeQuery( query )
.then( ( data: breeze.QueryResult ) => {
successCallback( data );
this.$rootScope.$apply();
})
.catch( ( reason: any ) => {
if ( failCallback ) {
failCallback( reason );
}
});
}
Get( key:number, successCallback: Function, failCallback?: Function ): void {
//this.manager.fetchMetadata();
//var entityType = this.manager.metadataStore.getEntityType('Tag');
//var entityKey = new breeze.EntityKey(entityType, key);
this.manager.fetchEntityByKey( 'Tag', key )
.then( ( data: breeze.EntityByKeyResult ) => {
successCallback( data );
this.$rootScope.$apply();
})
.catch( ( reason: any ) => {
if ( failCallback ) {
failCallback( reason );
}
});
}
}
}
And this is tagController.ts
'use strict';
module twine.routes {
interface ITagsScope extends ng.IScope {
vm: TagsCtrl;
}
interface ITagsCtrl extends twine.components.ITwineRoute{
tags:any[];
getTags: () => void;
tag: any[];
getTag: (id:number) => void;
}
export class TagsCtrl implements ITagsCtrl{
/* #ngInject */
static controllerId: string = 'TagsController';
static controllerAsId: string = 'tagsCtrl';
static $inject: string[] = ["$scope", "ODataService", '$route'];
entityQueryName: string = 'Tag';
query: breeze.EntityQuery;
tags:any;
tag: any;
constructor (private $scope: ITagsScope, private ODataService: twine.components.ODataService, $route: ng.route.IRouteService) {
this.query = new breeze.EntityQuery(this.entityQueryName);
if($route.current && $route.current.params.id){
this.getTag($route.current.params.id);
}
else {
this.getTags();
}
}
getTags() {
this.ODataService.All(this.query , (data) => {
this.tags = data.results[0].value;
}, (error) => {
console.log('error', error);
});
}
getTag(id:number){
this.ODataService.Get(id , (data) => {
this.tag = data.results[0].value;
}, (error) => {
console.log('error', error);
});
}
}
}
There are many errors, on different configurations, sometimes it's There is no resourceName for this query or EntityKey must be set, or Other stupid errors which are indeed doesn't have to appear because it's a typescript which doesn't allow type mismatches, but the configuration itself is not correct.
And this is abstract controller
[EnableCors(origins: "*", headers: "*", methods: "*")]
public abstract class EntityController<T> : ODataController where T: Entity
{
protected ODataRepository<T> repo = new ODataRepository<T>();
private static ODataValidationSettings _validationSettings = new ODataValidationSettings();
public EntityController()
{
}
// GET: odata/Entity
[EnableQuery]
public IQueryable<T> Get(ODataQueryOptions<T> queryOptions)
{
try
{
queryOptions.Validate(_validationSettings);
}
catch (ODataException ex)
{
Trace.WriteLine(ex.Message);
return null;
}
return repo.All();
}
// GET: odata/Entity(5)
[EnableQuery]
public SingleResult<T> Get([FromODataUri] long key, ODataQueryOptions<T> queryOptions)
{
try
{
queryOptions.Validate(_validationSettings);
}
catch (ODataException ex)
{
Trace.WriteLine(ex.Message);
return null;
}
return SingleResult.Create(repo.All().Where(x=>x._id == key));
}
//ommitted
}
And lastly this is ASP.NET webApi configuration
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Конфигурация и службы веб-API
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
// Маршруты веб-API
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//CORS
var cors = new EnableCorsAttribute(
"*",
"*",
"*",
"DataServiceVersion, MaxDataServiceVersion"
);
config.EnableCors(cors);
// Маршруты Odata
//config.EnableQuerySupport();
config.AddODataQueryFilter();
Builder<Account>(config);
Builder<Branch>(config);
Builder<Bucket>(config);
Builder<Ingredient>(config);
Builder<Like>(config);
Builder<Meetup>(config);
Builder<Shot>(config);
Builder<Skill>(config);
Builder<Tag>(config);
Builder<Team>(config);
}
private static void Builder<T>(HttpConfiguration config) where T: class
{
var entityType = Activator.CreateInstance<T>().GetType();
if (entityType != null)
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<T>(entityType.Name);
config.Routes.MapODataServiceRoute("odata_" + entityType.Name, "odata", builder.GetEdmModel());
}
}
}
For testing purpose i have this working backed OData service at http://twine.azurewebsites.net/odata/Tag, (currently no restrictions by CORS, feel free) last entity can be changed to other name based on webApi configuration Build method. Please feel free to ask any other information. If someone need whole source, im willing to publish on github
Update
Forget to mension, problem is in method Get of ODataService. I cannot bind metadata from server to breeze, method All works fine. But query fetchByEntityKey throws errors as described above
evc, please take a look at the following article under AngularJs Apps/Projects. You'll find sample projects that you can actually follow and learn using AngularJs, Breeze, Asp.net Web api. You're right, there's a lot of material out there but there not that effective. Hope it helps. http://www.learn-angularjs-apps-projects.com/
Have a look at the Breeze samples, especially WEB Api OData and Breeze. It's sample tutorial for a angularjs+webapi+odata+breeze, no typescript (but isn't javascript just typescript :). For your WEB Api controllers you should definitely install this nuget package :
PM> Install-Package Breeze.Server.WebApi2
This will allow you to build a breeze aware api controller and expose breeze odata metada easily using the Breeze.Server.ContextProviderClass.
Also you can check the excelent but paid training from Pluralsight. It covers SPA from scratch using JavaScript, Angular, and Breeze.
http://www.pluralsight.com/courses/build-apps-angular-breeze
I am getting 415 error in browser. I am not able to find the mistake. Could you please help.
loginController.js
$scope.user = {email: "admin", password: "password"};
$http.post('/expense-manager-api/login/authenticate', $scope.user, {
headers: {"Content-Type": "application/json"}
}).success(function(login) {
$scope.setError(login.status);
$location.path("main");
}).error(function() {
$scope.setError('Invalid user/password combination');
});
LoginController.java
#Controller
#RequestMapping("/login")
public class LoginController {
#RequestMapping(value = "/authenticate", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public
#ResponseBody
LoginResponse login(#RequestBody LoginRequest loginRequest) {
if (loginRequest.getEmail().equals("admin") && loginRequest.getPassword().equals("password")) {
UUID uuid = UUID.randomUUID();
return new LoginResponse(uuid.toString(), "OK");
}
return new LoginResponse(null, "Invalid user/password combination");
}
}
Jackson mapper fixed the problem.
add maven dependency of Jackson-mapper in your pom.xml