Best way to use spring for Web and mobile application - json

I am new to web development. I am planning to create a web service which is going to act as a back end for both web site and mobile application. I want to know if it is possible to use same method to return data in different type.
For example:
If i use http://somewebsite/getdetails.jsp should give me and modelView return type and http://somewebsite/getdetails.json should give the model in json format.
I don't want to create two different controller to handle this.
If there is any other better way also please share your comments.
I am open for alternative solutions too

Spring 4.0 / Spring Boot enables you to achieve this quite easily. I am currently developing web-service (API) for mobile and backend for browser based clients and I just simply split API for mobile under URL #RequestMapping("/api"). In addition, Spring allows you to easily implement RESTful url based application. I recommend you to have two different controllers for API and Web MVC because it ensures complete separation between two different logics. E.g.
Would you really like to implement something like following?
#SuppressWarnings("unchecked")
public Map<Object, Object> test(#RequestParam(value="mobileyes") boolean mobile){
if(mobile){
Map<Object, Object> m = new HashMap<Object, Object>();
m.put("test", "test")
return m;
} else {
ModelAndView mv = new ModelAndView();
mv.addObject("test", "test");
mv.setViewName("test");
return (Map<Object, Object>) mv;
}
}
Above example might work, but ugly and will certainly cause maintenance disaster in near future.
This is my overall structure of Spring MVC using Spring Boot :
Ordinary URL accessed by desktop based and mobile based browsers
These controllers use
#Controller annotation because it doesn't automatically enables #ResponseBody
www.mybusinesscard.com.au/ -> Index
//Displaying all businesscards
www.mybusinesscard.com.au/businesscards -> view all
//For saving from form
www.mybusinesscard.com.au/businesscard/save -> save a card
Following controller examples are for mobile API:
Following controllers use #RestController annotation to automatically enable requirements necessary for WebServices. E.g: #ResponseBody
www.mybusinesscard.com.au/api -> Index
//Displaying all businesscards
www.mybusinesscard.com.au/api/businesscards -> view all
//For saving from form
www.mybusinesscard.com.au/api/businesscard/save -> save a card

Related

How to use Hystrix with Spring WebFlux WebClients?

I'm using Spring WebFlux with functional endpoints to create an API. To provide the results I want, I need to consume an external RESTful API, and to do that in a async way I'm using a WebClient implementation. It works well and goes like this:
public WeatherWebClient() {
this.weatherWebClient = WebClient.create("http://api.openweathermap.org/data/2.5/weather");
}
public Mono<WeatherApiResponse> getWeatherByCityName(String cityName) {
return weatherWebClient
.get()
.uri(uriBuilder -> uriBuilder
.queryParam("q", cityName)
.queryParam("units", "metric")
.queryParam("appid", API_KEY)
.build())
.accept(APPLICATION_JSON)
.retrieve()
.bodyToMono(WeatherApiResponse.class);
}
As this performs network access, it's a good use case for NetFlix OSS Hystrix. I've tried using spring-cloud-starter-netflix-hystrix, adding #HystrixCommand to the method above, but there's no way to make it trip the circuit, even if I set a bad URL (404) or wrong API_KEY (401).
I thought this could be a problem of compatibility with the WebFlux itself, but setting property #HystrixProperty(name="circuitBreaker.forceOpen", value="true") indeed forces the fallback method to run.
Am I missing something? Is this approach incompatible with Spring WebClients?
Thanks!
#HystrixCommand won't really work, because Hystrix doesn't threat Mono/Flux any different from Java primitives.
Hystrix doesn't monitor content of Mono, but only the result of call public Mono<WeatherApiResponse> getWeatherByCityName(String cityName).
This result is always OK, because reactive-call-chain creation will always succeed.
What you need, is to make Hystrix threat Mono/Flux differently.
In Spring Cloud, there is a builder, to wrap Mono/Flux with HystrixCommand.
Mono<WeatherApiResponse> call = this.getWeatherByCityName(String cityName);
Mono<WeatherApiResponse> callWrappedWithHystrix = HystrixCommands
.from(call)
.fallback(Mono.just(WeatherApiResponse.EMPTY))
.commandName("getWeatherByCityName")
.toMono();

