带有SpringBoottest的MockMVC正在抛出Exceptiat

发布于 2025-02-13 09:32:07 字数 6404 浏览 2 评论 0原文

我正在借助集成测试和MOPEMVC在Spring RestController中测试请求验证。

contrantertest.java

@ExtendWith(MockitoExtension.class)
@SpringBootTest(classes = Controller.class)
@AutoConfigureMockMvc(addFilters = false)
class ControllerTest {

    private ObjectMapper objectMapper;

    @MockBean
    private Service service;

    @Autowired
    private MockMvc mockMvc;

    @BeforeEach
    void setUp() {
        objectMapper = new ObjectMapper();
    }

    @Test
    void createOrAdd_shouldReturnErrorResponseOnInvalidInput() throws Exception {
        Request request = Request.builder()
                .name("name<script>")
                .primaryEmail("[email protected]")
                .build();

        mockMvc.perform(MockMvcRequestBuilders.post("/api/create")
                        .accept(MediaType.APPLICATION_JSON)
                        .contentType(MediaType.APPLICATION_JSON_VALUE)
                        .content(objectMapper.writeValueAsString(request))
                .characterEncoding("utf-8"))
                .andExpect(MockMvcResultMatchers.status().isBadRequest());
    }
}

controller.java:

@Slf4j
@RestController
public class Controller {

    private final Service service;

    public Controller(Service service) {
        this.service = service;
    }

