gpt4 book ai didi

json - Spring MVC、REST、JSON、DispatcherServlet 和 @RequestMapping

转载 作者:行者123 更新时间:2023-12-05 01:18:04 26 4
gpt4 key购买 nike

我似乎无法进行简单的 REST 客户端集成测试。我正在使用带有 JavaConfig 设置的 Spring 3.1 MVC。我使用 Maven,我可以毫无问题地构建、运行和部署我当前的 webapp。

首先,这里有一些代码和配置

我的 Controller

@Controller
@RequestMapping("/rest")
public class StubRestController {

@Inject
private TestData testData;

@RequestMapping(value=Endpoints.GET_RESOURCES, method=RequestMethod.GET, produces="application/json")
@ResponseStatus(HttpStatus.OK)
public @ResponseBody JSONObject getResources(@RequestParam(value="q") String query, @RequestParam int indexFrom, @RequestParam int indexTo) throws JSONException {
return makeRequest(query, indexFrom, indexTo, testData.getResources());
}

@RequestMapping(value=Endpoints.GET_LOCATIONS, method=RequestMethod.GET, produces="application/json")
@ResponseStatus(HttpStatus.OK)
public @ResponseBody JSONObject getLocations(@RequestParam(value="q") String query, @RequestParam int indexFrom, @RequestParam int indexTo) throws JSONException {
return makeRequest(query, indexFrom, indexTo, testData.getLocations());
}

private JSONObject makeRequest(String query, int indexFrom, int indexTo, String[] data) throws JSONException {
int count = 0;
final JSONArray resources = new JSONArray();
for (final String resourceName: data) {
final String lowerColor = resourceName.toLowerCase();
final int has = lowerColor.indexOf(query.toLowerCase());

if (!query.isEmpty() && (query.equals("*") || has >= 0)) {
final JSONObject resource = new JSONObject();
resource.put("DisplayName", resourceName);
resource.put("Value", resourceName); // shouldn't this be a unique id? e.g., resourceid
resources.put(resource);
count++;
}
}

final JSONArray partial = new JSONArray();
if (resources.length() > 0) {
final int end = count - 1 > indexTo ? indexTo : count - 1;
for (int i = indexFrom; i <= end; i++) {
partial.put(resources.get(i));
}
}

final JSONObject result = new JSONObject();
result.put("TotalSize", count);
result.put("Options", partial);
return result;
}

@ExceptionHandler(JSONException.class)
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason="No data found matching criteria")
public void notFound() { }
}

我的测试