Programmatically instantiate a FeignClient for tests

I have a dead simple FeignClient interface that I would like to "unit"/integration test with a fake HTTP server, WireMock for example. The idea is to test the mapping with a sampled HTTP API response, without configuring a whole Spring Boot/Cloud Context.
#FeignClient(name = "foo", url = "${foo.url}")
public interface FooClient {
#RequestMapping(value = "/foo/{foo-id}/bar", method = RequestMethod.GET)
public Bar getBar(#PathVariable("foo-id") String fooId);
}
Is there any way to programmatically instantiate this interface, like a Spring Data Repository through a *RepositoryFactoryBean ?
I see a FeignClientFactoryBean in the source code, but it is package protected, and it relies on an ApplicationContext object to retrieve its dependencies anyway.
Well, you can fake a real rest client using wiremock for testing purposes, but this is more about containing the functional test, that feign clients themself work. This is mostly not what you really want to test, because the actual need is to test your components using your client behave in a specified way.
The best practice for me is not to make live hard with maintaing a fake server, but mock the clients behavior with Mockito. If you use Spring Boot 1.4.0, here is the way to go:
Consider you have some FooBarService, which internally uses your FooClient to peform some FooBarService::someAction(String fooId), which performs some business logic which needs to work with a foo with given id
#RunWith(SpringRunner.class)
#SpringBootTest(classes = App.class)
class FooUnitTest {
#Autowired;
private FooBarService fooBarService;
#MockBean;
private FooClient fooClient;
#Test
public void testService() {
given(fooClient.getBar("1")).willReturn(new Bar(...));
fooBarService.someAction("1");
//assert here, that someAction did what it supposed to do for that bar
}
}
At this point you first should clarify, what you expect the REST client to respond, when asking for "/foo/1/bar", by creating a mock for exactly that case and give the Bar object you expect to receive for that API, and assert that your application is in the desired state.

Spring Security 4.0 Content Negotiation restrictiions

I have an app that uses spring security 4.0 and i am concern now about the content negotiation response that this app could send on a REST web service i.e.
my target is to restrict the response on a global basis irrelevant of the type of the request i.e. if that would be REST http get request through MVC or some kind of websocket (although i am not sure if that apply for the websocket) the response should be only returned as a json and NOT as XML. I do not want to support xml or any negotiation format.
The reason i am concerned about this is because i watched
a video on infoq made by a gentlemen called Mike Wiesner about spring application security pitfalls.
i know i can use in this case the annotation #RequestMapping and the sub-option "produces", i.e. something like
#RequestMapping(produces={MediaType.APPLICATION_JSON_VALUE} , value = "/target/get", method=RequestMethod.GET)
but since i have so many controllers it will be a nightmare for me to put that additional sub-option on all of them.
and i know that there are other annotations such as
#XmlTransient
#JsonIgnore
that could help me with what i want to do i.e. make some filds (getter/setters) to not be exposed in case the content negotiation changes but putting those annotations on each
getter/setter will even be bigger problem
Thus my question how do i do that on a global basis. I suppose this should be done in the MVCConfig class that extends WebMvcConfigurerAdapter?
By that i mean overriding the configureContentNegotiation method There are multiple examples doing that but those only explaing how to set up the the default behavior. My question is how do we restrict the behavior i.e. if http request is coming with "Accept" header application/xml how do i reject that on a global basis.
examples of the default behavior:
Spring boot controller content negotiation
so what i do is someting like
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false).
If anything else then a json comms into the http request
reject this request or smply ignore it on a global basis.
Do not send/support xml, xhtml, html etc.
}
}
I coincidentally was looking into a related issue to this question in the last couple of days. We manually configure a ContentNegotiationManager in our code base, and in that process we limit the header based portion of the Spring PPA Strategy by providing an overridden HeaderContentNegotiationStrategy that does limiting by the Accept header similar to what you want. I took a quick look at ContentNegotiationConfigurer (which I have never used) and it does not appear to provide an option for which to alter mappings for the HeaderContentNegotiationStrategy, so here is a code snippet of the way we setup our ContentNegotiationManager.
#Bean
public ContentNegotiationManager contentNegotiationManager() {
//Supply a Map<String, org.springframework.http.MediaType>
PathExtensionContentNegotiationStrategy pathBased = new PathExtensionContentNegotiationStrategy(supportedMediaTypes());
//Supply a Map<org.springframework.http.MediaType, org.springframework.http.MediaType>
HeaderContentNegotiationStrategy headerBased = new MappingHeaderContentNegotiationStrategy(contentTypeMap());
FixedContentNegotiationStrategy defaultStrategy = new FixedContentNegotiationStrategy(MediaType.APPLICATION_JSON);
return ContentNegotiationManager(pathBased, headerBased, defaultStrategy);
return retval;
}
That bean is created in our config that overrides WebMvcConfigurerAdapter and is injected into this bean:
#Bean
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setRemoveSemicolonContent(false);
handlerMapping.getFileExtensions().add("json");
handlerMapping.setUseRegisteredSuffixPatternMatch(true);
handlerMapping.setInterceptors(getInterceptors());
handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
return handlerMapping;
}