    @PostMapping(value = "/api/create", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<GenericResponse<Response>> createOrAdd(@RequestBody @Valid Request request, Errors errors) {
        GenericResponse<Response> genericResponse = new GenericResponse<>();
        try {
            if (errors.hasErrors()) {
                throw new RequestParamsException(errors.getAllErrors());
            }
            Response response = service.createOrAdd(request);
            genericResponse.setData(response);
            return ResponseEntity.ok().body(genericResponse);
        } catch (RequestParamsException ex) {
            genericResponse.setErrors(ex.getErrors());
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(genericResponse);
        }
    }

错误

WARN 17304 --- [           main] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=utf-8' not supported]

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /api/create
       Parameters = {}
          Headers = [Content-Type:"application/json;charset=utf-8", Accept:"application/json", Content-Length:"162"]
             Body = {"name":"name<script>alert(1)</script>","primary_email_address":"[email protected]"}
    Session Attrs = {}

Handler:
             Type = com.org.controller.Controller
           Method = com.org.controller.Controller#createOrAdd(Request, Errors)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = org.springframework.web.HttpMediaTypeNotSupportedException

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 415
    Error message = null
          Headers = [Accept:"application/octet-stream, text/plain, application/xml, text/xml, application/x-www-form-urlencoded, application/*+xml, multipart/form-data, multipart/mixed, */*"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

java.lang.AssertionError: Status expected:<400> but was:<415>
Expected :400
Actual   :415

我使用了正确的content> content-type接受标题在使用test.java使用mockmvc中进行呼叫时,但它仍然在提供httpmediatypenotsupportedexception。在中尝试了许多组合content-type,但仍无法正常工作。

我读了很多与此例外有关的问题,但在这里找不到问题。 仍然无法弄清楚为什么要说httpmediatypenotsupportedexception。

更新:删除addfilters = false,如建议,无法找到处理程序本身。

    MockHttpServletRequest:
          HTTP Method = POST
          Request URI = /api/create
           Parameters = {}
              Headers = [Content-Type:"application/json;charset=utf-8", Accept:"application/json", Content-Length:"162"]
                 Body = {"name":"name<script>alert(1)</script>","primary_email_address":"[email protected]"}
    Session Attrs = {org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN=org.springframework.security.web.csrf.DefaultCsrfToken@7a687d8d}   

     
Handler:
             Type = null

Async:
    Async started = false
     Async result = null
    
    Resolved Exception:
             Type = null
    
    ModelAndView:
            View name = null
                 View = null
                Model = null
    
    FlashMap:
           Attributes = null
    
MockHttpServletResponse:
           Status = 403
    Error message = Forbidden
          Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

    
java.lang.AssertionError: Status expected:<400> but was:<403>
Expected :400
Actual   :403

请求

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CreateAgencyRequest {

    @NotNull(message = "name can't be null")
    @JsonProperty(value = "name")
    @Pattern(regexp = REGEX_CONST, message = "name is not valid")
    private String name;

    @NotNull(message = "primary_email_address can't be null")
    @JsonProperty(value = "primary_email_address")
    private String primaryEmail;

}

I am testing request validation in Spring RestController with the help of integration testing and MockMvc.

ControllerTest.java

@ExtendWith(MockitoExtension.class)
@SpringBootTest(classes = Controller.class)
@AutoConfigureMockMvc(addFilters = false)
class ControllerTest {

    private ObjectMapper objectMapper;

    @MockBean
    private Service service;

    @Autowired
    private MockMvc mockMvc;

    @BeforeEach
    void setUp() {
        objectMapper = new ObjectMapper();
    }

    @Test
    void createOrAdd_shouldReturnErrorResponseOnInvalidInput() throws Exception {
        Request request = Request.builder()
                .name("name<script>")
                .primaryEmail("[email protected]")
                .build();

        mockMvc.perform(MockMvcRequestBuilders.post("/api/create")
                        .accept(MediaType.APPLICATION_JSON)
                        .contentType(MediaType.APPLICATION_JSON_VALUE)
                        .content(objectMapper.writeValueAsString(request))
                .characterEncoding("utf-8"))
                .andExpect(MockMvcResultMatchers.status().isBadRequest());
    }
}

Controller.java :

@Slf4j
@RestController
public class Controller {

    private final Service service;

    public Controller(Service service) {
        this.service = service;
    }

    @PostMapping(value = "/api/create", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<GenericResponse<Response>> createOrAdd(@RequestBody @Valid Request request, Errors errors) {
        GenericResponse<Response> genericResponse = new GenericResponse<>();
        try {
            if (errors.hasErrors()) {
                throw new RequestParamsException(errors.getAllErrors());
            }
            Response response = service.createOrAdd(request);
            genericResponse.setData(response);
            return ResponseEntity.ok().body(genericResponse);
        } catch (RequestParamsException ex) {
            genericResponse.setErrors(ex.getErrors());
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(genericResponse);
        }
    }

Error :

WARN 17304 --- [           main] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=utf-8' not supported]

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /api/create
       Parameters = {}
          Headers = [Content-Type:"application/json;charset=utf-8", Accept:"application/json", Content-Length:"162"]
             Body = {"name":"name<script>alert(1)</script>","primary_email_address":"[email protected]"}
    Session Attrs = {}

Handler:
             Type = com.org.controller.Controller
           Method = com.org.controller.Controller#createOrAdd(Request, Errors)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = org.springframework.web.HttpMediaTypeNotSupportedException

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 415
    Error message = null
          Headers = [Accept:"application/octet-stream, text/plain, application/xml, text/xml, application/x-www-form-urlencoded, application/*+xml, multipart/form-data, multipart/mixed, */*"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

java.lang.AssertionError: Status expected:<400> but was:<415>
Expected :400
Actual   :415

I have used the correct Content-Type and Accept Headers while making a call in Test.java using mockMvc, but still it's giving HttpMediaTypeNotSupportedException. Tried many combinations in Accept and Content-Type but still not working.

I have read many SO questions related to this exception, but couldn't find what's the issue here.
Still not able to figure out why it's saying HttpMediaTypeNotSupportedException.

Update : After removing addFilters = false as suggested, not able to find the handler itself.

    MockHttpServletRequest:
          HTTP Method = POST
          Request URI = /api/create
           Parameters = {}
              Headers = [Content-Type:"application/json;charset=utf-8", Accept:"application/json", Content-Length:"162"]
                 Body = {"name":"name<script>alert(1)</script>","primary_email_address":"[email protected]"}
    Session Attrs = {org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN=org.springframework.security.web.csrf.DefaultCsrfToken@7a687d8d}   

     
Handler:
             Type = null

Async:
    Async started = false
     Async result = null
    
    Resolved Exception:
             Type = null
    
    ModelAndView:
            View name = null
                 View = null
                Model = null
    
    FlashMap:
           Attributes = null
    
MockHttpServletResponse:
           Status = 403
    Error message = Forbidden
          Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

    
java.lang.AssertionError: Status expected:<400> but was:<403>
Expected :400
Actual   :403

Request :

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CreateAgencyRequest {

    @NotNull(message = "name can't be null")
    @JsonProperty(value = "name")
    @Pattern(regexp = REGEX_CONST, message = "name is not valid")
    private String name;

    @NotNull(message = "primary_email_address can't be null")
    @JsonProperty(value = "primary_email_address")
    private String primaryEmail;

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

天邊彩虹 2025-02-20 09:32:07

让我们看一下您的考试。

@WebMvcTest(classes = Controller.class)
@AutoConfigureMockMvc(addFilters = false)
class ControllerTest {

    private ObjectMapper objectMapper;

    @MockBean
    private Service service;

    @Autowired
    private MockMvc mockMvc;

    @Test
    void createOrAdd_shouldReturnErrorResponseOnInvalidInput() throws Exception {
        Request request = Request.builder()
                .name("name<script>")
                .primaryEmail("[email protected]")
                .build();

        mockMvc.perform(MockMvcRequestBuilders.post("/api/create")
                        .accept(MediaType.APPLICATION_JSON)
                        .contentType(MediaType.APPLICATION_JSON_VALUE)
                        .content(objectMapper.writeValueAsString(request))
                .characterEncoding("utf-8"))
                .andExpect(MockMvcResultMatchers.status().isBadRequest());
    }
}

首先,@extendwith(mockitoextension.class)在您使用时不会添加任何内容,@mockbean由Spring处理的注释。因此,您应该删除它。

接下来,@springboottest用于引导完整的应用程序运行集成测试。您想要的是对网络的切片测试,因此代替@springboottest使用@webmvctest这将使您的测试更快。然后,您还可以删除@AutoconFigureMockMvc,因为默认情况下添加了。

您使用@AutoconFigureMockMvc(addfilters = false)禁用所有过滤器可能是由于您获得的403所致。您拥有的403是启用CSRF(默认启用)而不是将其添加到请求中的结果。如果您不希望CSRF(可能需要它)在安全配置中禁用该CSRF,或者是否要它修改请求。

查看罪魁祸首的错误是targeenCoding被添加到内容类型中,因此您可能想要/应该删除它。

有了所有的测试,应该看起来像这样。


import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;

@WebMvcTest(Controller.class)
class ControllerTest {

    private ObjectMapper objectMapper = new ObjectMapper();

    @MockBean
    private Service service;

    @Autowired
    private MockMvc mockMvc;

    @Test
    void createOrAdd_shouldReturnErrorResponseOnInvalidInput() throws Exception {
        Request request = Request.builder()
                .name("name<script>")
                .primaryEmail("[email protected]")
                .build();

        mockMvc.perform(MockMvcRequestBuilders.post("/api/create")
                        .with(csrf()) // Add CSRF field for security
                        .accept(MediaType.APPLICATION_JSON)
                        .contentType(MediaType.APPLICATION_JSON_VALUE)
                        .content(objectMapper.writeValueAsString(request))
                .andExpect(MockMvcResultMatchers.status().isBadRequest());
    }
}

注意:如果您还具有身份验证,则可能还需要在请求中添加用户/密码,例如在这里解释

Lets take a look at your test.

@WebMvcTest(classes = Controller.class)
@AutoConfigureMockMvc(addFilters = false)
class ControllerTest {

    private ObjectMapper objectMapper;

    @MockBean
    private Service service;

    @Autowired
    private MockMvc mockMvc;

    @Test
    void createOrAdd_shouldReturnErrorResponseOnInvalidInput() throws Exception {
        Request request = Request.builder()
                .name("name<script>")
                .primaryEmail("[email protected]")
                .build();

        mockMvc.perform(MockMvcRequestBuilders.post("/api/create")
                        .accept(MediaType.APPLICATION_JSON)
                        .contentType(MediaType.APPLICATION_JSON_VALUE)
                        .content(objectMapper.writeValueAsString(request))
                .characterEncoding("utf-8"))
                .andExpect(MockMvcResultMatchers.status().isBadRequest());
    }
}

First the @ExtendWith(MockitoExtension.class) doesn't add anything as you are using, correctly, the @MockBean annotation which is handled by Spring. So you should remove that.

Next the @SpringBootTest is for bootstrapping the full application to run an integration test. What you want is a sliced test for the web, so instead of @SpringBootTest use @WebMvcTest this will make your test considerably faster. You can then also remove @AutoConfigureMockMvc as that is added by default.

You disabled all filters with @AutoConfigureMockMvc(addFilters = false) probably due to the 403 you got. The 403 you have is the result of enabling CSRF (enabled by default) and not adding that to the request. If you don't want CSRF (you probably want it) either disable that in the Security configuration, or if you want it modify your request.

Looking at the error you have the culprit is the characterEncoding being added to the content type, so you probably want/should remove that.

With all that your test should look something like this.


import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;

@WebMvcTest(Controller.class)
class ControllerTest {

    private ObjectMapper objectMapper = new ObjectMapper();

    @MockBean
    private Service service;

    @Autowired
    private MockMvc mockMvc;

    @Test
    void createOrAdd_shouldReturnErrorResponseOnInvalidInput() throws Exception {
        Request request = Request.builder()
                .name("name<script>")
                .primaryEmail("[email protected]")
                .build();

        mockMvc.perform(MockMvcRequestBuilders.post("/api/create")
                        .with(csrf()) // Add CSRF field for security
                        .accept(MediaType.APPLICATION_JSON)
                        .contentType(MediaType.APPLICATION_JSON_VALUE)
                        .content(objectMapper.writeValueAsString(request))
                .andExpect(MockMvcResultMatchers.status().isBadRequest());
    }
}

NOTE: If you also have authentication in place you might also need to add a user/password to the request as explained here

想你的星星会说话 2025-02-20 09:32:07

添加消耗在您的控制器端点中产生怎么样?显然,端点不接受您在请求中发送的内容类型:

mockMvc.perform(MockMvcRequestBuilders.post("/api/create")
                        .accept(MediaType.APPLICATION_JSON)
                        .contentType(MediaType.APPLICATION_JSON_VALUE)

您告诉端点:“嘿,我要发送JSON,我只接受JSON响应回来”。但是端点说:“看,在我接受的东西中,没有JSON,所以请重新考虑!”

MockHttpServletResponse:
           Status = 415
    Error message = null
          Headers = [Accept:"application/octet-stream, text/plain, application/xml, text/xml, application/x-www-form-urlencoded, application/*+xml, multipart/form-data, multipart/mixed, */*"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

因此,相应调整。

@RequestMapping(
    produces = {"application/json"}, 
    consumes = {"application/json"}, 
    method = RequestMethod.POST
)

所有其他配置,测试,注射都是无关紧要的。很高兴有,但不能解决您的问题。

How about adding consumes and produces in your controller endpoint? Apparently, endpoint does not accept the content type you send in request:

mockMvc.perform(MockMvcRequestBuilders.post("/api/create")
                        .accept(MediaType.APPLICATION_JSON)
                        .contentType(MediaType.APPLICATION_JSON_VALUE)

You tell endpoint "hey, I am sending JSON and I only accept JSON response back". But endpoint says "Look, among what I accept, there is no JSON, so please rethink!"

MockHttpServletResponse:
           Status = 415
    Error message = null
          Headers = [Accept:"application/octet-stream, text/plain, application/xml, text/xml, application/x-www-form-urlencoded, application/*+xml, multipart/form-data, multipart/mixed, */*"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

So adjust accordingly.

@RequestMapping(
    produces = {"application/json"}, 
    consumes = {"application/json"}, 
    method = RequestMethod.POST
)

All other configs, testing, injections are irrelevant. Good to have, but does not solve your problem.

白鸥掠海 2025-02-20 09:32:07

我认为您缺少@webmvctest(控制器= Controller.Class)

I think you are missing @WebMvcTest(controllers = Controller.class)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文