@ContextConfiguration(classes={ RestClientContext.class }, loader=AnnotationConfigContextLoader.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class StubRestClientITCase {

private static final String SCHEME = "http";
private static final String HOST = "localhost";
private static final int PORT = 8081;

// requests must match <url-pattern> in order to be handled by DispatcherServlet
private static final String ENDPOINT_PREFIX = "spp-emkt-mui-experimental/EMKT/services/rest";

private static final String QUERY_KEY = "q";
private static final String INDEX_FROM_KEY = "indexFrom";
private static final String INDEX_TO_KEY = "indexTo";
private static final String TOTAL_SIZE_KEY = "TotalSize";

private Logger log = LoggerFactory.getLogger(StubRestClientITCase.class);

@Inject
RestTemplate restTemplate;

@Test
public void testGetResources() {

// Case 1: Discover all resources using * (asterisk), first 25
final URI uri = buildUri("*", 0, 24, Endpoints.GET_RESOURCES);
final HttpEntity<JSONObject> response = obtainResponse(uri);

try {
Assert.assertTrue(response.hasBody());
Assert.assertEquals(25, Integer.parseInt(response.getBody().getString(TOTAL_SIZE_KEY)));
} catch (final JSONException je) {
fail("Could not obtain \"" + TOTAL_SIZE_KEY + "\" from JSON payload for getResources().\n" + je.getMessage());
}

}

private URI buildUri(String query, int indexFrom, int indexTo, String endPointUrl) {
final UriComponents uriComponents =
UriComponentsBuilder.newInstance()
.scheme(SCHEME).host(HOST).port(PORT).path(ENDPOINT_PREFIX + endPointUrl)
.queryParam(QUERY_KEY, query)
.queryParam(INDEX_FROM_KEY, indexFrom)
.queryParam(INDEX_TO_KEY, indexTo)
.build()
.encode();

final URI uri = uriComponents.toUri();
return uri;
}

private HttpEntity<JSONObject> obtainResponse(URI uri) {
final HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAccept(Arrays.asList(new MediaType[] {MediaType.APPLICATION_JSON}));
requestHeaders.setAcceptCharset(Arrays.asList(new Charset[] {Charset.forName("UTF-8")}));
final HttpEntity<?> requestEntity = new HttpEntity(requestHeaders);
final HttpEntity<JSONObject> response = restTemplate.exchange(uri, HttpMethod.GET, requestEntity, JSONObject.class);
return response;
}
}

我的 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.spp.mui.gwt.server.config.WebAppContextExperimental</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>

<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<servlet>
<servlet-name>gwt</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>gwt</servlet-name>
<url-pattern>/EMKT/service/*</url-pattern>
</servlet-mapping>

<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

<session-config>
<session-timeout>0</session-timeout>
</session-config>
</web-app>

我可以看到我的 Controller 方法映射到服务器启动...

17:48:58,651 INFO  [RequestMappingHandlerMapping] Mapped "{[/rest/resources],methods=[GET],params=[],headers=[],consumes=[],produces=[application/json],custom=[]}"
17:48:58,651 INFO [RequestMappingHandlerMapping] Mapped "{[/rest/locations],methods=[GET],params=[],headers=[],consumes=[],produces=[application/json],custom=[]}"

当测试运行时,我得到...

17:49:00,617 DEBUG [AutowiredAnnotationBeanPostProcessor] Autowiring by type from bean name 'com.spp.mui.gwt.server.controller.stub.StubRestClientITCase' to bean named 'restTemplate'
17:49:00,648 DEBUG [RestTemplate] Created GET request for "http://localhost:8080/EMKT/services/rest/resources?q=*&indexFrom=0&indexTo=24"
17:49:00,680 DEBUG [RestTemplate] Setting request Accept header to [application/json]
17:49:00,742 WARN [RestTemplate] GET request for "http://localhost:8080/EMKT/services/rest/resources?q=*&indexFrom=0&indexTo=24" resulted in 404 (Not Found); invoking error handler

终于……

Tests in error:
testGetResources(com.spp.mui.gwt.server.controller.stub.StubRestClientITCase): 404 Not Found

可能是什么问题?认为它与 servlet-mapping 中的 url-pattern 有关。我曾尝试在设置上遵循 Spring 文档,但没有成功。注意:虽然我有一个 GWT-SL 设置来映射 RPC 服务接口(interface),但我对此并不灵活。

更新 #1

如果我尝试使用

curl --verbose -H "Accept: application/json" "localhost:8081/spp-emkt-mui-experimental/EMKT/service/rest/resources?q=*&indexFrom=0&indexTo=24"

针对 Tomcat 6 部署(使用 cargo 插件),我得到了不同的结果:

* About to connect() to localhost port 8081 (#0)
* Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 8081 (#0)
> GET /spp-emkt-mui-experimental/EMKT/service/rest/resources?q=*&indexFrom=0&indexTo=24 HTTP/1.1
> User-Agent: curl/7.21.1 (i686-pc-mingw32) libcurl/7.21.1 OpenSSL/0.9.8r zlib/1.2.3
> Host: localhost:8081
> Accept: */*
>
< HTTP/1.1 406 Not Acceptable
< Server: Apache-Coyote/1.1
< Content-Type: text/html;charset=utf-8
< Content-Length: 1070
< Date: Tue, 28 Feb 2012 05:00:26 GMT

这似乎表明我没有在我的请求中发送适当的 header 信息。嗯嗯嗯。

更新 #2

书面测试返回 404。而 curl 则返回 406。

我搜索了很多看似相关的帖子并尝试了很多东西,我想知道 Spring MVC、GWT-RPC 和 GWT-SL 是否能够组合在同一个容器中。考虑迁移到 RestyGWT,服务器端是 Spring MVC。评论?

最佳答案

这对我来说绝对是一个学习练习。我必须清除一些配置方面的障碍,以便 GWT RPC 和 Spring MVC 可以很好地协同工作。

我现在有一个工作测试。我的“失礼”试图返回一个@ResponseBody 注释的 JSONObject。 不要这样做!我制作了一个自定义 DTO,并使用 @JsonProperty、@JsonSerialize 和 @JsonDeserialize 等 Jackson 注释进行注释,以按照我想要的方式获得 I/O。

如果有人感兴趣,我将在下面展示我的工作测试和配置更新...

测试

@ContextConfiguration(classes={ RestClientContext.class }, loader=AnnotationConfigContextLoader.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class SuggestionsClientITCase {

private static final String SCHEME = "http";
private static final String HOST = "localhost";
private static final int PORT = 8080;

private static final String QUERY_KEY = "q";
private static final String INDEX_FROM_KEY = "indexFrom";
private static final String INDEX_TO_KEY = "indexTo";;

private Logger log = LoggerFactory.getLogger(SuggestionsClientITCase.class);

@Inject
RestTemplate restTemplate;

@Test
public void testGetResources() {

// Case 1: Discover all resources using * (asterisk), first 25
// -- the total # of resources in TestData is 250
// -- the total # of options returned should be constrained by indexTo - indexFrom = 25
happyPathAssertions(Endpoints.GET_RESOURCES, "*", 0, 24, 250, 25);

}

@Test
public void testGetLocations() {

// Case 1: Discover all resources using * (asterisk), first 25
// -- the total # of locations in TestData is 4316
// -- the total # of options returned should be constrained by indexTo - indexFrom = 25
happyPathAssertions(Endpoints.GET_LOCATIONS, "*", 0, 24, 4316, 25);

}

private void happyPathAssertions(String endpointUrl, String query, int indexFrom, int indexTo, int expectedTotal, int expectedOptionsPerPage) {
final URI uri = buildUri(query, indexFrom, indexTo, endpointUrl);
final HttpEntity<SuggestionsPayload> response = obtainResponse(uri);
Assert.assertTrue(response.hasBody());
Assert.assertEquals(expectedTotal, response.getBody().getTotalSize());
Assert.assertEquals(expectedOptionsPerPage, response.getBody().getOptions().size());
}

private URI buildUri(String query, int indexFrom, int indexTo, String endPointUrl) {
final UriComponents uriComponents =
UriComponentsBuilder.newInstance()
.scheme(SCHEME).host(HOST).port(PORT).path(Endpoints.REST_PREFIX + endPointUrl)
.queryParam(QUERY_KEY, query)
.queryParam(INDEX_FROM_KEY, indexFrom)
.queryParam(INDEX_TO_KEY, indexTo)
.build()
.encode();

final URI uri = uriComponents.toUri();
return uri;
}

private HttpEntity<SuggestionsPayload> obtainResponse(URI uri) {
final HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAccept(Arrays.asList(new MediaType[] {MediaType.APPLICATION_JSON}));
requestHeaders.setAcceptCharset(Arrays.asList(new Charset[] {Charset.forName("UTF-8")}));
final HttpEntity<?> requestEntity = new HttpEntity(requestHeaders);
final HttpEntity<SuggestionsPayload> response = restTemplate.exchange(uri, HttpMethod.GET, requestEntity, SuggestionsPayload.class);
return response;
}

}

Controller

@Controller
@RequestMapping("/" + Endpoints.REST_PREFIX)
public class StubSuggestionsController {

@Inject
private TestData testData;

@RequestMapping(value=Endpoints.GET_RESOURCES, method=RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})
@ResponseStatus(HttpStatus.OK)
public @ResponseBody SuggestionsPayload getResources(@RequestParam(value="q") String query, @RequestParam int indexFrom, @RequestParam int indexTo) {
return makeRequest(query, indexFrom, indexTo, testData.getResources());
}

@RequestMapping(value=Endpoints.GET_LOCATIONS, method=RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})
@ResponseStatus(HttpStatus.OK)
public @ResponseBody SuggestionsPayload getLocations(@RequestParam(value="q") String query, @RequestParam int indexFrom, @RequestParam int indexTo) {
return makeRequest(query, indexFrom, indexTo, testData.getLocations());
}

private SuggestionsPayload makeRequest(String query, int indexFrom, int indexTo, String[] data) {
int count = 0;
final List<SuggestionOption> possibilities = new ArrayList<SuggestionOption>();
for (final String resourceName: data) {
final String key = resourceName.toLowerCase();
final int has = key.indexOf(query.toLowerCase());

if (!query.isEmpty() && (query.equals("*") || has >= 0)) {
final SuggestionOption possibility = new SuggestionOption();
possibility.setDisplayName(resourceName);
possibility.setValue(resourceName); // shouldn't this be a unique id? e.g., resourceid
possibilities.add(possibility);
count++;
}
}

final List<SuggestionOption> options = new ArrayList<SuggestionOption>();
if (possibilities.size() > 0) {
final int end = count - 1 > indexTo ? indexTo : count - 1;
for (int i = indexFrom; i <= end; i++) {
options.add(possibilities.get(i));
}
// sort the suggestions by display name
Collections.sort(options, new Comparator<SuggestionOption>() {

@Override
public int compare(SuggestionOption o1, SuggestionOption o2) {
final int comparison = o1.getDisplayName().compareTo(o2.getDisplayName());
return comparison;
}

});
}

final SuggestionsPayload result = new SuggestionsPayload();
result.setTotalSize(count);
result.setOptions(options);
return result;
}

}

