I'm new to Spring framework and REST concepts. I've been working on a project to grasp these concepts efficiently.
I'm building a quiz tool where a user can login using his credentials and take a quiz.
I've created a RESTful API for same using JAX-RS. And now I want to create a Client which will work over this API, using Spring MVC.
Is that possible and how to start with that ??
I mean, How do I use Spring MVC to create a Client for my RESTful API ??
some of my resources are -
GET /scorecard
GET /scorecard/{quizId}
GET /scorecard/{userId}
GET /quiz/{questionId}
POST /quiz/{questionId}
and so on..
I'm really confused about the design aspects about a client using Spring MVC. Do I include the logic of evaluating quiz,calculating & storing scores in the API or in the spring MVC client ??
Thanx in advance.
Here is an example of the first two endpoints implemented with Spring MVC:
#Controller
#RequestMapping(value = "/scorecard")
public class ScorecardController {
#Autowired
private ScorecardService scorecardService;
// GET /scorecard
#RequestMapping(method = RequestMethod.GET)
public List<Scorecard> getScorecards()
{
List<Scorecard> scorecards = scorecardService.getScorecards();
return scorecards;
}
// GET /scorecard/{quizId}
#RequestMapping(value = "/{quizId}", method = RequestMethod.GET)
public List<Scorecard> getScorecardsByQuizId(#PathVariable long quizId)
{
List<Scorecard> scorecards = scorecardService.getScorecardsByQuizId(quizId);
return scorecards;
}
}
I would suggest checkout spring mvc showcase project from Github and experimenting with source code.
spring-mvc-showcase
You can easily use your browser as client for quite a few REST calls.
Design for application depends on requirements. e.g.
Keeping the score at client will keep you free from session management otherwise you will need to handle at server.
Related
I am new to Salesforce and I would like to get some expert advice on how I can expose the Sales force data as an Rest API so the external System can consume it. I was think if I can create a Apex Class like
below
#RestResource(urlMapping='/GetAccounts/*')
global with sharing class GetAccounts {
#HttpGet
global static Account doGet() {
RestRequest req = RestContext.request;
RestResponse res = RestContext.response;
String accountId = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1);
Account result = [SELECT Id, Name FROM Account WHERE Id = :accountId];
return result;
}
}
And for the external user to consume the data I thought I can set up up a Connected App and provide them with the Username,password, Consumer Key,Consumer secret and they should be authenticating in to Salesforce to get the URI and session ID. Using the SessionID and URI the should be able to call the API that is exposed above. Is this the right approach, please let me know if I am missing anything here.
Also there is a requirement to use Swagger with the API, is it possible to use the Swagger within the Apex Class. Can you please help how I can leverage Swagger with my API here.
First of all you should try to use Salesforce standard REST API. You can check the full documentation from here.
https://developer.salesforce.com/docs/api-explorer/sobject/Account
You might be asking yourself, well when I should expose an APEX class as a REST API like the code you have provided?
You need to do that when you need custom logic to be performed and combined with the API call.
Exposing Salesforce REST API as OPEN API specification(Swagger) is not yet supported. You can vote for this idea if you need it.
https://success.salesforce.com/ideaView?id=0873A000000cQsxQAE
But the other way is supported. You can import a swagger specification file and invoke it using point and clicks from Salesforce.
Check this blog for more details:
https://andyinthecloud.com/2017/07/23/simplified-api-integrations-with-external-services/
I have an application where I'm registering a user so user will enter his data on JSP page and the data will be save into DB, the flow will be JSP->dispatcher->controller->Service->Dao.
Now in MemberController which is delegating the request to the Service, has a method register() which will take the MemberDto as a parameter now and return the Successfull msg to the success.jsp page. Sometihng like user registered successfully.
public String Register(MemberDto memberDto)
Now I want to expose this same method as RestFul service using Jersey for partners and also use this same method within my application as a normal MVC flow. How can I acheive this
So u want to use Jersey so import the jersey library to support JAX-RS.
#Path("/classlevelpath")
public class MyController {
#POST
#Produces(MediaType.APPLICATION-XML)
#Path("/register")
public String Register(MemberDto memberDto) {
}
}
Be careful JAX-RS (Jersey is an implementaion) and Spring REST annotations are different.
Annotate your rest class with #RestController. The best practice is to create another controller. By you can see this answer if you want to transform your existing controller: https://stackoverflow.com/questions/33062509/returning-view-from-spring-mvc-restcontroller
I'm having problems testing the generation of MVC Routes from inside Web API. The code works when hit manually, but fails under test as the in-memory instance of Web API is unaware of the MVC routes and I can't figure out how to add them.
Here's an example project on github illustrating the problem, but I'll include some relevant code here.
I'm using an in-memory HTTP Server to host the Web API for integration testing:
private HttpConfiguration _config;
private HttpServer _server;
private HttpMessageInvoker _client;
[TestInitialize]
public void TestInitialize()
{
_config = new HttpConfiguration();
WebApiConfig.Register(_config);
_server = new HttpServer(_config);
_client = new HttpMessageInvoker(_server);
}
In my Web API Controller I'm trying to return links via the out-of-the-box default routes, both Web API and Mvc:
[HttpGet]
[Route("MvcRoute")]
public string MvcRoute()
{
return Url.Link("Default", new {Controller = "Other", Action = "Index"});
}
[HttpGet]
[Route("ApiRoute")]
public string ApiRoute()
{
return Url.Link("DefaultApi", new {Controller = "Example", Id = "MvcRoute"});
}
A test for the ApiRoute passes, but this test for the MvcRoute fails with the error message "A route named 'Default' could not be found in the route collection.":
[TestMethod]
public void ShouldReturnMvcRoute()
{
using (var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/example/mvcroute"))
{
using (var response = _client.SendAsync(request, CancellationToken.None).Result)
{
var responseContent = response.Content.ReadAsStringAsync().Result;
Assert.AreEqual("\"http://localhost/Other\"", responseContent);
}
}
}
So how can I make the in-memory server aware of MVC's routes? Or if that's the wrong question to ask, how can I run automated tests on a build server (i.e., no IIS) that hit Web API routes that generate links to MVC routes?
In-memory scenario is only supported in Web API. When hosted on a real server like IIS, Web API registers its routes onto the route table provided by System.Web.Routing.RouteCollection...Since MVC also registers its routes into this same route table, when generating links from Web API to MVC, the routes are indeed there...
Note that Web API came later than MVC and one of the design goals of it was to run outside IIS host (like it can be run on Selfhost too) and not have dependency on System.Web...In your about example, you are instantiating HttpConfiguration which means that you are having a different route collection where as in the real app WebApiConfig.Register would be passed in GlobalConfiguration instance...
I have REST api for accessing "parties" and the URL's look like this:
/parties
/parties/{partyId}
Using Spring controllers and #PathVariable I'm able to implement this interface. But to prevent users from accessing parties they don't have access to, I have to add checks to every method call which is kind of repeating myself and I might forget to add it everywhere:
#RequestMapping(value="/parties/{partyId}", method=RequestMethod.GET)
public #ResponseBody Party getParty(#PathVariable Integer partyId){
authorizeForParty(partyId);
...
Now what I would like to do is create a check that would be called every time that user enters url like this:
/parties/{partyId}/**
How would I do something like this? Do I have to create some servlet filter and parse the url myself? If I have to parse the url then is there atleast tools that would make it easy? I wish there was a way to add a method to controller that would be called before methods but could still use #PathVariables and such...
What I ended up with is using the Spring MVC interceptors and parsing the path variables in the same way that Spring does. So I define an interceptor for the REST url:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/parties/*/**" />
<bean class="PartyAuthorizationInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
The PartyAuthorizationInterceptor has to implement HandlerInterceptor in which we have to implement preHandle. It has HttpServletRequest as a parameter so we can get the request URL but we still have to parse the partyId from the url. After reading how Spring MVC does it, I found out they have a class named org.springframework.util.AntPathMatcher. It can read the path variables from the URL and place the values in a map. The method is called extractUriTemplateVariables.
So the result looks like this:
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String partyIdStr = new AntPathMatcher().extractUriTemplateVariables("/parties/{partyId}/**", request.getPathInfo()).get("partyId");
...
That makes the parsing almost as easy as using #PathVariable in MVC Controller methods. You still have to do conversions yourself(e.g. String -> Integer).
Now I can implement authorization logic on all urls that access a party in this interceptor and keep that logic out of the individual controller methods. Not as easy as I would have hoped but it gets the job done.
Are you already using some kind of security library in your application, e. g. Spring Security?
Because the kind of logic you want to implement is a classic case for an AccessDecisionVoter in an authentication chain. You would just put your API behind Spring Security's protection and implement the custom check as part of the security chain.
If you are not using a security framework at all, your idea of implementing a HandlerInterceptor may be the best alternative, though. But it would require you (as you mentioned) to take into account all kinds of obfuscation the user may use in order to gain access to other URLs (e. g. %-encoding of letters, ../../ patterns etc.).
I developed several REST services using an early version of webapi (0.6.0), and for My services I enabled help page and Test client in as below in RegisterRoutes (called from application_start:
routes.Add(new ServiceRoute("auth",
new HttpServiceHostFactory()
{
Configuration = new HttpConfiguration()
{
EnableTestClient = true,
EnableHelpPage = true
}
},
typeof(Auth_Api)));
So I was able to access service at
http://<myserver>/auth
and access help page and test client at
http://<myserver>/auth/help
http://<myserver>/auth/test
Now I need to migrate them to MVC4 webapi, and I would like to accomplish the same behaviour, regarding test and help page, but I cannot find how to do it.
In RegisterRoutes I have this code which setup routes for API (REST) functionality
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Can I add System.ServiceModel.Activation and Microsoft.ApplicationServer.Http.Activation assemblies to MVC4 webapi app and set the routes as before?
Any drawbacks if I do it this way (in case it works)?
Thanks
Look at the API Explorer for the ability to generate Help pages. I believe the Test client has been dropped for the moment. I think there are plans to bring it back. I don't remember exactly.