Web API: 'Global' filter not working (ExceptionFilter)

I implemented the exception filter like here: http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling
And registered it globally, like microsoft or stackoverflow-users ( How to add global ASP.Net Web Api Filters? ) explained.
public static void RegisterWebApiFilters(System.Web.Http.Filters.GlobalFilterCollection filters)
{
//other filters
filters.Add(new MyExceptionFilter());
}
But if I throw an exception, my method is not called.
My exception-handling method is called only if I add the attribute [MyExceptionFilter] to the controller-method, but I hoped I can avoid that for all methods by registering the filter globally.
I tried to set a order for the filters, but this had no effect.
Edit: I have noticed, that in the new Wep Api RC the method is called "RegisterGlobalFilters" and this seems to be the MVC filter collection.
If I call
GlobalConfiguration.Configuration.Filters.Add(new MyExceptionFilter());
it works. This is the collection for the Web Api.
Looks like I have to build my own "FilterConfig" class for the web api...
Like I mentioned in my question: There are different filter collections. One for MVC and one for the web api.
If you want to add the filter to the web api, add this line of code to the global.asax
GlobalConfiguration.Configuration.Filters.Add(new MyExceptionFilter());

Edit Models in different MVC project layer

I have a ASP.NET MVC3 solution named "SampleProject". I have 4 projects in the solution.
The project names of the solution are
SampleProject.Data (holds entity classes, DAL classes, and filter classes)
SampleProject.Service (something like BLL in standard ERP)
SampleProject.Tests (test project)
SampleProject.Web (holds controllers and views)
I am calling the Service classes from my controllers. The service classes are calling Data classes and data classes are performing the database operations.
I have done create, list and details part. Now I stucked in Edit part. None of the examples (NerdDinner,MVCMusicStore etc) using my architecture. In the provided examples(NerdDinner,MVCMusicStore etc or in ASP.NET website tutorials), they are just using built in UpdateModel method which I don't want to use. I want to manually get the model object from my view and send it to my Data layer for update.
My question is, how can I update the models through different project layer?
I solved the porblem. Here is the code.Just for reference, CResult is a class which contains IsSuccess(bool), Message(string) properties in it.
CResult oCResult;
[HttpPost]
public ActionResult Edit(Restaurant model)
{
try
{
oCResult = restaurantService.Update(model);
if (oCResult.IsSuccess)
{
return RedirectToAction("Index");
}
return View("Error");
}
catch
{
return View();
}
}
The view engine prepares the object (in my case, it is Restaurant type of object) it inherits with new values and send back to controller. this is my understanding.