有效负载

public class SuggestionsPayload {

@JsonProperty("TotalSize")
@JsonSerialize @JsonDeserialize
private int totalSize;
@JsonProperty("Options")
@JsonSerialize @JsonDeserialize
private List<SuggestionOption> options;

public int getTotalSize() {
return totalSize;
}

public void setTotalSize(int totalSize) {
this.totalSize = totalSize;
}

public List<SuggestionOption> getOptions() {
return options;
}

public void setOptions(List<SuggestionOption> options) {
this.options = options;
}

public static class SuggestionOption {

@JsonProperty("Value")
@JsonSerialize @JsonDeserialize
private String value;
@JsonProperty("DisplayName")
@JsonSerialize @JsonDeserialize
private String displayName;

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

public String getDisplayName() {
return displayName;
}

public void setDisplayName(String displayName) {
this.displayName = displayName;
}

}
}

WEB.XML

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<!-- Java-based annotation-driven Spring container definition -->
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>

<!-- Location of Java @Configuration classes that configure the components that makeup this application -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.spp.mui.gwt.server.config.WebAppContextExperimental</param-value>
</context-param>

<!-- Specifies the default mode of this application, to be activated if no other profile (or mode) is specified -->
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>standard</param-value>
</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Exposes request on current thread of execution, required for beans
employing <aop:scoped-proxy /> or e.g., @Scope(value="request", proxyMode=ScopedProxyMode.TARGET_CLASS) -->
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>

<!-- Reads request input using UTF-8 encoding -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Enables support for DELETE and PUT request methods with web browser clients -->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Secures the application -->
<!--
<filter>
<filter-name>securityFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>springSecurityFilterChain</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>securityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
-->

<!-- Handles requests into the application -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- No explicit configuration file reference here: everything is configured in the root container for simplicity -->
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<welcome-file-list>
<welcome-file>Application.html</welcome-file>
</welcome-file-list>

<session-config>
<session-timeout>0</session-timeout>
</session-config>

配置

@Configuration
@Import(value={ AopConfig.class, GwtServiceConfig.class, ComponentConfig.class, MvcConfig.class })
public class WebAppContextExperimental {
// context used for web application

}

在我的 MvcConfig 中,我必须确保添加这些(特别是 GWT 支持的注册表项,因此 Spring 的 DispatcherServlet 将允许对代码生成资源的请求)

// serve static resources

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// i.e., images, JS, and CSS
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
// i.e., GWT module code-generated resources
registry.addResourceHandler("/*").addResourceLocations("/");
registry.addResourceHandler("/EMKT/**").addResourceLocations("/EMKT/");
}

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJacksonHttpMessageConverter());
}

关于json - Spring MVC、REST、JSON、DispatcherServlet 和 @RequestMapping,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9475500/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com